@photivra/engine 2.20.0 → 2.22.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 +295 -1
- package/CITATION.cff +1 -1
- package/README.md +7 -6
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +18 -0
- package/dist/capture/c2pa-embed.d.ts +116 -0
- package/dist/capture/c2pa-embed.js +493 -0
- package/dist/capture/c2pa-embed.js.map +1 -0
- package/dist/capture/dng-import.d.ts +73 -8
- package/dist/capture/dng-import.js +315 -80
- package/dist/capture/dng-import.js.map +1 -1
- package/dist/capture/dng-lens-correction-opcodes.d.ts +2 -2
- package/dist/capture/dng-lens-correction-opcodes.js +2 -2
- package/dist/capture/dng-lens-correction-opcodes.js.map +1 -1
- package/dist/capture/dng-opcodes.d.ts +96 -0
- package/dist/capture/dng-opcodes.js +391 -0
- package/dist/capture/dng-opcodes.js.map +1 -0
- package/dist/capture/dng-profile-maps.d.ts +99 -0
- package/dist/capture/dng-profile-maps.js +381 -0
- package/dist/capture/dng-profile-maps.js.map +1 -0
- package/dist/capture/dng-stage-image.d.ts +23 -0
- package/dist/capture/dng-stage-image.js +65 -0
- package/dist/capture/dng-stage-image.js.map +1 -0
- package/dist/capture/exposure-build-up-stage.d.ts +16 -0
- package/dist/capture/exposure-build-up-stage.js +4 -0
- package/dist/capture/exposure-build-up-stage.js.map +1 -0
- package/dist/capture/jpeg-xl-tiles.d.ts +22 -0
- package/dist/capture/jpeg-xl-tiles.js +151 -0
- package/dist/capture/jpeg-xl-tiles.js.map +1 -0
- package/dist/capture/lossy-jpeg.d.ts +17 -0
- package/dist/capture/lossy-jpeg.js +369 -0
- package/dist/capture/lossy-jpeg.js.map +1 -0
- package/dist/capture/native-environment-lean.d.ts +2 -1
- package/dist/capture/native-environment-lean.js +41 -73
- package/dist/capture/native-environment-lean.js.map +1 -1
- package/dist/capture/native-environment-tabulated.js +4 -3
- package/dist/capture/native-environment-tabulated.js.map +1 -1
- package/dist/capture/native-raw-development.d.ts +2 -1
- package/dist/capture/native-raw-development.js.map +1 -1
- package/dist/capture/native-raw.d.ts +3 -2
- package/dist/capture/native-raw.js +5 -4
- package/dist/capture/native-raw.js.map +1 -1
- package/dist/capture/sensor-raw-producer.d.ts +4 -1
- package/dist/capture/sensor-raw-producer.js +12 -3
- package/dist/capture/sensor-raw-producer.js.map +1 -1
- package/dist/color/neighbor-highlight-reconstruction.d.ts +66 -0
- package/dist/color/neighbor-highlight-reconstruction.js +234 -0
- package/dist/color/neighbor-highlight-reconstruction.js.map +1 -0
- package/dist/color/sensor-color-development.d.ts +5 -0
- package/dist/color/sensor-color-development.js +7 -1
- package/dist/color/sensor-color-development.js.map +1 -1
- package/dist/color/spectral-spikiness.d.ts +11 -0
- package/dist/color/spectral-spikiness.js +23 -0
- package/dist/color/spectral-spikiness.js.map +1 -0
- package/dist/core/execution-error.d.ts +21 -0
- package/dist/core/execution-error.js +12 -0
- package/dist/core/execution-error.js.map +1 -0
- 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-lens-catalogue.d.ts +3 -2
- package/dist/equipment/generic-lens-catalogue.js +18 -5
- package/dist/equipment/generic-lens-catalogue.js.map +1 -1
- package/dist/equipment/generic-tier-assets.d.ts +32 -0
- package/dist/equipment/generic-tier-assets.js +16 -11
- package/dist/equipment/generic-tier-assets.js.map +1 -1
- package/dist/equipment/sensor-profile-body.d.ts +78 -0
- package/dist/equipment/sensor-profile-body.js +77 -0
- package/dist/equipment/sensor-profile-body.js.map +1 -0
- package/dist/equipment/sensor-profiles.d.ts +8 -0
- package/dist/equipment/sensor-profiles.js +82 -0
- package/dist/equipment/sensor-profiles.js.map +1 -0
- package/dist/facade/catalogue-lens-signal.d.ts +28 -3
- package/dist/facade/catalogue-lens-signal.js +83 -78
- package/dist/facade/catalogue-lens-signal.js.map +1 -1
- package/dist/facade/chart-radiance-batch.js +2 -1
- package/dist/facade/chart-radiance-batch.js.map +1 -1
- package/dist/facade/defocus-image-space-provider.d.ts +7 -0
- package/dist/facade/defocus-image-space-provider.js +64 -76
- package/dist/facade/defocus-image-space-provider.js.map +1 -1
- package/dist/facade/diffraction-provider.d.ts +1 -1
- package/dist/facade/diffraction-provider.js +4 -4
- package/dist/facade/diffraction-provider.js.map +1 -1
- package/dist/facade/display-view-gpu-session.js +39 -5
- package/dist/facade/display-view-gpu-session.js.map +1 -1
- package/dist/facade/display-view-gpu.d.ts +19 -6
- package/dist/facade/display-view-gpu.js.map +1 -1
- package/dist/facade/display-view-lens-signal.js +25 -23
- package/dist/facade/display-view-lens-signal.js.map +1 -1
- package/dist/facade/display-view-normal.d.ts +15 -0
- package/dist/facade/display-view-normal.js +44 -0
- package/dist/facade/display-view-normal.js.map +1 -0
- package/dist/facade/display-view-rendering.d.ts +97 -0
- package/dist/facade/display-view-rendering.js +598 -0
- package/dist/facade/display-view-rendering.js.map +1 -0
- package/dist/facade/display-view.d.ts +37 -65
- package/dist/facade/display-view.js +152 -499
- package/dist/facade/display-view.js.map +1 -1
- package/dist/facade/expected-signal-setup.d.ts +3 -1
- package/dist/facade/expected-signal-setup.js +3 -3
- package/dist/facade/expected-signal-setup.js.map +1 -1
- package/dist/facade/expected-signal.d.ts +47 -14
- package/dist/facade/expected-signal.js +117 -112
- package/dist/facade/expected-signal.js.map +1 -1
- package/dist/facade/exposure-build-up.d.ts +70 -0
- package/dist/facade/exposure-build-up.js +164 -0
- package/dist/facade/exposure-build-up.js.map +1 -0
- package/dist/facade/fast-methods.d.ts +1 -1
- package/dist/facade/fast-methods.js +46 -9
- package/dist/facade/fast-methods.js.map +1 -1
- package/dist/facade/generic-lens-capture.d.ts +3 -3
- package/dist/facade/generic-lens-capture.js +3 -3
- package/dist/facade/generic-lens-capture.js.map +1 -1
- package/dist/facade/image-plane-enable.d.ts +7 -0
- package/dist/facade/image-plane-enable.js +17 -0
- package/dist/facade/image-plane-enable.js.map +1 -0
- package/dist/facade/image-plane-lattice-data.d.ts +6 -0
- package/dist/facade/image-plane-lattice-data.js +10 -0
- package/dist/facade/image-plane-lattice-data.js.map +1 -0
- package/dist/facade/image-plane-support.d.ts +33 -0
- package/dist/facade/image-plane-support.js +11 -0
- package/dist/facade/image-plane-support.js.map +1 -0
- package/dist/facade/image-plane.d.ts +36 -0
- package/dist/facade/image-plane.js +384 -0
- package/dist/facade/image-plane.js.map +1 -0
- package/dist/facade/mixed-illumination.d.ts +108 -0
- package/dist/facade/mixed-illumination.js +174 -0
- package/dist/facade/mixed-illumination.js.map +1 -0
- package/dist/facade/on-demand-kits.d.ts +14 -0
- package/dist/facade/on-demand-kits.js +36 -0
- package/dist/facade/on-demand-kits.js.map +1 -0
- package/dist/facade/opt-in-kit.d.ts +9 -0
- package/dist/facade/opt-in-kit.js +11 -0
- package/dist/facade/opt-in-kit.js.map +1 -0
- package/dist/facade/quality-kit.d.ts +21 -0
- package/dist/facade/quality-kit.js +23 -0
- package/dist/facade/quality-kit.js.map +1 -0
- package/dist/facade/quality-paths.d.ts +22 -0
- package/dist/facade/quality-paths.js +44 -0
- package/dist/facade/quality-paths.js.map +1 -0
- package/dist/facade/representative-light-sources.d.ts +72 -0
- package/dist/facade/representative-light-sources.js +124 -0
- package/dist/facade/representative-light-sources.js.map +1 -0
- package/dist/facade/scene-radiance.d.ts +145 -4
- package/dist/facade/scene-radiance.js +50 -13
- package/dist/facade/scene-radiance.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +68 -20
- package/dist/facade/simulate-capture.js +399 -146
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.d.ts +10 -12
- package/dist/facade/simulate-display-view.js +70 -53
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/chart-composite-gpu.d.ts +13 -1
- package/dist/gpu/chart-composite-gpu.js +48 -9
- package/dist/gpu/chart-composite-gpu.js.map +1 -1
- package/dist/gpu/chart-radiance-gpu.js +4 -30
- package/dist/gpu/chart-radiance-gpu.js.map +1 -1
- package/dist/gpu/display-view-composite-material-module.d.ts +10 -4
- package/dist/gpu/display-view-composite-material-module.js +39 -13
- package/dist/gpu/display-view-composite-material-module.js.map +1 -1
- package/dist/gpu/display-view-gpu-renderer.d.ts +43 -0
- package/dist/gpu/display-view-gpu-renderer.js +134 -0
- package/dist/gpu/display-view-gpu-renderer.js.map +1 -0
- package/dist/gpu/display-view-image-material-module.d.ts +87 -0
- package/dist/gpu/display-view-image-material-module.js +361 -0
- package/dist/gpu/display-view-image-material-module.js.map +1 -0
- package/dist/gpu/display-view-image-quality.d.ts +87 -0
- package/dist/gpu/display-view-image-quality.js +625 -0
- package/dist/gpu/display-view-image-quality.js.map +1 -0
- package/dist/gpu/display-view-quality-gpu.d.ts +80 -65
- package/dist/gpu/display-view-quality-gpu.js +187 -445
- package/dist/gpu/display-view-quality-gpu.js.map +1 -1
- package/dist/gpu/display-view-speed-gpu.d.ts +67 -19
- package/dist/gpu/display-view-speed-gpu.js +181 -105
- package/dist/gpu/display-view-speed-gpu.js.map +1 -1
- package/dist/gpu/image-kernel-accelerator.js +3 -0
- package/dist/gpu/image-kernel-accelerator.js.map +1 -1
- package/dist/gpu/image-kernel-execution.d.ts +2 -13
- package/dist/gpu/image-kernel-execution.js +2 -10
- package/dist/gpu/image-kernel-execution.js.map +1 -1
- package/dist/gpu/wgsl-display-view-coupled-stub.d.ts +8 -0
- package/dist/gpu/wgsl-display-view-coupled-stub.js +21 -0
- package/dist/gpu/wgsl-display-view-coupled-stub.js.map +1 -0
- package/dist/gpu/wgsl-display-view-coupled.d.ts +88 -0
- package/dist/gpu/wgsl-display-view-coupled.js +353 -0
- package/dist/gpu/wgsl-display-view-coupled.js.map +1 -0
- package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +33 -24
- package/dist/gpu/wgsl-display-view-quality-kernel.js +166 -146
- package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
- package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +19 -44
- package/dist/gpu/wgsl-display-view-speed-kernel.js +116 -18
- package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
- package/dist/gpu/wgsl-exact-sum.d.ts +20 -0
- package/dist/gpu/wgsl-exact-sum.js +42 -0
- package/dist/gpu/wgsl-exact-sum.js.map +1 -0
- package/dist/index.d.ts +22 -6
- package/dist/index.js +16 -2
- package/dist/index.js.map +1 -1
- package/dist/motion/subject-motion.d.ts +66 -0
- package/dist/motion/subject-motion.js +52 -0
- package/dist/motion/subject-motion.js.map +1 -0
- package/dist/optics/generic-lens-lateral-color.d.ts +83 -0
- package/dist/optics/generic-lens-lateral-color.js +77 -0
- package/dist/optics/generic-lens-lateral-color.js.map +1 -0
- package/dist/optics/generic-lens-psf.js +54 -46
- package/dist/optics/generic-lens-psf.js.map +1 -1
- package/dist/optics/lens-psf-profile.d.ts +7 -0
- package/dist/optics/lens-psf-profile.js +2 -1
- package/dist/optics/lens-psf-profile.js.map +1 -1
- package/dist/output/camera-channel-response.d.ts +50 -0
- package/dist/output/camera-channel-response.js +69 -0
- package/dist/output/camera-channel-response.js.map +1 -0
- package/dist/output/developed-lens-corrections.d.ts +19 -5
- package/dist/output/developed-lens-corrections.js +28 -9
- package/dist/output/developed-lens-corrections.js.map +1 -1
- package/dist/output/generic-lens-corrections.d.ts +41 -5
- package/dist/output/generic-lens-corrections.js +52 -8
- package/dist/output/generic-lens-corrections.js.map +1 -1
- package/dist/output/native-capture-sdr.js +5 -2
- package/dist/output/native-capture-sdr.js.map +1 -1
- package/dist/output/print-plan.d.ts +41 -5
- package/dist/output/print-plan.js +59 -14
- package/dist/output/print-plan.js.map +1 -1
- package/dist/output/sdr-rendering.d.ts +18 -1
- package/dist/output/sdr-rendering.js +39 -2
- package/dist/output/sdr-rendering.js.map +1 -1
- package/dist/sensor/binomial-sample.d.ts +3 -0
- package/dist/sensor/binomial-sample.js +86 -0
- package/dist/sensor/binomial-sample.js.map +1 -0
- package/dist/sensor/native-sensor-report.d.ts +10 -0
- package/dist/sensor/native-sensor-report.js +16 -0
- package/dist/sensor/native-sensor-report.js.map +1 -0
- package/dist/sensor/sensor-characteristics.d.ts +91 -13
- package/dist/sensor/sensor-characteristics.js +30 -15
- package/dist/sensor/sensor-characteristics.js.map +1 -1
- package/docs/AGENT_GUIDE.md +1 -0
- package/docs/API_REFERENCE.md +214 -4
- package/docs/QUICK_START.md +162 -8
- package/docs/USAGE.md +195 -22
- package/docs/api/capture-c2pa-embed.md +177 -0
- package/docs/api/capture-dng-import.md +86 -10
- package/docs/api/capture-dng-opcodes.md +93 -0
- package/docs/api/capture-dng-profile-maps.md +141 -0
- package/docs/api/capture-dng-stage-image.md +43 -0
- package/docs/api/capture-jpeg-xl-tiles.md +33 -0
- package/docs/api/capture-lossy-jpeg.md +41 -0
- package/docs/api/capture-native-raw-development.md +2 -1
- package/docs/api/core-execution-error.md +43 -0
- package/docs/api/core-version.md +1 -1
- package/docs/api/equipment-generic-lens-catalogue.md +10 -5
- package/docs/api/equipment-sensor-profile-body.md +64 -0
- package/docs/api/equipment-sensor-profiles.md +86 -0
- package/docs/api/exports.json +460 -5
- package/docs/api/facade-display-view-gpu.md +5 -2
- package/docs/api/facade-display-view.md +11 -15
- package/docs/api/facade-exposure-build-up.md +105 -0
- package/docs/api/facade-fast-methods.md +47 -10
- package/docs/api/facade-image-plane-enable.md +16 -0
- package/docs/api/facade-mixed-illumination.md +67 -0
- package/docs/api/facade-quality-paths.md +24 -0
- package/docs/api/facade-representative-light-sources.md +131 -0
- package/docs/api/facade-scene-radiance.md +54 -3
- package/docs/api/facade-simulate-capture.md +48 -11
- package/docs/api/facade-simulate-display-view.md +10 -12
- package/docs/api/gpu-image-kernel-execution.md +0 -36
- package/docs/api/motion-subject-motion.md +23 -0
- package/docs/api/optics-generic-lens-lateral-color.md +79 -0
- package/docs/api/output-camera-channel-response.md +55 -0
- package/docs/api/output-developed-lens-corrections.md +10 -1
- package/docs/api/output-generic-lens-corrections.md +40 -4
- package/docs/api/output-print-plan.md +35 -1
- package/docs/api/output-sdr-rendering.md +6 -1
- package/docs/api/sensor-sensor-characteristics.md +54 -2
- package/llms.txt +3 -0
- package/package.json +6 -5
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* 2.22.0 (#594): the CPU full-quality code, one module that the capture and the display view load on demand (`loadQualityKit`,
|
|
4
|
+
* src/facade/on-demand-kits.ts) where they parse a request that asks for it, so a bundler that splits dynamic imports puts all of
|
|
5
|
+
* it in one chunk outside the default path. Only re-exports: the code, its results and its bounds are those of the modules named.
|
|
6
|
+
*
|
|
7
|
+
* - Diffraction (2.1): the Airy profile, its site kernels and their convolution.
|
|
8
|
+
* - The capture's AHD demosaic (`output.demosaic: "ahd"`).
|
|
9
|
+
* - `lens.defocus` beyond the geometric disc (owner, 2026-11-10): the bokeh shapes, the cat's eye and their field zones, the
|
|
10
|
+
* lattice of a disc above the direct radius (#564) and the image-space provider (lens projection and distortion).
|
|
11
|
+
* - The display view's coupled rendering steps (#593, #601: luminance tone curves, reconstruct, desaturation) and the neighbor
|
|
12
|
+
* highlight reconstruction (#602).
|
|
13
|
+
*/
|
|
14
|
+
export { createDiffractionLimitedPsfProfile } from "../optics/diffraction-psf-profile.js";
|
|
15
|
+
export { registerDiffractionPsfKey, siteDiffractionKernel } from "../optics/site-diffraction-kernel.js";
|
|
16
|
+
export { createAhdDemosaicTask } from "../processing/demosaic.js";
|
|
17
|
+
export { createDefocusBokehSiteKernel, resolveDefocusBokehOptions } from "../optics/defocus-bokeh-kernel.js";
|
|
18
|
+
export { planDefocusBokehZones } from "./defocus-bokeh-zones.js";
|
|
19
|
+
export { LARGE_DEFOCUS_CACHE_BYTES, createLargeDefocusNodeLattice } from "./large-defocus-lattice.js";
|
|
20
|
+
export { calculateDefocusFieldVariation, createDefocusImageSpaceProvider, prepareDefocusImageSpaceGeometry } from "./defocus-image-space-provider.js";
|
|
21
|
+
export { createDisplayViewCoupledRendering } from "./display-view-rendering.js";
|
|
22
|
+
export * as neighborHighlights from "../color/neighbor-highlight-reconstruction.js";
|
|
23
|
+
//# sourceMappingURL=quality-kit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quality-kit.js","sourceRoot":"","sources":["../../src/facade/quality-kit.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,kCAAkC,EAAE,MAAM,sCAAsC,CAAC;AAC1F,OAAO,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAC;AACxG,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,4BAA4B,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAC;AAC7G,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,yBAAyB,EAAE,6BAA6B,EAAE,MAAM,4BAA4B,CAAC;AACtG,OAAO,EAAE,8BAA8B,EAAE,+BAA+B,EAAE,gCAAgC,EAAE,MAAM,mCAAmC,CAAC;AACtJ,OAAO,EAAE,iCAAiC,EAAE,MAAM,6BAA6B,CAAC;AAChF,OAAO,KAAK,kBAAkB,MAAM,+CAA+C,CAAC","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * 2.22.0 (#594): the CPU full-quality code, one module that the capture and the display view load on demand (`loadQualityKit`,\n * src/facade/on-demand-kits.ts) where they parse a request that asks for it, so a bundler that splits dynamic imports puts all of\n * it in one chunk outside the default path. Only re-exports: the code, its results and its bounds are those of the modules named.\n *\n * - Diffraction (2.1): the Airy profile, its site kernels and their convolution.\n * - The capture's AHD demosaic (`output.demosaic: \"ahd\"`).\n * - `lens.defocus` beyond the geometric disc (owner, 2026-11-10): the bokeh shapes, the cat's eye and their field zones, the\n * lattice of a disc above the direct radius (#564) and the image-space provider (lens projection and distortion).\n * - The display view's coupled rendering steps (#593, #601: luminance tone curves, reconstruct, desaturation) and the neighbor\n * highlight reconstruction (#602).\n */\n\nexport { createDiffractionLimitedPsfProfile } from \"../optics/diffraction-psf-profile.js\";\nexport { registerDiffractionPsfKey, siteDiffractionKernel } from \"../optics/site-diffraction-kernel.js\";\nexport { createAhdDemosaicTask } from \"../processing/demosaic.js\";\nexport { createDefocusBokehSiteKernel, resolveDefocusBokehOptions } from \"../optics/defocus-bokeh-kernel.js\";\nexport { planDefocusBokehZones } from \"./defocus-bokeh-zones.js\";\nexport { LARGE_DEFOCUS_CACHE_BYTES, createLargeDefocusNodeLattice } from \"./large-defocus-lattice.js\";\nexport { calculateDefocusFieldVariation, createDefocusImageSpaceProvider, prepareDefocusImageSpaceGeometry } from \"./defocus-image-space-provider.js\";\nexport { createDisplayViewCoupledRendering } from \"./display-view-rendering.js\";\nexport * as neighborHighlights from \"../color/neighbor-highlight-reconstruction.js\";\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loads the engine's on-demand full-quality code now instead of at the first request that needs it (#594): the CPU Quality kit
|
|
3
|
+
* (diffraction, the capture's AHD demosaic, `lens.defocus` beyond the geometric disc, the image-space provider of lens projection,
|
|
4
|
+
* distortion and vignetting, the display view's coupled tone, reconstruct and desaturation steps, the neighbor highlight
|
|
5
|
+
* reconstruction), the opt-in kit (`sensor.profile`, `illumination`, `motion.subject`) and the display view's GPU full-quality code
|
|
6
|
+
* (the Quality passes behind `prepareDisplayView(…, { gpu: "auto" | "require" })`), together with the GPU session it builds on.
|
|
7
|
+
*
|
|
8
|
+
* Optional: without it, the first request that needs the code loads it. Safe to call at any time and from any number of places:
|
|
9
|
+
* concurrent calls share one load, and a call after it loaded resolves at once and loads nothing. It changes no result. If the
|
|
10
|
+
* load fails (for example a chunk download error) the promise rejects with `ImageKernelExecutionError` reason
|
|
11
|
+
* `quality-code-unavailable`; nothing is remembered, so a later call or request tries again, and a render in the meantime runs
|
|
12
|
+
* the CPU reference, labelled with that reason. A bundler that splits dynamic imports keeps this code out of the first load
|
|
13
|
+
* whether or not this function is imported.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { prepareQualityPaths } from "@photivra/engine";
|
|
18
|
+
* // The user opened Export, where full-quality renders follow: fetch their code while they choose a format.
|
|
19
|
+
* void prepareQualityPaths().catch(() => undefined); // a failed preload is retried by the render that needs the code
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function prepareQualityPaths(): Promise<void>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* 2.22.0 (#594): the optional preload of the engine's on-demand full-quality code. The Quality paths load automatically at the
|
|
4
|
+
* first request that needs them; this loads them ahead of time, so that request does not wait for the download.
|
|
5
|
+
*/
|
|
6
|
+
import { ImageKernelExecutionError } from "../gpu/image-kernel-execution.js";
|
|
7
|
+
import { optInKit, qualityKit } from "./on-demand-kits.js";
|
|
8
|
+
let pending = null;
|
|
9
|
+
/**
|
|
10
|
+
* Loads the engine's on-demand full-quality code now instead of at the first request that needs it (#594): the CPU Quality kit
|
|
11
|
+
* (diffraction, the capture's AHD demosaic, `lens.defocus` beyond the geometric disc, the image-space provider of lens projection,
|
|
12
|
+
* distortion and vignetting, the display view's coupled tone, reconstruct and desaturation steps, the neighbor highlight
|
|
13
|
+
* reconstruction), the opt-in kit (`sensor.profile`, `illumination`, `motion.subject`) and the display view's GPU full-quality code
|
|
14
|
+
* (the Quality passes behind `prepareDisplayView(…, { gpu: "auto" | "require" })`), together with the GPU session it builds on.
|
|
15
|
+
*
|
|
16
|
+
* Optional: without it, the first request that needs the code loads it. Safe to call at any time and from any number of places:
|
|
17
|
+
* concurrent calls share one load, and a call after it loaded resolves at once and loads nothing. It changes no result. If the
|
|
18
|
+
* load fails (for example a chunk download error) the promise rejects with `ImageKernelExecutionError` reason
|
|
19
|
+
* `quality-code-unavailable`; nothing is remembered, so a later call or request tries again, and a render in the meantime runs
|
|
20
|
+
* the CPU reference, labelled with that reason. A bundler that splits dynamic imports keeps this code out of the first load
|
|
21
|
+
* whether or not this function is imported.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* import { prepareQualityPaths } from "@photivra/engine";
|
|
26
|
+
* // The user opened Export, where full-quality renders follow: fetch their code while they choose a format.
|
|
27
|
+
* void prepareQualityPaths().catch(() => undefined); // a failed preload is retried by the render that needs the code
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export function prepareQualityPaths() {
|
|
31
|
+
if (!pending) {
|
|
32
|
+
const load = Promise.all([qualityKit.load(), optInKit.load(),
|
|
33
|
+
import("../gpu/display-view-gpu-renderer.js").then((renderer) => renderer.prepareDisplayViewQualityGpu())]).then(() => undefined, (error) => {
|
|
34
|
+
if (error instanceof ImageKernelExecutionError)
|
|
35
|
+
throw error;
|
|
36
|
+
throw new ImageKernelExecutionError("quality-code-unavailable", "the engine's full-quality code could not be loaded: " + (error instanceof Error ? error.message : String(error)));
|
|
37
|
+
});
|
|
38
|
+
pending = load;
|
|
39
|
+
load.catch(() => { if (pending === load)
|
|
40
|
+
pending = null; });
|
|
41
|
+
}
|
|
42
|
+
return pending;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=quality-paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quality-paths.js","sourceRoot":"","sources":["../../src/facade/quality-paths.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC;;;GAGG;AAEH,OAAO,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAC;AAC7E,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAE3D,IAAI,OAAO,GAAyB,IAAI,CAAC;AAEzC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,mBAAmB;IACjC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,GAAkB,OAAO,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,IAAI,EAAE;YACzE,MAAM,CAAC,qCAAqC,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,4BAA4B,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,EAAE,CAAC,KAAc,EAAE,EAAE;YACrJ,IAAI,KAAK,YAAY,yBAAyB;gBAAE,MAAM,KAAK,CAAC;YAC5D,MAAM,IAAI,yBAAyB,CAAC,0BAA0B,EAAE,sDAAsD,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACrL,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,IAAI,CAAC;QACf,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,IAAI,OAAO,KAAK,IAAI;YAAE,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * 2.22.0 (#594): the optional preload of the engine's on-demand full-quality code. The Quality paths load automatically at the\n * first request that needs them; this loads them ahead of time, so that request does not wait for the download.\n */\n\nimport { ImageKernelExecutionError } from \"../gpu/image-kernel-execution.js\";\nimport { optInKit, qualityKit } from \"./on-demand-kits.js\";\n\nlet pending: Promise<void> | null = null;\n\n/**\n * Loads the engine's on-demand full-quality code now instead of at the first request that needs it (#594): the CPU Quality kit\n * (diffraction, the capture's AHD demosaic, `lens.defocus` beyond the geometric disc, the image-space provider of lens projection,\n * distortion and vignetting, the display view's coupled tone, reconstruct and desaturation steps, the neighbor highlight\n * reconstruction), the opt-in kit (`sensor.profile`, `illumination`, `motion.subject`) and the display view's GPU full-quality code\n * (the Quality passes behind `prepareDisplayView(…, { gpu: \"auto\" | \"require\" })`), together with the GPU session it builds on.\n *\n * Optional: without it, the first request that needs the code loads it. Safe to call at any time and from any number of places:\n * concurrent calls share one load, and a call after it loaded resolves at once and loads nothing. It changes no result. If the\n * load fails (for example a chunk download error) the promise rejects with `ImageKernelExecutionError` reason\n * `quality-code-unavailable`; nothing is remembered, so a later call or request tries again, and a render in the meantime runs\n * the CPU reference, labelled with that reason. A bundler that splits dynamic imports keeps this code out of the first load\n * whether or not this function is imported.\n *\n * @example\n * ```ts\n * import { prepareQualityPaths } from \"@photivra/engine\";\n * // The user opened Export, where full-quality renders follow: fetch their code while they choose a format.\n * void prepareQualityPaths().catch(() => undefined); // a failed preload is retried by the render that needs the code\n * ```\n */\nexport function prepareQualityPaths(): Promise<void> {\n if (!pending) {\n const load: Promise<void> = Promise.all([qualityKit.load(), optInKit.load(),\n import(\"../gpu/display-view-gpu-renderer.js\").then((renderer) => renderer.prepareDisplayViewQualityGpu())]).then(() => undefined, (error: unknown) => {\n if (error instanceof ImageKernelExecutionError) throw error;\n throw new ImageKernelExecutionError(\"quality-code-unavailable\", \"the engine's full-quality code could not be loaded: \" + (error instanceof Error ? error.message : String(error)));\n });\n pending = load;\n load.catch(() => { if (pending === load) pending = null; });\n }\n return pending;\n}\n"]}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { ColorimetricSpectrum } from "../color/color-matching-functions.js";
|
|
2
|
+
import type { CaptureIlluminant } from "./scene-radiance.js";
|
|
3
|
+
/** Identity of the built-in light-source set. */
|
|
4
|
+
export declare const REPRESENTATIVE_LIGHT_SOURCE_SET_ID = "photivra-representative-light-sources";
|
|
5
|
+
/** Version of the set's spectra; a change to any spectrum changes it. */
|
|
6
|
+
export declare const REPRESENTATIVE_LIGHT_SOURCE_SET_VERSION = "1.0.0";
|
|
7
|
+
/** Domain of every spectrum, nm (the CIE 1931 observer's domain; the capture facade uses 380–780 nm of it). */
|
|
8
|
+
export declare const REPRESENTATIVE_LIGHT_SOURCE_WAVELENGTH_RANGE_NANOMETERS: Readonly<{
|
|
9
|
+
minimum: 360;
|
|
10
|
+
maximum: 830;
|
|
11
|
+
}>;
|
|
12
|
+
/** The source ids, in set order. */
|
|
13
|
+
export declare const REPRESENTATIVE_LIGHT_SOURCE_IDS: readonly ["tri-phosphor-fluorescent-4000k", "white-led-low-rendering-4000k", "white-led-high-rendering-4000k", "warm-white-led-2700k"];
|
|
14
|
+
export type RepresentativeLightSourceId = (typeof REPRESENTATIVE_LIGHT_SOURCE_IDS)[number];
|
|
15
|
+
/** One Gaussian band a·exp(−½((λ − μ)/σ)²) of a source spectrum. */
|
|
16
|
+
export interface RepresentativeLightSourceBand {
|
|
17
|
+
kind: "gaussian";
|
|
18
|
+
/** What the band represents, for example "blue pump" or "green phosphor line". */
|
|
19
|
+
role: string;
|
|
20
|
+
amplitude: number;
|
|
21
|
+
centerNanometers: number;
|
|
22
|
+
standardDeviationNanometers: number;
|
|
23
|
+
}
|
|
24
|
+
/** One built-in source: its id, name, design reason and spectrum definition. */
|
|
25
|
+
export interface RepresentativeLightSource {
|
|
26
|
+
sourceId: RepresentativeLightSourceId;
|
|
27
|
+
/** Photivra's descriptive name, for example "White LED, high color rendering, 4000 K". */
|
|
28
|
+
name: string;
|
|
29
|
+
family: "fluorescent" | "led";
|
|
30
|
+
/** The CCT the spectrum was designed for, K (its computed CCT is within 1 K of it, Duv within 0.0001; docs/LIGHT_SOURCES.md). */
|
|
31
|
+
nominalCorrelatedColorTemperatureKelvin: number;
|
|
32
|
+
/** Always "representative, not a named product". */
|
|
33
|
+
label: "representative, not a named product";
|
|
34
|
+
/** One sentence: what the source represents and why it is built this way. */
|
|
35
|
+
reason: string;
|
|
36
|
+
/** The constant b (a low continuum), relative units. */
|
|
37
|
+
base: number;
|
|
38
|
+
bands: readonly RepresentativeLightSourceBand[];
|
|
39
|
+
}
|
|
40
|
+
export interface RepresentativeLightSourceSet {
|
|
41
|
+
setId: typeof REPRESENTATIVE_LIGHT_SOURCE_SET_ID;
|
|
42
|
+
setVersion: typeof REPRESENTATIVE_LIGHT_SOURCE_SET_VERSION;
|
|
43
|
+
wavelengthRangeNanometers: {
|
|
44
|
+
readonly minimum: number;
|
|
45
|
+
readonly maximum: number;
|
|
46
|
+
};
|
|
47
|
+
sources: readonly RepresentativeLightSource[];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The built-in representative light sources: Photivra's own parametric spectra of a tri-phosphor fluorescent tube and
|
|
51
|
+
* three white LEDs (low and high color rendering at 4000 K, warm at 2700 K). A static, deeply frozen engine contract.
|
|
52
|
+
* Each is "representative, not a named product"; docs/LIGHT_SOURCES.md gives every formula and the reasons for it.
|
|
53
|
+
*/
|
|
54
|
+
export declare function getRepresentativeLightSourceSet(): RepresentativeLightSourceSet;
|
|
55
|
+
/**
|
|
56
|
+
* A source's relative spectral power distribution S(λ) = b + Σ aₖ·exp(−½((λ − μₖ)/σₖ)²) as a function spectrum on
|
|
57
|
+
* 360–830 nm (relative units; values memoized per wavelength). Pass it to a capture as
|
|
58
|
+
* `illuminant: { kind: "spectrum", spectrum }`; for color work use `sensor.spectralStepNanometers: 5` or finer
|
|
59
|
+
* (docs/LIGHT_SOURCES.md, "Quadrature"). Throws `InvalidScientificInputError` outside 360–830 nm.
|
|
60
|
+
*/
|
|
61
|
+
export declare function createRepresentativeLightSourceSpectrum(input: {
|
|
62
|
+
sourceId: RepresentativeLightSourceId;
|
|
63
|
+
}): ColorimetricSpectrum;
|
|
64
|
+
/**
|
|
65
|
+
* A capture illuminant for a source: `{ kind: "spectrum", spectrum, label }` with the spectrum of
|
|
66
|
+
* {@link createRepresentativeLightSourceSpectrum} and the label "<name> (representative, not a named product)".
|
|
67
|
+
*/
|
|
68
|
+
export declare function createRepresentativeLightSourceIlluminant(input: {
|
|
69
|
+
sourceId: RepresentativeLightSourceId;
|
|
70
|
+
}): Extract<CaptureIlluminant, {
|
|
71
|
+
kind: "spectrum";
|
|
72
|
+
}>;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Representative light sources (#557): four relative spectral power distributions designed by Photivra for indoor
|
|
4
|
+
* light presets, so that two lights at the same correlated color temperature (CCT) can render colors differently.
|
|
5
|
+
*
|
|
6
|
+
* Every spectrum is an engine-defined analytic function of wavelength, not measured or copied data: a constant plus a
|
|
7
|
+
* sum of Gaussian bands,
|
|
8
|
+
*
|
|
9
|
+
* S(λ) = b + Σₖ aₖ·exp(−½·((λ − μₖ)/σₖ)²),
|
|
10
|
+
*
|
|
11
|
+
* with λ in nanometers on 360–830 nm (the CIE 1931 observer's domain), in relative units (the capture facade scales an
|
|
12
|
+
* illuminant to its lux). Each source's band shapes were chosen by Photivra to represent a kind of lamp; two amplitudes
|
|
13
|
+
* (or one amplitude and one band center) were then solved so the chromaticity lies on the Planckian locus at the stated
|
|
14
|
+
* CCT, and rounded (amplitudes to 0.001, wavelengths to 0.1 nm). Those rounded values are the definition. The CCT and
|
|
15
|
+
* Duv of every rounded spectrum are checked with the engine's own `calculateCorrelatedColorTemperature` and
|
|
16
|
+
* `estimateCorrelatedColorTemperatureMcCamy` (docs/LIGHT_SOURCES.md, test/representative-light-sources.test.ts).
|
|
17
|
+
*
|
|
18
|
+
* These are representative constructions, not named products: no CIE, IES or manufacturer spectral data, names or
|
|
19
|
+
* tables are used. The bands are wider than the emission lines of real fluorescent phosphors so that the 5 nm sensor
|
|
20
|
+
* quadrature recommended for color work integrates them; real lamps of each kind vary widely.
|
|
21
|
+
*
|
|
22
|
+
* The exponentials use the portable `portableExp`, so a spectrum's values have the same bits in every JavaScript
|
|
23
|
+
* engine, in captures and in the display view alike.
|
|
24
|
+
*
|
|
25
|
+
* The module is not reached by `simulateCapture` itself: an application passes a spectrum as
|
|
26
|
+
* `illuminant: { kind: "spectrum", spectrum }` (or {@link createRepresentativeLightSourceIlluminant}), so bundles that
|
|
27
|
+
* do not use the sources do not contain them.
|
|
28
|
+
*/
|
|
29
|
+
import { InvalidConfigurationError } from "../core/configuration-error.js";
|
|
30
|
+
import { InvalidScientificInputError } from "../core/validation.js";
|
|
31
|
+
import { requireInputKeys } from "../core/record-validation.js";
|
|
32
|
+
import { portableExp } from "../core/portable-math.js";
|
|
33
|
+
/** Identity of the built-in light-source set. */
|
|
34
|
+
export const REPRESENTATIVE_LIGHT_SOURCE_SET_ID = "photivra-representative-light-sources";
|
|
35
|
+
/** Version of the set's spectra; a change to any spectrum changes it. */
|
|
36
|
+
export const REPRESENTATIVE_LIGHT_SOURCE_SET_VERSION = "1.0.0";
|
|
37
|
+
/** Domain of every spectrum, nm (the CIE 1931 observer's domain; the capture facade uses 380–780 nm of it). */
|
|
38
|
+
export const REPRESENTATIVE_LIGHT_SOURCE_WAVELENGTH_RANGE_NANOMETERS = /* @__PURE__ */ Object.freeze({ minimum: 360, maximum: 830 });
|
|
39
|
+
/** The source ids, in set order. */
|
|
40
|
+
export const REPRESENTATIVE_LIGHT_SOURCE_IDS = /* @__PURE__ */ Object.freeze([
|
|
41
|
+
"tri-phosphor-fluorescent-4000k", "white-led-low-rendering-4000k", "white-led-high-rendering-4000k", "warm-white-led-2700k"
|
|
42
|
+
]);
|
|
43
|
+
const LABEL = "representative, not a named product";
|
|
44
|
+
/* Photivra's design (docs/LIGHT_SOURCES.md, "The spectra"); the solved values were rounded and are the definition. */
|
|
45
|
+
const ROWS = [
|
|
46
|
+
["tri-phosphor-fluorescent-4000k", "Tri-phosphor fluorescent, 4000 K", "fluorescent", 4000,
|
|
47
|
+
"Three narrow emission bands in the blue, green and red on a low continuum, the construction of a tri-phosphor fluorescent tube, which leaves gaps between the bands.",
|
|
48
|
+
0.02, [["blue phosphor band", 0.285, 450, 6], ["green phosphor line", 1, 544, 3.5], ["red phosphor line", 1.02, 611, 3.5]]],
|
|
49
|
+
["white-led-low-rendering-4000k", "White LED, low color rendering, 4000 K", "led", 4000,
|
|
50
|
+
"A blue pump peak and one broad yellow phosphor band, the simplest white LED, which is weak in the cyan and the deep red.",
|
|
51
|
+
0, [["blue pump", 1.123, 450, 9], ["yellow phosphor", 1, 579.1, 50]]],
|
|
52
|
+
["white-led-high-rendering-4000k", "White LED, high color rendering, 4000 K", "led", 4000,
|
|
53
|
+
"A blue pump with a green and a red phosphor band that fill the cyan and the red, a white LED built for high color rendering at the same CCT as the low-rendering one.",
|
|
54
|
+
0, [["blue pump", 1.306, 450, 9], ["green phosphor", 1, 530, 35], ["red phosphor", 1.264, 630, 40]]],
|
|
55
|
+
["warm-white-led-2700k", "Warm white LED, 2700 K", "led", 2700,
|
|
56
|
+
"The same pump and two-phosphor construction with more red phosphor and less pump, a warm white LED for home lighting.",
|
|
57
|
+
0, [["blue pump", 0.795, 450, 9], ["green phosphor", 1, 540, 38], ["red phosphor", 2.133, 630, 40]]]
|
|
58
|
+
];
|
|
59
|
+
const SET = /* @__PURE__ */ (() => Object.freeze({
|
|
60
|
+
setId: REPRESENTATIVE_LIGHT_SOURCE_SET_ID,
|
|
61
|
+
setVersion: REPRESENTATIVE_LIGHT_SOURCE_SET_VERSION,
|
|
62
|
+
wavelengthRangeNanometers: REPRESENTATIVE_LIGHT_SOURCE_WAVELENGTH_RANGE_NANOMETERS,
|
|
63
|
+
sources: Object.freeze(ROWS.map(([sourceId, name, family, cct, reason, base, bands]) => Object.freeze({
|
|
64
|
+
sourceId, name, family, nominalCorrelatedColorTemperatureKelvin: cct, label: LABEL, reason, base,
|
|
65
|
+
bands: Object.freeze(bands.map(([role, amplitude, centerNanometers, standardDeviationNanometers]) => Object.freeze({ kind: "gaussian", role, amplitude, centerNanometers, standardDeviationNanometers })))
|
|
66
|
+
})))
|
|
67
|
+
}))();
|
|
68
|
+
/**
|
|
69
|
+
* The built-in representative light sources: Photivra's own parametric spectra of a tri-phosphor fluorescent tube and
|
|
70
|
+
* three white LEDs (low and high color rendering at 4000 K, warm at 2700 K). A static, deeply frozen engine contract.
|
|
71
|
+
* Each is "representative, not a named product"; docs/LIGHT_SOURCES.md gives every formula and the reasons for it.
|
|
72
|
+
*/
|
|
73
|
+
export function getRepresentativeLightSourceSet() {
|
|
74
|
+
return SET;
|
|
75
|
+
}
|
|
76
|
+
function findSource(input, functionName) {
|
|
77
|
+
requireInputKeys(input, functionName, ["sourceId"]);
|
|
78
|
+
const id = input.sourceId;
|
|
79
|
+
const source = typeof id === "string" ? SET.sources.find((s) => s.sourceId === id) : undefined;
|
|
80
|
+
if (!source)
|
|
81
|
+
throw new InvalidConfigurationError("sourceId must be one of the built-in representative light sources (REPRESENTATIVE_LIGHT_SOURCE_IDS).");
|
|
82
|
+
return source;
|
|
83
|
+
}
|
|
84
|
+
/** Most wavelengths whose values one spectrum keeps (a capture uses a few hundred). */
|
|
85
|
+
const CACHE_LIMIT = 4096;
|
|
86
|
+
/**
|
|
87
|
+
* A source's relative spectral power distribution S(λ) = b + Σ aₖ·exp(−½((λ − μₖ)/σₖ)²) as a function spectrum on
|
|
88
|
+
* 360–830 nm (relative units; values memoized per wavelength). Pass it to a capture as
|
|
89
|
+
* `illuminant: { kind: "spectrum", spectrum }`; for color work use `sensor.spectralStepNanometers: 5` or finer
|
|
90
|
+
* (docs/LIGHT_SOURCES.md, "Quadrature"). Throws `InvalidScientificInputError` outside 360–830 nm.
|
|
91
|
+
*/
|
|
92
|
+
export function createRepresentativeLightSourceSpectrum(input) {
|
|
93
|
+
const source = findSource(input, "createRepresentativeLightSourceSpectrum");
|
|
94
|
+
const range = REPRESENTATIVE_LIGHT_SOURCE_WAVELENGTH_RANGE_NANOMETERS, cache = new Map();
|
|
95
|
+
return {
|
|
96
|
+
kind: "function",
|
|
97
|
+
wavelengthRangeNanometers: { minimum: range.minimum, maximum: range.maximum },
|
|
98
|
+
evaluate(nm) {
|
|
99
|
+
const hit = cache.get(nm);
|
|
100
|
+
if (hit !== undefined)
|
|
101
|
+
return hit;
|
|
102
|
+
if (typeof nm !== "number" || !(nm >= range.minimum && nm <= range.maximum)) {
|
|
103
|
+
throw new InvalidScientificInputError("Representative light source " + source.sourceId + " is defined on 360–830 nm.");
|
|
104
|
+
}
|
|
105
|
+
let value = source.base;
|
|
106
|
+
for (const band of source.bands) {
|
|
107
|
+
const z = (nm - band.centerNanometers) / band.standardDeviationNanometers;
|
|
108
|
+
value += band.amplitude * portableExp(-0.5 * z * z);
|
|
109
|
+
}
|
|
110
|
+
if (cache.size < CACHE_LIMIT)
|
|
111
|
+
cache.set(nm, value);
|
|
112
|
+
return value;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* A capture illuminant for a source: `{ kind: "spectrum", spectrum, label }` with the spectrum of
|
|
118
|
+
* {@link createRepresentativeLightSourceSpectrum} and the label "<name> (representative, not a named product)".
|
|
119
|
+
*/
|
|
120
|
+
export function createRepresentativeLightSourceIlluminant(input) {
|
|
121
|
+
const source = findSource(input, "createRepresentativeLightSourceIlluminant");
|
|
122
|
+
return { kind: "spectrum", spectrum: createRepresentativeLightSourceSpectrum({ sourceId: source.sourceId }), label: source.name + " (" + LABEL + ")" };
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=representative-light-sources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"representative-light-sources.js","sourceRoot":"","sources":["../../src/facade/representative-light-sources.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Representative light sources (#557): four relative spectral power distributions designed by Photivra for indoor\n * light presets, so that two lights at the same correlated color temperature (CCT) can render colors differently.\n *\n * Every spectrum is an engine-defined analytic function of wavelength, not measured or copied data: a constant plus a\n * sum of Gaussian bands,\n *\n * S(λ) = b + Σₖ aₖ·exp(−½·((λ − μₖ)/σₖ)²),\n *\n * with λ in nanometers on 360–830 nm (the CIE 1931 observer's domain), in relative units (the capture facade scales an\n * illuminant to its lux). Each source's band shapes were chosen by Photivra to represent a kind of lamp; two amplitudes\n * (or one amplitude and one band center) were then solved so the chromaticity lies on the Planckian locus at the stated\n * CCT, and rounded (amplitudes to 0.001, wavelengths to 0.1 nm). Those rounded values are the definition. The CCT and\n * Duv of every rounded spectrum are checked with the engine's own `calculateCorrelatedColorTemperature` and\n * `estimateCorrelatedColorTemperatureMcCamy` (docs/LIGHT_SOURCES.md, test/representative-light-sources.test.ts).\n *\n * These are representative constructions, not named products: no CIE, IES or manufacturer spectral data, names or\n * tables are used. The bands are wider than the emission lines of real fluorescent phosphors so that the 5 nm sensor\n * quadrature recommended for color work integrates them; real lamps of each kind vary widely.\n *\n * The exponentials use the portable `portableExp`, so a spectrum's values have the same bits in every JavaScript\n * engine, in captures and in the display view alike.\n *\n * The module is not reached by `simulateCapture` itself: an application passes a spectrum as\n * `illuminant: { kind: \"spectrum\", spectrum }` (or {@link createRepresentativeLightSourceIlluminant}), so bundles that\n * do not use the sources do not contain them.\n */\n\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport { requireInputKeys } from \"../core/record-validation.js\";\nimport { portableExp } from \"../core/portable-math.js\";\nimport type { ColorimetricSpectrum } from \"../color/color-matching-functions.js\";\nimport type { CaptureIlluminant } from \"./scene-radiance.js\";\n\n/** Identity of the built-in light-source set. */\nexport const REPRESENTATIVE_LIGHT_SOURCE_SET_ID = \"photivra-representative-light-sources\";\n/** Version of the set's spectra; a change to any spectrum changes it. */\nexport const REPRESENTATIVE_LIGHT_SOURCE_SET_VERSION = \"1.0.0\";\n/** Domain of every spectrum, nm (the CIE 1931 observer's domain; the capture facade uses 380–780 nm of it). */\nexport const REPRESENTATIVE_LIGHT_SOURCE_WAVELENGTH_RANGE_NANOMETERS = /* @__PURE__ */ Object.freeze({ minimum: 360, maximum: 830 });\n\n/** The source ids, in set order. */\nexport const REPRESENTATIVE_LIGHT_SOURCE_IDS = /* @__PURE__ */ Object.freeze([\n \"tri-phosphor-fluorescent-4000k\", \"white-led-low-rendering-4000k\", \"white-led-high-rendering-4000k\", \"warm-white-led-2700k\"\n] as const);\nexport type RepresentativeLightSourceId = (typeof REPRESENTATIVE_LIGHT_SOURCE_IDS)[number];\n\n/** One Gaussian band a·exp(−½((λ − μ)/σ)²) of a source spectrum. */\nexport interface RepresentativeLightSourceBand {\n kind: \"gaussian\";\n /** What the band represents, for example \"blue pump\" or \"green phosphor line\". */\n role: string;\n amplitude: number;\n centerNanometers: number;\n standardDeviationNanometers: number;\n}\n\n/** One built-in source: its id, name, design reason and spectrum definition. */\nexport interface RepresentativeLightSource {\n sourceId: RepresentativeLightSourceId;\n /** Photivra's descriptive name, for example \"White LED, high color rendering, 4000 K\". */\n name: string;\n family: \"fluorescent\" | \"led\";\n /** The CCT the spectrum was designed for, K (its computed CCT is within 1 K of it, Duv within 0.0001; docs/LIGHT_SOURCES.md). */\n nominalCorrelatedColorTemperatureKelvin: number;\n /** Always \"representative, not a named product\". */\n label: \"representative, not a named product\";\n /** One sentence: what the source represents and why it is built this way. */\n reason: string;\n /** The constant b (a low continuum), relative units. */\n base: number;\n bands: readonly RepresentativeLightSourceBand[];\n}\n\nexport interface RepresentativeLightSourceSet {\n setId: typeof REPRESENTATIVE_LIGHT_SOURCE_SET_ID;\n setVersion: typeof REPRESENTATIVE_LIGHT_SOURCE_SET_VERSION;\n wavelengthRangeNanometers: { readonly minimum: number; readonly maximum: number };\n sources: readonly RepresentativeLightSource[];\n}\n\nconst LABEL = \"representative, not a named product\" as const;\ntype BandRow = readonly [role: string, amplitude: number, centerNanometers: number, standardDeviationNanometers: number];\n/* Photivra's design (docs/LIGHT_SOURCES.md, \"The spectra\"); the solved values were rounded and are the definition. */\nconst ROWS: readonly (readonly [RepresentativeLightSourceId, string, \"fluorescent\" | \"led\", number, string, number, readonly BandRow[]])[] = [\n [\"tri-phosphor-fluorescent-4000k\", \"Tri-phosphor fluorescent, 4000 K\", \"fluorescent\", 4000,\n \"Three narrow emission bands in the blue, green and red on a low continuum, the construction of a tri-phosphor fluorescent tube, which leaves gaps between the bands.\",\n 0.02, [[\"blue phosphor band\", 0.285, 450, 6], [\"green phosphor line\", 1, 544, 3.5], [\"red phosphor line\", 1.02, 611, 3.5]]],\n [\"white-led-low-rendering-4000k\", \"White LED, low color rendering, 4000 K\", \"led\", 4000,\n \"A blue pump peak and one broad yellow phosphor band, the simplest white LED, which is weak in the cyan and the deep red.\",\n 0, [[\"blue pump\", 1.123, 450, 9], [\"yellow phosphor\", 1, 579.1, 50]]],\n [\"white-led-high-rendering-4000k\", \"White LED, high color rendering, 4000 K\", \"led\", 4000,\n \"A blue pump with a green and a red phosphor band that fill the cyan and the red, a white LED built for high color rendering at the same CCT as the low-rendering one.\",\n 0, [[\"blue pump\", 1.306, 450, 9], [\"green phosphor\", 1, 530, 35], [\"red phosphor\", 1.264, 630, 40]]],\n [\"warm-white-led-2700k\", \"Warm white LED, 2700 K\", \"led\", 2700,\n \"The same pump and two-phosphor construction with more red phosphor and less pump, a warm white LED for home lighting.\",\n 0, [[\"blue pump\", 0.795, 450, 9], [\"green phosphor\", 1, 540, 38], [\"red phosphor\", 2.133, 630, 40]]]\n];\n\nconst SET: RepresentativeLightSourceSet = /* @__PURE__ */ ((): RepresentativeLightSourceSet => Object.freeze({\n setId: REPRESENTATIVE_LIGHT_SOURCE_SET_ID,\n setVersion: REPRESENTATIVE_LIGHT_SOURCE_SET_VERSION,\n wavelengthRangeNanometers: REPRESENTATIVE_LIGHT_SOURCE_WAVELENGTH_RANGE_NANOMETERS,\n sources: Object.freeze(ROWS.map(([sourceId, name, family, cct, reason, base, bands]) => Object.freeze({\n sourceId, name, family, nominalCorrelatedColorTemperatureKelvin: cct, label: LABEL, reason, base,\n bands: Object.freeze(bands.map(([role, amplitude, centerNanometers, standardDeviationNanometers]) =>\n Object.freeze({ kind: \"gaussian\" as const, role, amplitude, centerNanometers, standardDeviationNanometers })))\n })))\n}))();\n\n/**\n * The built-in representative light sources: Photivra's own parametric spectra of a tri-phosphor fluorescent tube and\n * three white LEDs (low and high color rendering at 4000 K, warm at 2700 K). A static, deeply frozen engine contract.\n * Each is \"representative, not a named product\"; docs/LIGHT_SOURCES.md gives every formula and the reasons for it.\n */\nexport function getRepresentativeLightSourceSet(): RepresentativeLightSourceSet {\n return SET;\n}\n\nfunction findSource(input: unknown, functionName: string): RepresentativeLightSource {\n requireInputKeys(input, functionName, [\"sourceId\"]);\n const id = (input as { sourceId?: unknown }).sourceId;\n const source = typeof id === \"string\" ? SET.sources.find((s) => s.sourceId === id) : undefined;\n if (!source) throw new InvalidConfigurationError(\"sourceId must be one of the built-in representative light sources (REPRESENTATIVE_LIGHT_SOURCE_IDS).\");\n return source;\n}\n\n/** Most wavelengths whose values one spectrum keeps (a capture uses a few hundred). */\nconst CACHE_LIMIT = 4096;\n\n/**\n * A source's relative spectral power distribution S(λ) = b + Σ aₖ·exp(−½((λ − μₖ)/σₖ)²) as a function spectrum on\n * 360–830 nm (relative units; values memoized per wavelength). Pass it to a capture as\n * `illuminant: { kind: \"spectrum\", spectrum }`; for color work use `sensor.spectralStepNanometers: 5` or finer\n * (docs/LIGHT_SOURCES.md, \"Quadrature\"). Throws `InvalidScientificInputError` outside 360–830 nm.\n */\nexport function createRepresentativeLightSourceSpectrum(input: { sourceId: RepresentativeLightSourceId }): ColorimetricSpectrum {\n const source = findSource(input, \"createRepresentativeLightSourceSpectrum\");\n const range = REPRESENTATIVE_LIGHT_SOURCE_WAVELENGTH_RANGE_NANOMETERS, cache = new Map<number, number>();\n return {\n kind: \"function\",\n wavelengthRangeNanometers: { minimum: range.minimum, maximum: range.maximum },\n evaluate(nm: number): number {\n const hit = cache.get(nm);\n if (hit !== undefined) return hit;\n if (typeof nm !== \"number\" || !(nm >= range.minimum && nm <= range.maximum)) {\n throw new InvalidScientificInputError(\"Representative light source \" + source.sourceId + \" is defined on 360–830 nm.\");\n }\n let value = source.base;\n for (const band of source.bands) {\n const z = (nm - band.centerNanometers) / band.standardDeviationNanometers;\n value += band.amplitude * portableExp(-0.5 * z * z);\n }\n if (cache.size < CACHE_LIMIT) cache.set(nm, value);\n return value;\n }\n };\n}\n\n/**\n * A capture illuminant for a source: `{ kind: \"spectrum\", spectrum, label }` with the spectrum of\n * {@link createRepresentativeLightSourceSpectrum} and the label \"<name> (representative, not a named product)\".\n */\nexport function createRepresentativeLightSourceIlluminant(input: { sourceId: RepresentativeLightSourceId }): Extract<CaptureIlluminant, { kind: \"spectrum\" }> {\n const source = findSource(input, \"createRepresentativeLightSourceIlluminant\");\n return { kind: \"spectrum\", spectrum: createRepresentativeLightSourceSpectrum({ sourceId: source.sourceId }), label: source.name + \" (\" + LABEL + \")\" };\n}\n"]}
|
|
@@ -5,6 +5,7 @@ import type { EvidenceProvenance } from "../core/evidence-provenance.js";
|
|
|
5
5
|
import type { SensorApertureRay } from "../optics/sensor-aperture-rays.js";
|
|
6
6
|
import { type ChartReflectance, type ChartScene, type ResolvedChartScene } from "./chart-scenes.js";
|
|
7
7
|
import { type ChartMaterialLayerScene } from "./chart-material-layers.js";
|
|
8
|
+
import type { ResolvedMixedIllumination } from "./mixed-illumination.js";
|
|
8
9
|
/** CIE maximum luminous efficacy for photopic vision, lm/W. */
|
|
9
10
|
export declare const PHOTOPIC_MAXIMUM_LUMINOUS_EFFICACY_LM_PER_W = 683;
|
|
10
11
|
/** Wavelength range of every facade spectrum and of the facade sensor quadrature, nm. */
|
|
@@ -101,6 +102,128 @@ export interface CallerMaterialScene {
|
|
|
101
102
|
}
|
|
102
103
|
/** Most materials a {@link CallerMaterialScene} may list. */
|
|
103
104
|
export declare const CALLER_MATERIAL_SCENE_MAX_MATERIALS = 65536;
|
|
105
|
+
/**
|
|
106
|
+
* 2.21 (#581): an image the caller supplies, for an {@link ImagePlaneScene}. `data` holds `width × height` pixels row by row
|
|
107
|
+
* from the top, `channels` (3: RGB, 4: RGBA) samples each, in the encoding's type: `srgb-8` Uint8Array or Uint8ClampedArray
|
|
108
|
+
* (canvas `ImageData.data`), `srgb-16` Uint16Array, `linear-srgb` Float32Array. sRGB primaries and white (IEC 61966-2-1) in
|
|
109
|
+
* every encoding; `srgb-8` and `srgb-16` are decoded with the sRGB transfer function, `linear-srgb` is already linear and is
|
|
110
|
+
* clipped to [0, 1] (the clipped samples are counted). Alpha is linear coverage over the paper. The array is read, never
|
|
111
|
+
* changed or kept beyond the call.
|
|
112
|
+
*/
|
|
113
|
+
export interface ImagePlaneImage {
|
|
114
|
+
width: number;
|
|
115
|
+
height: number;
|
|
116
|
+
channels: 3 | 4;
|
|
117
|
+
encoding: "srgb-8" | "srgb-16" | "linear-srgb";
|
|
118
|
+
data: Uint8Array | Uint8ClampedArray | Uint16Array | Float32Array;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* 2.21 (#581): a photo as a flat matte print on the scene plane, lit by the request's illuminant like a chart. Each pixel's
|
|
122
|
+
* color becomes a reflectance spectrum through the engine's print reflectance lattice (docs/IMAGE_PLANE.md): an
|
|
123
|
+
* approximation, a print of an image that was already processed, clipped and blurred by the camera that took it. The print's
|
|
124
|
+
* physical size is `chartSize` (its height defaults to the image's aspect); without it the print is fitted inside the frame,
|
|
125
|
+
* centered, on the 18 % surround.
|
|
126
|
+
*/
|
|
127
|
+
export interface ImagePlaneScene {
|
|
128
|
+
kind: "image-plane";
|
|
129
|
+
sceneId: string;
|
|
130
|
+
image: ImagePlaneImage;
|
|
131
|
+
/** The paper and the deepest ink: reflectances of sRGB white and black, 0 ≤ black < white ≤ 1. Defaults 0.90 and 0.01. */
|
|
132
|
+
paper?: {
|
|
133
|
+
whiteReflectance?: number;
|
|
134
|
+
blackReflectance?: number;
|
|
135
|
+
};
|
|
136
|
+
/** How the print is reconstructed between pixel centers. Default `bilinear` (the pixels' spectra, bilinearly weighted). */
|
|
137
|
+
interpolation?: "bilinear" | "nearest";
|
|
138
|
+
/** Opt-in raised pixel limit: an integer up to the full-resolution ceiling. Default 24 000 000 pixels. */
|
|
139
|
+
maximumPixels?: number;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Internal (#581): what a pixel's signals are integrated from (`ResolvedImagePlaneScene.pixelSignals`): the lattice's node spectra at
|
|
143
|
+
* the N spectral nodes (node k's reflectance at λ_n at k·N + n), the white radiance and the channel responses per node.
|
|
144
|
+
*/
|
|
145
|
+
export interface ImagePlaneSpectralNodes {
|
|
146
|
+
count: number;
|
|
147
|
+
nodeSpectra: Float64Array;
|
|
148
|
+
whiteRadiance: Float64Array;
|
|
149
|
+
response: readonly Float64Array[];
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Internal (#581): an image-plane print as the tabulated expected signal's scene (`ResolvedImagePlaneScene.signalScene`): its radiance
|
|
153
|
+
* per node, the direct path's signal (each pixel's Σ_n A_c(λ_n)·L_n integrated from its reconstructed spectrum, `pixelSignals`, then
|
|
154
|
+
* interpolated between pixels), and (for the GPU module) the scene, its spectral nodes and the surround's channel signals.
|
|
155
|
+
*/
|
|
156
|
+
export interface ImagePlaneSignalScene {
|
|
157
|
+
kind: "radiance";
|
|
158
|
+
radianceAt(u: number, v: number, nm: number): number;
|
|
159
|
+
signalAt(u: number, v: number, c: number): number;
|
|
160
|
+
image: {
|
|
161
|
+
scene: ResolvedImagePlaneScene;
|
|
162
|
+
nodes: ImagePlaneSpectralNodes;
|
|
163
|
+
surround: readonly [number, number, number];
|
|
164
|
+
whiteRadiance: Float64Array;
|
|
165
|
+
surroundReflectance: number;
|
|
166
|
+
/** #597: under mixed light, its parts and the print's half width in metres (print-local cu = ±1 at ±halfWidthM); no `signalAt` then. */
|
|
167
|
+
light?: {
|
|
168
|
+
layout: ResolvedMixedIllumination | null;
|
|
169
|
+
halfWidthM: number;
|
|
170
|
+
};
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
/** #597: an image-plane print under mixed light: the lit radiance only (every site integrates it), and the print for the GPU pass. */
|
|
174
|
+
export interface ImagePlaneLitScene {
|
|
175
|
+
kind: "radiance";
|
|
176
|
+
radianceAt(u: number, v: number, nm: number): number;
|
|
177
|
+
image: ImagePlaneSignalScene["image"];
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Internal (#581): a validated image-plane scene (src/facade/image-plane.ts, a lazily loaded module), placed on a print of
|
|
181
|
+
* aspect `aspect` (height / width). Print-local coordinates are the chart's: cu = ±1 at the print's left and right edges, cv up
|
|
182
|
+
* on the same scale, ±aspect at its top and bottom.
|
|
183
|
+
*/
|
|
184
|
+
export interface ResolvedImagePlaneScene {
|
|
185
|
+
kind: "image-plane";
|
|
186
|
+
sceneId: string;
|
|
187
|
+
width: number;
|
|
188
|
+
height: number;
|
|
189
|
+
aspect: number;
|
|
190
|
+
channels: 3 | 4;
|
|
191
|
+
encoding: ImagePlaneImage["encoding"];
|
|
192
|
+
interpolation: "bilinear" | "nearest";
|
|
193
|
+
paperWhiteReflectance: number;
|
|
194
|
+
paperBlackReflectance: number;
|
|
195
|
+
maximumPixels: number;
|
|
196
|
+
/** `linear-srgb` samples (color and alpha) clipped to [0, 1]; 0 for the integer encodings. */
|
|
197
|
+
clippedSampleCount: number;
|
|
198
|
+
/** SHA-256 of the image data's bytes, hex. */
|
|
199
|
+
dataSha256: string;
|
|
200
|
+
/** The print's reflectance at a print-local point and wavelength (nm, 380–780). */
|
|
201
|
+
reflectanceAt(cu: number, cv: number, nm: number): number;
|
|
202
|
+
/**
|
|
203
|
+
* One pixel's three channel signals Σ_n A_c(λ_n)·ρ_n·W_n, integrated from its reconstructed spectrum ρ_n (its cell's node spectra
|
|
204
|
+
* blended, then the paper) at the time of use, into `out[at..at + 2]`. No per-channel values of the lattice nodes are formed.
|
|
205
|
+
*/
|
|
206
|
+
pixelSignals(index: number, nodes: ImagePlaneSpectralNodes, out: Float64Array, at: number): void;
|
|
207
|
+
/** The print at a print-local point in node values (`nodeValues[k]` of node k, `neutral` of the flat reflectance 1). */
|
|
208
|
+
sampleAt(cu: number, cv: number, nodeValues: Float64Array, neutral: number): number;
|
|
209
|
+
/** One pixel (row-major index) in node values. */
|
|
210
|
+
pixelValue(index: number, nodeValues: Float64Array, neutral: number): number;
|
|
211
|
+
/** One pixel's lattice cell (its base node r·25 + g·5 + b, returned) and its fractions within the cell per channel (written to `out[0..2]`). */
|
|
212
|
+
pixelLattice(index: number, out: Float64Array): number;
|
|
213
|
+
/** Every lattice node's reflectance at one wavelength (nm, 380–780). */
|
|
214
|
+
nodeReflectances(nm: number): Float64Array;
|
|
215
|
+
/**
|
|
216
|
+
* The print placed on the plane (frame point → print-local cu = u·scale/half, cv = v·scale/half; outside |cu| ≤ 1, |cv| ≤ aspect the
|
|
217
|
+
* surround) as the expected signal's scene at these nodes, responses and white radiance (see `ImagePlaneSignalScene`). #597: with
|
|
218
|
+
* `light` (mixed lighting; null when its parts are not given), the lit print: `radianceAt` only, no per-pixel signals.
|
|
219
|
+
*/
|
|
220
|
+
signalScene(wavelengths: readonly number[], perNode: readonly Float64Array[], whiteRadiance: Float64Array, radianceAt: (u: number, v: number, nm: number) => number, scale: number, half: number, surroundReflectance: number): ImagePlaneSignalScene;
|
|
221
|
+
signalScene(wavelengths: readonly number[], perNode: readonly Float64Array[], whiteRadiance: Float64Array, radianceAt: (u: number, v: number, nm: number) => number, scale: number, half: number, surroundReflectance: number, light: ResolvedMixedIllumination | null | undefined): ImagePlaneSignalScene | ImagePlaneLitScene;
|
|
222
|
+
/** The scene's form in the facade's source digest (the image's SHA-256 and shape, the paper, the interpolation, the lattice version). */
|
|
223
|
+
digestForm: Readonly<Record<string, unknown>>;
|
|
224
|
+
/** The result's `scene.parameters`: the image, paper and interpolation, the clipped samples and the image's SHA-256. */
|
|
225
|
+
parameters: Readonly<Record<string, unknown>>;
|
|
226
|
+
}
|
|
104
227
|
/** Internal: a validated material scene with its materials prepared (and the caller inputs, for the scene digest). */
|
|
105
228
|
export interface ResolvedCallerMaterialScene {
|
|
106
229
|
kind: "material-function";
|
|
@@ -110,7 +233,7 @@ export interface ResolvedCallerMaterialScene {
|
|
|
110
233
|
materialAt(point: Readonly<SceneMaterialPoint>): number;
|
|
111
234
|
}
|
|
112
235
|
/** Internal: a validated caller scene. */
|
|
113
|
-
export type ResolvedCallerScene = CallerReflectanceScene | CallerRadianceScene | ResolvedCallerMaterialScene;
|
|
236
|
+
export type ResolvedCallerScene = CallerReflectanceScene | CallerRadianceScene | ResolvedCallerMaterialScene | ResolvedImagePlaneScene;
|
|
114
237
|
export interface PlanarSceneGeometry {
|
|
115
238
|
distanceM: number;
|
|
116
239
|
/** Metres per frame unit on the plane (half the framed width). */
|
|
@@ -131,8 +254,11 @@ export interface PlanarSceneRadianceProvider {
|
|
|
131
254
|
providerProfileId: string;
|
|
132
255
|
evaluateRadiance(request: Readonly<SceneRadianceEvaluationRequest>): SceneRadianceEvaluationResult;
|
|
133
256
|
evaluateApertureRadiance(request: Readonly<SceneRadianceEvaluationRequest>, ray: Readonly<SensorApertureRay>): SceneRadianceEvaluationResult;
|
|
134
|
-
/**
|
|
135
|
-
|
|
257
|
+
/**
|
|
258
|
+
* Spectral radiance at a plane point (frame units), the same value the callbacks return. #551: a moving subject's provider
|
|
259
|
+
* reads it at time `t` (seconds from the first opening, default 0); a still scene ignores it.
|
|
260
|
+
*/
|
|
261
|
+
radianceAt(u: number, v: number, wavelengthNanometers: number, t?: number): number;
|
|
136
262
|
/**
|
|
137
263
|
* 2.3, chart scenes only: the chart material at a plane point (frame units), or `materials.length` for the
|
|
138
264
|
* surround of a fixed-size chart; `materialRadiance` gives that material's spectral radiance, the same value
|
|
@@ -140,6 +266,12 @@ export interface PlanarSceneRadianceProvider {
|
|
|
140
266
|
*/
|
|
141
267
|
chartMaterialAt?(u: number, v: number): number;
|
|
142
268
|
materialRadiance?(material: number, wavelengthNanometers: number): number;
|
|
269
|
+
/**
|
|
270
|
+
* 2.21 (#581), image-plane scenes only: the tabulated expected signal's scene at these spectral nodes and channel responses, its
|
|
271
|
+
* radiance per node and, for the direct path, Σ_n A_c(λ_n)·L(λ_n) integrated from each pixel's reconstructed spectrum
|
|
272
|
+
* (`ResolvedImagePlaneScene.pixelSignals`, the surround's reflectance times the neutral signal outside the print).
|
|
273
|
+
*/
|
|
274
|
+
imageSignalScene?(wavelengths: readonly number[], perNode: readonly Float64Array[]): ImagePlaneSignalScene | ImagePlaneLitScene;
|
|
143
275
|
}
|
|
144
276
|
/** Internal (#394): the material lookup of a provider built by {@link buildPlanarSceneRadianceProvider}, if it has one. */
|
|
145
277
|
export declare function planarProviderMaterialOfDirection(provider: PlanarSceneRadianceProvider): ((direction: Readonly<{
|
|
@@ -171,6 +303,14 @@ export declare function buildPlanarSceneRadianceProvider(input: {
|
|
|
171
303
|
* radiance instead of failing; the plane fills the forward hemisphere only. Omitted: such a query fails, as before.
|
|
172
304
|
*/
|
|
173
305
|
zeroBeyondHorizon?: true;
|
|
306
|
+
/**
|
|
307
|
+
* #550, mixed illumination: the plane's spectral irradiance at a plane point, given in chart units (chart-local with a fixed-size
|
|
308
|
+
* chart, frame units without) and in metres from the optical axis, used instead of `irradianceScale`·S(λ). The provider then has no material access (the radiance at a point is
|
|
309
|
+
* no longer a function of its material alone), so every renderer reads `radianceAt`. A radiance-function scene is refused.
|
|
310
|
+
*/
|
|
311
|
+
irradianceAt?: (u: number, v: number, xM: number, yM: number, wavelengthNanometers: number) => number;
|
|
312
|
+
/** #597: the illumination's parts behind `irradianceAt`, which an image-plane print hands to the GPU's image-plane pass. */
|
|
313
|
+
illumination?: ResolvedMixedIllumination;
|
|
174
314
|
}): PlanarSceneRadianceProvider;
|
|
175
315
|
/** 2.3: a chart's physical size on the scene plane (`simulateCapture` `chartSize`, `createPlanarSceneRadianceProvider` `chartSize`). */
|
|
176
316
|
export interface ChartSizeOption {
|
|
@@ -199,7 +339,8 @@ export declare function resolveChartSizeOption(value: unknown, frameAspect: numb
|
|
|
199
339
|
* provider `providerProfileId`.
|
|
200
340
|
*/
|
|
201
341
|
export declare function createPlanarSceneRadianceProvider(input: {
|
|
202
|
-
scene:
|
|
342
|
+
/** An `image-plane` scene (#581) is refused: it is taken by the facade's capture and display-view functions, which load its module. */
|
|
343
|
+
scene: ChartScene | ChartMaterialLayerScene | CallerReflectanceScene | CallerRadianceScene | CallerMaterialScene | ImagePlaneScene;
|
|
203
344
|
illuminant: CaptureIlluminant;
|
|
204
345
|
illuminanceLux: number;
|
|
205
346
|
distanceM: number;
|