@photivra/engine 0.5.1 → 1.0.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/AGENTS.md +674 -0
- package/CHANGELOG.md +218 -0
- package/CITATION.cff +1 -1
- package/CONTRIBUTING.md +76 -0
- package/DCO +33 -0
- package/NODE_SUPPORT.md +37 -0
- package/NOTICE +2 -0
- package/README.md +157 -56
- package/SECURITY.md +55 -0
- package/THIRD_PARTY.md +63 -0
- package/dist/camera/equivalent-focal-length.d.ts +9 -0
- package/dist/camera/equivalent-focal-length.d.ts.map +1 -1
- package/dist/camera/equivalent-focal-length.js +9 -0
- package/dist/camera/equivalent-focal-length.js.map +1 -1
- package/dist/camera/field-of-view.d.ts +13 -0
- package/dist/camera/field-of-view.d.ts.map +1 -1
- package/dist/camera/field-of-view.js +13 -0
- package/dist/camera/field-of-view.js.map +1 -1
- package/dist/camera/projected-object-size.d.ts +8 -0
- package/dist/camera/projected-object-size.d.ts.map +1 -1
- package/dist/camera/projected-object-size.js +8 -0
- package/dist/camera/projected-object-size.js.map +1 -1
- package/dist/capture/capture-export-metadata.d.ts +60 -0
- package/dist/capture/capture-export-metadata.d.ts.map +1 -0
- package/dist/capture/capture-export-metadata.js +62 -0
- package/dist/capture/capture-export-metadata.js.map +1 -0
- package/dist/capture/environment-raw-producer.d.ts +55 -0
- package/dist/capture/environment-raw-producer.d.ts.map +1 -0
- package/dist/capture/environment-raw-producer.js +88 -0
- package/dist/capture/environment-raw-producer.js.map +1 -0
- package/dist/capture/export-jpeg.d.ts +12 -0
- package/dist/capture/export-jpeg.d.ts.map +1 -0
- package/dist/capture/export-jpeg.js +126 -0
- package/dist/capture/export-jpeg.js.map +1 -0
- package/dist/capture/export-tiff.d.ts +45 -0
- package/dist/capture/export-tiff.d.ts.map +1 -0
- package/dist/capture/export-tiff.js +129 -0
- package/dist/capture/export-tiff.js.map +1 -0
- package/dist/capture/focus-control.d.ts +210 -0
- package/dist/capture/focus-control.d.ts.map +1 -0
- package/dist/capture/focus-control.js +664 -0
- package/dist/capture/focus-control.js.map +1 -0
- package/dist/capture/linear-encoding.d.ts +59 -0
- package/dist/capture/linear-encoding.d.ts.map +1 -0
- package/dist/capture/linear-encoding.js +98 -0
- package/dist/capture/linear-encoding.js.map +1 -0
- package/dist/capture/photographic-export.d.ts +135 -0
- package/dist/capture/photographic-export.d.ts.map +1 -0
- package/dist/capture/photographic-export.js +273 -0
- package/dist/capture/photographic-export.js.map +1 -0
- package/dist/capture/raw-frame-limits.d.ts +12 -0
- package/dist/capture/raw-frame-limits.d.ts.map +1 -0
- package/dist/capture/raw-frame-limits.js +13 -0
- package/dist/capture/raw-frame-limits.js.map +1 -0
- package/dist/capture/raw-frame-reconstruction.d.ts +48 -0
- package/dist/capture/raw-frame-reconstruction.d.ts.map +1 -0
- package/dist/capture/raw-frame-reconstruction.js +146 -0
- package/dist/capture/raw-frame-reconstruction.js.map +1 -0
- package/dist/capture/release-sequence.d.ts +120 -0
- package/dist/capture/release-sequence.d.ts.map +1 -0
- package/dist/capture/release-sequence.js +446 -0
- package/dist/capture/release-sequence.js.map +1 -0
- package/dist/capture/sensor-raw-frame.d.ts +34 -0
- package/dist/capture/sensor-raw-frame.d.ts.map +1 -0
- package/dist/capture/sensor-raw-frame.js +106 -0
- package/dist/capture/sensor-raw-frame.js.map +1 -0
- package/dist/capture/sensor-raw-producer.d.ts +51 -0
- package/dist/capture/sensor-raw-producer.d.ts.map +1 -0
- package/dist/capture/sensor-raw-producer.js +235 -0
- package/dist/capture/sensor-raw-producer.js.map +1 -0
- package/dist/capture/simulated-capture.d.ts +125 -0
- package/dist/capture/simulated-capture.d.ts.map +1 -0
- package/dist/capture/simulated-capture.js +248 -0
- package/dist/capture/simulated-capture.js.map +1 -0
- package/dist/color/capture-color.d.ts +73 -0
- package/dist/color/capture-color.d.ts.map +1 -0
- package/dist/color/capture-color.js +145 -0
- package/dist/color/capture-color.js.map +1 -0
- package/dist/color/sensor-color-development.d.ts +23 -0
- package/dist/color/sensor-color-development.d.ts.map +1 -0
- package/dist/color/sensor-color-development.js +56 -0
- package/dist/color/sensor-color-development.js.map +1 -0
- package/dist/color/white-balance.d.ts +175 -0
- package/dist/color/white-balance.d.ts.map +1 -0
- package/dist/color/white-balance.js +606 -0
- package/dist/color/white-balance.js.map +1 -0
- package/dist/composition/image-formation-plan.d.ts +481 -0
- package/dist/composition/image-formation-plan.d.ts.map +1 -0
- package/dist/composition/image-formation-plan.js +2499 -0
- package/dist/composition/image-formation-plan.js.map +1 -0
- package/dist/composition/plan-consumer.d.ts +53 -0
- package/dist/composition/plan-consumer.d.ts.map +1 -0
- package/dist/composition/plan-consumer.js +119 -0
- package/dist/composition/plan-consumer.js.map +1 -0
- package/dist/core/calculation-result.d.ts.map +1 -1
- package/dist/core/calculation-result.js +6 -0
- package/dist/core/calculation-result.js.map +1 -1
- package/dist/core/canonical-json.d.ts +18 -0
- package/dist/core/canonical-json.d.ts.map +1 -0
- package/dist/core/canonical-json.js +44 -0
- package/dist/core/canonical-json.js.map +1 -0
- package/dist/core/configuration-error.d.ts +6 -0
- package/dist/core/configuration-error.d.ts.map +1 -1
- package/dist/core/configuration-error.js +6 -0
- package/dist/core/configuration-error.js.map +1 -1
- package/dist/core/evidence-provenance.d.ts +6 -0
- package/dist/core/evidence-provenance.d.ts.map +1 -1
- package/dist/core/evidence-provenance.js +7 -0
- package/dist/core/evidence-provenance.js.map +1 -1
- package/dist/core/image-formation.d.ts +9 -3
- package/dist/core/image-formation.d.ts.map +1 -1
- package/dist/core/image-formation.js +29 -5
- package/dist/core/image-formation.js.map +1 -1
- package/dist/core/owned-data.d.ts +25 -0
- package/dist/core/owned-data.d.ts.map +1 -0
- package/dist/core/owned-data.js +34 -0
- package/dist/core/owned-data.js.map +1 -0
- package/dist/core/record-validation.d.ts +17 -0
- package/dist/core/record-validation.d.ts.map +1 -0
- package/dist/core/record-validation.js +44 -0
- package/dist/core/record-validation.js.map +1 -0
- package/dist/core/scientific-assurance.d.ts +101 -0
- package/dist/core/scientific-assurance.d.ts.map +1 -0
- package/dist/core/scientific-assurance.js +301 -0
- package/dist/core/scientific-assurance.js.map +1 -0
- package/dist/core/spectral-composition.d.ts +106 -0
- package/dist/core/spectral-composition.d.ts.map +1 -0
- package/dist/core/spectral-composition.js +334 -0
- package/dist/core/spectral-composition.js.map +1 -0
- package/dist/core/spectral.d.ts +34 -0
- package/dist/core/spectral.d.ts.map +1 -0
- package/dist/core/spectral.js +57 -0
- package/dist/core/spectral.js.map +1 -0
- package/dist/core/validation.d.ts +6 -0
- package/dist/core/validation.d.ts.map +1 -1
- package/dist/core/validation.js +6 -0
- package/dist/core/validation.js.map +1 -1
- package/dist/core/version.d.ts +14 -0
- package/dist/core/version.d.ts.map +1 -0
- package/dist/core/version.js +15 -0
- package/dist/core/version.js.map +1 -0
- package/dist/equipment/exposure-capabilities.d.ts +125 -0
- package/dist/equipment/exposure-capabilities.d.ts.map +1 -0
- package/dist/equipment/exposure-capabilities.js +391 -0
- package/dist/equipment/exposure-capabilities.js.map +1 -0
- package/dist/equipment/generic-tier-assets.d.ts +82 -0
- package/dist/equipment/generic-tier-assets.d.ts.map +1 -0
- package/dist/equipment/generic-tier-assets.js +119 -0
- package/dist/equipment/generic-tier-assets.js.map +1 -0
- package/dist/equipment/generic-tier-presets.d.ts +57 -0
- package/dist/equipment/generic-tier-presets.d.ts.map +1 -0
- package/dist/equipment/generic-tier-presets.js +122 -0
- package/dist/equipment/generic-tier-presets.js.map +1 -0
- package/dist/equipment/iso-capabilities.d.ts +143 -0
- package/dist/equipment/iso-capabilities.d.ts.map +1 -0
- package/dist/equipment/iso-capabilities.js +578 -0
- package/dist/equipment/iso-capabilities.js.map +1 -0
- package/dist/equipment/metering-capabilities.d.ts +58 -0
- package/dist/equipment/metering-capabilities.d.ts.map +1 -0
- package/dist/equipment/metering-capabilities.js +151 -0
- package/dist/equipment/metering-capabilities.js.map +1 -0
- package/dist/equipment/release-capabilities.d.ts +33 -0
- package/dist/equipment/release-capabilities.d.ts.map +1 -0
- package/dist/equipment/release-capabilities.js +176 -0
- package/dist/equipment/release-capabilities.js.map +1 -0
- package/dist/exposure/exposure-duration-control.d.ts +72 -0
- package/dist/exposure/exposure-duration-control.d.ts.map +1 -0
- package/dist/exposure/exposure-duration-control.js +161 -0
- package/dist/exposure/exposure-duration-control.js.map +1 -0
- package/dist/exposure/exposure-mode-resolver.d.ts +637 -0
- package/dist/exposure/exposure-mode-resolver.d.ts.map +1 -0
- package/dist/exposure/exposure-mode-resolver.js +1905 -0
- package/dist/exposure/exposure-mode-resolver.js.map +1 -0
- package/dist/exposure/exposure.d.ts +8 -0
- package/dist/exposure/exposure.d.ts.map +1 -1
- package/dist/exposure/exposure.js +8 -0
- package/dist/exposure/exposure.js.map +1 -1
- package/dist/exposure/flash.d.ts +179 -0
- package/dist/exposure/flash.d.ts.map +1 -0
- package/dist/exposure/flash.js +644 -0
- package/dist/exposure/flash.js.map +1 -0
- package/dist/exposure/metering-scene-radiance.d.ts +72 -0
- package/dist/exposure/metering-scene-radiance.d.ts.map +1 -0
- package/dist/exposure/metering-scene-radiance.js +222 -0
- package/dist/exposure/metering-scene-radiance.js.map +1 -0
- package/dist/exposure/metering-target.d.ts +67 -0
- package/dist/exposure/metering-target.d.ts.map +1 -0
- package/dist/exposure/metering-target.js +265 -0
- package/dist/exposure/metering-target.js.map +1 -0
- package/dist/exposure/metering-temporal.d.ts +76 -0
- package/dist/exposure/metering-temporal.d.ts.map +1 -0
- package/dist/exposure/metering-temporal.js +210 -0
- package/dist/exposure/metering-temporal.js.map +1 -0
- package/dist/exposure/metering.d.ts +124 -0
- package/dist/exposure/metering.d.ts.map +1 -0
- package/dist/exposure/metering.js +422 -0
- package/dist/exposure/metering.js.map +1 -0
- package/dist/exposure/program-line.d.ts +29 -0
- package/dist/exposure/program-line.d.ts.map +1 -0
- package/dist/exposure/program-line.js +126 -0
- package/dist/exposure/program-line.js.map +1 -0
- package/dist/index.d.ts +101 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +99 -7
- package/dist/index.js.map +1 -1
- package/dist/motion/camera-rotation.d.ts +76 -0
- package/dist/motion/camera-rotation.d.ts.map +1 -1
- package/dist/motion/camera-rotation.js +111 -0
- package/dist/motion/camera-rotation.js.map +1 -1
- package/dist/motion/capture-rotation-inverse-mapping.d.ts +113 -0
- package/dist/motion/capture-rotation-inverse-mapping.d.ts.map +1 -0
- package/dist/motion/capture-rotation-inverse-mapping.js +184 -0
- package/dist/motion/capture-rotation-inverse-mapping.js.map +1 -0
- package/dist/motion/capture-rotation-temporal-quadrature.d.ts +104 -0
- package/dist/motion/capture-rotation-temporal-quadrature.d.ts.map +1 -0
- package/dist/motion/capture-rotation-temporal-quadrature.js +131 -0
- package/dist/motion/capture-rotation-temporal-quadrature.js.map +1 -0
- package/dist/motion/capture-rotation-trajectory.d.ts +113 -0
- package/dist/motion/capture-rotation-trajectory.d.ts.map +1 -0
- package/dist/motion/capture-rotation-trajectory.js +168 -0
- package/dist/motion/capture-rotation-trajectory.js.map +1 -0
- package/dist/motion/capture-translation-parallax-temporal-quadrature.d.ts +119 -0
- package/dist/motion/capture-translation-parallax-temporal-quadrature.d.ts.map +1 -0
- package/dist/motion/capture-translation-parallax-temporal-quadrature.js +263 -0
- package/dist/motion/capture-translation-parallax-temporal-quadrature.js.map +1 -0
- package/dist/motion/extended-object-projection.d.ts +175 -0
- package/dist/motion/extended-object-projection.d.ts.map +1 -0
- package/dist/motion/extended-object-projection.js +402 -0
- package/dist/motion/extended-object-projection.js.map +1 -0
- package/dist/motion/projected-motion.d.ts +11 -0
- package/dist/motion/projected-motion.d.ts.map +1 -1
- package/dist/motion/projected-motion.js +11 -0
- package/dist/motion/projected-motion.js.map +1 -1
- package/dist/optics/aperture.d.ts +9 -0
- package/dist/optics/aperture.d.ts.map +1 -1
- package/dist/optics/aperture.js +9 -0
- package/dist/optics/aperture.js.map +1 -1
- package/dist/optics/circle-of-confusion.d.ts +8 -0
- package/dist/optics/circle-of-confusion.d.ts.map +1 -1
- package/dist/optics/circle-of-confusion.js +8 -0
- package/dist/optics/circle-of-confusion.js.map +1 -1
- package/dist/optics/complex-pupil-psf.d.ts +128 -0
- package/dist/optics/complex-pupil-psf.d.ts.map +1 -0
- package/dist/optics/complex-pupil-psf.js +462 -0
- package/dist/optics/complex-pupil-psf.js.map +1 -0
- package/dist/optics/depth-of-field.d.ts +18 -0
- package/dist/optics/depth-of-field.d.ts.map +1 -1
- package/dist/optics/depth-of-field.js +29 -4
- package/dist/optics/depth-of-field.js.map +1 -1
- package/dist/optics/diffraction.d.ts +8 -0
- package/dist/optics/diffraction.d.ts.map +1 -1
- package/dist/optics/diffraction.js +8 -0
- package/dist/optics/diffraction.js.map +1 -1
- package/dist/optics/focus-breathing.d.ts +13 -0
- package/dist/optics/focus-breathing.d.ts.map +1 -1
- package/dist/optics/focus-breathing.js +13 -0
- package/dist/optics/focus-breathing.js.map +1 -1
- package/dist/optics/focus-state.d.ts +47 -0
- package/dist/optics/focus-state.d.ts.map +1 -0
- package/dist/optics/focus-state.js +78 -0
- package/dist/optics/focus-state.js.map +1 -0
- package/dist/optics/front-of-lens-filter.d.ts +114 -0
- package/dist/optics/front-of-lens-filter.d.ts.map +1 -0
- package/dist/optics/front-of-lens-filter.js +468 -0
- package/dist/optics/front-of-lens-filter.js.map +1 -0
- package/dist/optics/illumination-vignetting.d.ts +9 -0
- package/dist/optics/illumination-vignetting.d.ts.map +1 -1
- package/dist/optics/illumination-vignetting.js +9 -0
- package/dist/optics/illumination-vignetting.js.map +1 -1
- package/dist/optics/lateral-chromatic-aberration.d.ts +11 -0
- package/dist/optics/lateral-chromatic-aberration.d.ts.map +1 -1
- package/dist/optics/lateral-chromatic-aberration.js +23 -4
- package/dist/optics/lateral-chromatic-aberration.js.map +1 -1
- package/dist/optics/lens-psf-profile.d.ts +217 -0
- package/dist/optics/lens-psf-profile.d.ts.map +1 -0
- package/dist/optics/lens-psf-profile.js +823 -0
- package/dist/optics/lens-psf-profile.js.map +1 -0
- package/dist/optics/polygon-diffraction.d.ts +67 -0
- package/dist/optics/polygon-diffraction.d.ts.map +1 -0
- package/dist/optics/polygon-diffraction.js +145 -0
- package/dist/optics/polygon-diffraction.js.map +1 -0
- package/dist/optics/profile-contract.d.ts +42 -0
- package/dist/optics/profile-contract.d.ts.map +1 -0
- package/dist/optics/profile-contract.js +92 -0
- package/dist/optics/profile-contract.js.map +1 -0
- package/dist/optics/psf-foundation.d.ts +16 -3
- package/dist/optics/psf-foundation.d.ts.map +1 -1
- package/dist/optics/psf-foundation.js +28 -15
- package/dist/optics/psf-foundation.js.map +1 -1
- package/dist/optics/radial-distortion.d.ts +22 -2
- package/dist/optics/radial-distortion.d.ts.map +1 -1
- package/dist/optics/radial-distortion.js +101 -23
- package/dist/optics/radial-distortion.js.map +1 -1
- package/dist/optics/scene-to-sensor-irradiance.d.ts +213 -0
- package/dist/optics/scene-to-sensor-irradiance.d.ts.map +1 -0
- package/dist/optics/scene-to-sensor-irradiance.js +662 -0
- package/dist/optics/scene-to-sensor-irradiance.js.map +1 -0
- package/dist/optics/scene-to-sensor-quadrature.d.ts +63 -0
- package/dist/optics/scene-to-sensor-quadrature.d.ts.map +1 -0
- package/dist/optics/scene-to-sensor-quadrature.js +82 -0
- package/dist/optics/scene-to-sensor-quadrature.js.map +1 -0
- package/dist/optics/sensor-environment-query.d.ts +60 -0
- package/dist/optics/sensor-environment-query.d.ts.map +1 -0
- package/dist/optics/sensor-environment-query.js +50 -0
- package/dist/optics/sensor-environment-query.js.map +1 -0
- package/dist/optics/sensor-psf-quadrature.d.ts +70 -0
- package/dist/optics/sensor-psf-quadrature.d.ts.map +1 -0
- package/dist/optics/sensor-psf-quadrature.js +101 -0
- package/dist/optics/sensor-psf-quadrature.js.map +1 -0
- package/dist/optics/sensor-psf-support.d.ts +20 -0
- package/dist/optics/sensor-psf-support.d.ts.map +1 -0
- package/dist/optics/sensor-psf-support.js +13 -0
- package/dist/optics/sensor-psf-support.js.map +1 -0
- package/dist/optics/stray-light.d.ts +91 -0
- package/dist/optics/stray-light.d.ts.map +1 -0
- package/dist/optics/stray-light.js +110 -0
- package/dist/optics/stray-light.js.map +1 -0
- package/dist/optics/thin-lens.d.ts +8 -0
- package/dist/optics/thin-lens.d.ts.map +1 -1
- package/dist/optics/thin-lens.js +8 -0
- package/dist/optics/thin-lens.js.map +1 -1
- package/dist/output/capture-corrected-sdr.d.ts +49 -0
- package/dist/output/capture-corrected-sdr.d.ts.map +1 -0
- package/dist/output/capture-corrected-sdr.js +149 -0
- package/dist/output/capture-corrected-sdr.js.map +1 -0
- package/dist/output/capture-geometry.d.ts.map +1 -1
- package/dist/output/capture-geometry.js +32 -11
- package/dist/output/capture-geometry.js.map +1 -1
- package/dist/output/capture-sdr.d.ts +49 -0
- package/dist/output/capture-sdr.d.ts.map +1 -0
- package/dist/output/capture-sdr.js +90 -0
- package/dist/output/capture-sdr.js.map +1 -0
- package/dist/output/crop.d.ts +6 -0
- package/dist/output/crop.d.ts.map +1 -1
- package/dist/output/crop.js +6 -0
- package/dist/output/crop.js.map +1 -1
- package/dist/output/geometric-transforms.d.ts +137 -0
- package/dist/output/geometric-transforms.d.ts.map +1 -0
- package/dist/output/geometric-transforms.js +225 -0
- package/dist/output/geometric-transforms.js.map +1 -0
- package/dist/output/lens-corrections.d.ts +121 -0
- package/dist/output/lens-corrections.d.ts.map +1 -0
- package/dist/output/lens-corrections.js +210 -0
- package/dist/output/lens-corrections.js.map +1 -0
- package/dist/output/sdr-rendering.d.ts +81 -0
- package/dist/output/sdr-rendering.d.ts.map +1 -0
- package/dist/output/sdr-rendering.js +106 -0
- package/dist/output/sdr-rendering.js.map +1 -0
- package/dist/output/subject-framing-crop.d.ts +8 -0
- package/dist/output/subject-framing-crop.d.ts.map +1 -1
- package/dist/output/subject-framing-crop.js +8 -0
- package/dist/output/subject-framing-crop.js.map +1 -1
- package/dist/schema/camera.d.ts +6 -0
- package/dist/schema/camera.d.ts.map +1 -1
- package/dist/schema/illumination-spectral.d.ts +51 -0
- package/dist/schema/illumination-spectral.d.ts.map +1 -0
- package/dist/schema/illumination-spectral.js +132 -0
- package/dist/schema/illumination-spectral.js.map +1 -0
- package/dist/schema/illumination-temporal.d.ts +156 -0
- package/dist/schema/illumination-temporal.d.ts.map +1 -0
- package/dist/schema/illumination-temporal.js +554 -0
- package/dist/schema/illumination-temporal.js.map +1 -0
- package/dist/schema/illumination.d.ts +138 -0
- package/dist/schema/illumination.d.ts.map +1 -0
- package/dist/schema/illumination.js +483 -0
- package/dist/schema/illumination.js.map +1 -0
- package/dist/schema/scene-radiance-temporal.d.ts +107 -0
- package/dist/schema/scene-radiance-temporal.d.ts.map +1 -0
- package/dist/schema/scene-radiance-temporal.js +259 -0
- package/dist/schema/scene-radiance-temporal.js.map +1 -0
- package/dist/schema/scene-radiance.d.ts +185 -0
- package/dist/schema/scene-radiance.d.ts.map +1 -0
- package/dist/schema/scene-radiance.js +535 -0
- package/dist/schema/scene-radiance.js.map +1 -0
- package/dist/schema/scene.d.ts +7 -0
- package/dist/schema/scene.d.ts.map +1 -1
- package/dist/schema/validation.d.ts.map +1 -1
- package/dist/schema/validation.js +7 -0
- package/dist/schema/validation.js.map +1 -1
- package/dist/sensor/accumulated-charge.d.ts +199 -0
- package/dist/sensor/accumulated-charge.d.ts.map +1 -0
- package/dist/sensor/accumulated-charge.js +450 -0
- package/dist/sensor/accumulated-charge.js.map +1 -0
- package/dist/sensor/architecture.d.ts +48 -13
- package/dist/sensor/architecture.d.ts.map +1 -1
- package/dist/sensor/architecture.js +58 -16
- package/dist/sensor/architecture.js.map +1 -1
- package/dist/sensor/camera-saturation-capacity.d.ts +138 -0
- package/dist/sensor/camera-saturation-capacity.d.ts.map +1 -0
- package/dist/sensor/camera-saturation-capacity.js +356 -0
- package/dist/sensor/camera-saturation-capacity.js.map +1 -0
- package/dist/sensor/capture-color-sampling-binding.d.ts +212 -0
- package/dist/sensor/capture-color-sampling-binding.d.ts.map +1 -0
- package/dist/sensor/capture-color-sampling-binding.js +437 -0
- package/dist/sensor/capture-color-sampling-binding.js.map +1 -0
- package/dist/sensor/capture-mode-timing.d.ts +63 -0
- package/dist/sensor/capture-mode-timing.d.ts.map +1 -0
- package/dist/sensor/capture-mode-timing.js +270 -0
- package/dist/sensor/capture-mode-timing.js.map +1 -0
- package/dist/sensor/capture-mode.d.ts +157 -0
- package/dist/sensor/capture-mode.d.ts.map +1 -0
- package/dist/sensor/capture-mode.js +447 -0
- package/dist/sensor/capture-mode.js.map +1 -0
- package/dist/sensor/color-sampling.d.ts +133 -0
- package/dist/sensor/color-sampling.d.ts.map +1 -0
- package/dist/sensor/color-sampling.js +245 -0
- package/dist/sensor/color-sampling.js.map +1 -0
- package/dist/sensor/constant-rate-temporal-integration.d.ts +126 -0
- package/dist/sensor/constant-rate-temporal-integration.d.ts.map +1 -0
- package/dist/sensor/constant-rate-temporal-integration.js +382 -0
- package/dist/sensor/constant-rate-temporal-integration.js.map +1 -0
- package/dist/sensor/dark-current.d.ts +169 -0
- package/dist/sensor/dark-current.d.ts.map +1 -0
- package/dist/sensor/dark-current.js +427 -0
- package/dist/sensor/dark-current.js.map +1 -0
- package/dist/sensor/environment-photo-signal.d.ts +123 -0
- package/dist/sensor/environment-photo-signal.d.ts.map +1 -0
- package/dist/sensor/environment-photo-signal.js +186 -0
- package/dist/sensor/environment-photo-signal.js.map +1 -0
- package/dist/sensor/eqe-electron-rate.d.ts +107 -0
- package/dist/sensor/eqe-electron-rate.d.ts.map +1 -0
- package/dist/sensor/eqe-electron-rate.js +561 -0
- package/dist/sensor/eqe-electron-rate.js.map +1 -0
- package/dist/sensor/eqe-local-exposure.d.ts +65 -0
- package/dist/sensor/eqe-local-exposure.d.ts.map +1 -0
- package/dist/sensor/eqe-local-exposure.js +84 -0
- package/dist/sensor/eqe-local-exposure.js.map +1 -0
- package/dist/sensor/eqe-temporal-exposure.d.ts +55 -0
- package/dist/sensor/eqe-temporal-exposure.d.ts.map +1 -0
- package/dist/sensor/eqe-temporal-exposure.js +62 -0
- package/dist/sensor/eqe-temporal-exposure.js.map +1 -0
- package/dist/sensor/exposure-window.d.ts +117 -0
- package/dist/sensor/exposure-window.d.ts.map +1 -0
- package/dist/sensor/exposure-window.js +305 -0
- package/dist/sensor/exposure-window.js.map +1 -0
- package/dist/sensor/iso-signal-chain.d.ts +148 -0
- package/dist/sensor/iso-signal-chain.d.ts.map +1 -0
- package/dist/sensor/iso-signal-chain.js +411 -0
- package/dist/sensor/iso-signal-chain.js.map +1 -0
- package/dist/sensor/local-exposure-binding.d.ts +87 -0
- package/dist/sensor/local-exposure-binding.d.ts.map +1 -0
- package/dist/sensor/local-exposure-binding.js +214 -0
- package/dist/sensor/local-exposure-binding.js.map +1 -0
- package/dist/sensor/optical-stack.d.ts +132 -0
- package/dist/sensor/optical-stack.d.ts.map +1 -0
- package/dist/sensor/optical-stack.js +303 -0
- package/dist/sensor/optical-stack.js.map +1 -0
- package/dist/sensor/photon-energy.d.ts +97 -0
- package/dist/sensor/photon-energy.d.ts.map +1 -0
- package/dist/sensor/photon-energy.js +390 -0
- package/dist/sensor/photon-energy.js.map +1 -0
- package/dist/sensor/physical-charge-capacity.d.ts +130 -0
- package/dist/sensor/physical-charge-capacity.d.ts.map +1 -0
- package/dist/sensor/physical-charge-capacity.js +345 -0
- package/dist/sensor/physical-charge-capacity.js.map +1 -0
- package/dist/sensor/pixel-pitch.d.ts +6 -0
- package/dist/sensor/pixel-pitch.d.ts.map +1 -1
- package/dist/sensor/pixel-pitch.js +6 -0
- package/dist/sensor/pixel-pitch.js.map +1 -1
- package/dist/sensor/radiometry-readiness.d.ts +10 -0
- package/dist/sensor/radiometry-readiness.d.ts.map +1 -1
- package/dist/sensor/radiometry-readiness.js +10 -0
- package/dist/sensor/radiometry-readiness.js.map +1 -1
- package/dist/sensor/raw-readout.d.ts +314 -0
- package/dist/sensor/raw-readout.d.ts.map +1 -0
- package/dist/sensor/raw-readout.js +914 -0
- package/dist/sensor/raw-readout.js.map +1 -0
- package/dist/sensor/raw-reconstruction.d.ts +170 -0
- package/dist/sensor/raw-reconstruction.d.ts.map +1 -0
- package/dist/sensor/raw-reconstruction.js +424 -0
- package/dist/sensor/raw-reconstruction.js.map +1 -0
- package/dist/sensor/readout-exposure-linkage.d.ts +123 -0
- package/dist/sensor/readout-exposure-linkage.d.ts.map +1 -0
- package/dist/sensor/readout-exposure-linkage.js +222 -0
- package/dist/sensor/readout-exposure-linkage.js.map +1 -0
- package/dist/sensor/readout-timing.d.ts +137 -0
- package/dist/sensor/readout-timing.d.ts.map +1 -0
- package/dist/sensor/readout-timing.js +243 -0
- package/dist/sensor/readout-timing.js.map +1 -0
- package/dist/sensor/response-application-compatibility.d.ts +136 -0
- package/dist/sensor/response-application-compatibility.d.ts.map +1 -0
- package/dist/sensor/response-application-compatibility.js +408 -0
- package/dist/sensor/response-application-compatibility.js.map +1 -0
- package/dist/sensor/response-operating-range.d.ts +173 -0
- package/dist/sensor/response-operating-range.d.ts.map +1 -0
- package/dist/sensor/response-operating-range.js +672 -0
- package/dist/sensor/response-operating-range.js.map +1 -0
- package/dist/sensor/responsivity-photocurrent.d.ts +158 -0
- package/dist/sensor/responsivity-photocurrent.d.ts.map +1 -0
- package/dist/sensor/responsivity-photocurrent.js +644 -0
- package/dist/sensor/responsivity-photocurrent.js.map +1 -0
- package/dist/sensor/sampling-aperture.d.ts +154 -0
- package/dist/sensor/sampling-aperture.d.ts.map +1 -0
- package/dist/sensor/sampling-aperture.js +388 -0
- package/dist/sensor/sampling-aperture.js.map +1 -0
- package/dist/sensor/scene-eqe-temporal-exposure.d.ts +45 -0
- package/dist/sensor/scene-eqe-temporal-exposure.d.ts.map +1 -0
- package/dist/sensor/scene-eqe-temporal-exposure.js +64 -0
- package/dist/sensor/scene-eqe-temporal-exposure.js.map +1 -0
- package/dist/sensor/sensor-geometry.d.ts +12 -0
- package/dist/sensor/sensor-geometry.d.ts.map +1 -1
- package/dist/sensor/sensor-geometry.js +12 -0
- package/dist/sensor/sensor-geometry.js.map +1 -1
- package/dist/sensor/signal-noise.d.ts +10 -0
- package/dist/sensor/signal-noise.d.ts.map +1 -1
- package/dist/sensor/signal-noise.js +10 -0
- package/dist/sensor/signal-noise.js.map +1 -1
- package/dist/sensor/spatial-sample-reduction.d.ts +171 -0
- package/dist/sensor/spatial-sample-reduction.d.ts.map +1 -0
- package/dist/sensor/spatial-sample-reduction.js +345 -0
- package/dist/sensor/spatial-sample-reduction.js.map +1 -0
- package/dist/sensor/spatial-sampling-quadrature.d.ts +179 -0
- package/dist/sensor/spatial-sampling-quadrature.d.ts.map +1 -0
- package/dist/sensor/spatial-sampling-quadrature.js +224 -0
- package/dist/sensor/spatial-sampling-quadrature.js.map +1 -0
- package/dist/sensor/spatio-spectral-reduction.d.ts +187 -0
- package/dist/sensor/spatio-spectral-reduction.d.ts.map +1 -0
- package/dist/sensor/spatio-spectral-reduction.js +373 -0
- package/dist/sensor/spatio-spectral-reduction.js.map +1 -0
- package/dist/sensor/spectral-coverage.d.ts +24 -0
- package/dist/sensor/spectral-coverage.d.ts.map +1 -0
- package/dist/sensor/spectral-coverage.js +86 -0
- package/dist/sensor/spectral-coverage.js.map +1 -0
- package/dist/sensor/spectral-quadrature.d.ts +169 -0
- package/dist/sensor/spectral-quadrature.d.ts.map +1 -0
- package/dist/sensor/spectral-quadrature.js +542 -0
- package/dist/sensor/spectral-quadrature.js.map +1 -0
- package/dist/sensor/spectral-response.d.ts +192 -0
- package/dist/sensor/spectral-response.d.ts.map +1 -0
- package/dist/sensor/spectral-response.js +664 -0
- package/dist/sensor/spectral-response.js.map +1 -0
- package/dist/sensor/temporal-eqe-sum.d.ts +15 -0
- package/dist/sensor/temporal-eqe-sum.d.ts.map +1 -0
- package/dist/sensor/temporal-eqe-sum.js +30 -0
- package/dist/sensor/temporal-eqe-sum.js.map +1 -0
- package/dist/sensor/temporal-photo-signal.d.ts +38 -0
- package/dist/sensor/temporal-photo-signal.d.ts.map +1 -0
- package/dist/sensor/temporal-photo-signal.js +110 -0
- package/dist/sensor/temporal-photo-signal.js.map +1 -0
- package/dist/sensor/temporal-sample-binding.d.ts +6 -0
- package/dist/sensor/temporal-sample-binding.d.ts.map +1 -0
- package/dist/sensor/temporal-sample-binding.js +32 -0
- package/dist/sensor/temporal-sample-binding.js.map +1 -0
- package/dist/simulation/poc-simulation.d.ts.map +1 -1
- package/dist/simulation/poc-simulation.js +34 -11
- package/dist/simulation/poc-simulation.js.map +1 -1
- package/dist/stabilization/camera-shake.d.ts +11 -0
- package/dist/stabilization/camera-shake.d.ts.map +1 -1
- package/dist/stabilization/camera-shake.js +11 -0
- package/dist/stabilization/camera-shake.js.map +1 -1
- package/dist/stabilization/system.d.ts +218 -0
- package/dist/stabilization/system.d.ts.map +1 -0
- package/dist/stabilization/system.js +695 -0
- package/dist/stabilization/system.js.map +1 -0
- package/docs/API_REFERENCE.md +2017 -0
- package/docs/API_STYLE.md +132 -0
- package/docs/ARCHITECTURE.md +150 -23
- package/docs/AUTOMATION_COORDINATION.md +190 -0
- package/docs/CAPTURE_COLOR.md +25 -0
- package/docs/CAPTURE_CORRECTED_SDR.md +25 -0
- package/docs/CAPTURE_EXPORT_METADATA.md +27 -0
- package/docs/CAPTURE_SDR.md +20 -0
- package/docs/DEVELOPERS.md +60 -0
- package/docs/DIGITAL_OPTICS_FOUNDATION.md +69 -0
- package/docs/ENVIRONMENT_RAW_CAPTURE.md +187 -0
- package/docs/EXPORT_ACCEPTANCE.md +133 -0
- package/docs/GENERIC_TIER_PRESETS.md +269 -0
- package/docs/GEOMETRIC_PREPARATION_REUSE.md +141 -0
- package/docs/IMAGE_FORMATION.md +105 -9
- package/docs/LINEAR_CAPTURE_ENCODING.md +25 -0
- package/docs/MOTION_AND_SIGNAL.md +145 -1
- package/docs/NUMERICAL_CORRECTNESS.md +191 -0
- package/docs/PERFORMANCE_BASELINE.md +56 -0
- package/docs/PHOTOGRAPHIC_EXPORT.md +206 -0
- package/docs/PHYSICS_FOUNDATION.md +152 -5
- package/docs/POC_API.md +2 -0
- package/docs/POC_PROJECTION_REUSE.md +124 -0
- package/docs/PROCESSED_OUTPUT.md +157 -0
- package/docs/PRODUCTION_COMPOSITION.md +373 -0
- package/docs/PRODUCTION_ENVIRONMENT_CAPTURE.md +131 -0
- package/docs/PROVENANCE.md +4 -0
- package/docs/PSF_FOUNDATION.md +110 -88
- package/docs/RAW_FRAME_ENVELOPE.md +56 -0
- package/docs/RAW_FRAME_RECONSTRUCTION.md +30 -0
- package/docs/RELEASE_1_0.md +65 -0
- package/docs/RELEASE_AUDIT.md +38 -0
- package/docs/SCENE_RADIANCE_AND_ILLUMINATION.md +298 -0
- package/docs/SCENE_SENSOR_QUADRATURE.md +98 -0
- package/docs/SCENE_SENSOR_TEMPORAL_EXPOSURE.md +86 -0
- package/docs/SCIENTIFIC_ASSURANCE.md +280 -0
- package/docs/SCIENTIFIC_CONFORMANCE.md +83 -0
- package/docs/SDR_RENDERING.md +33 -0
- package/docs/SENSOR_ENVIRONMENT_QUERY.md +86 -0
- package/docs/SENSOR_EQE_LOCAL_EXPOSURE.md +104 -0
- package/docs/SENSOR_EQE_TEMPORAL_EXPOSURE.md +74 -0
- package/docs/SENSOR_PSF_QUADRATURE.md +90 -0
- package/docs/SENSOR_RAW_FRAME.md +20 -0
- package/docs/SENSOR_RAW_PRODUCER.md +77 -0
- package/docs/SIGNAL_CONFORMANCE.md +23 -0
- package/docs/SIMULATED_CAPTURE.md +65 -0
- package/docs/STABILIZATION.md +160 -17
- package/docs/TEMPORAL_PHOTO_RAW.md +83 -0
- package/docs/TIER_ACCEPTANCE.md +129 -0
- package/docs/TIER_PRESET_ACCEPTANCE.md +59 -0
- package/docs/USAGE.md +5604 -331
- package/docs/V1_COMPOSITION_MAP.md +123 -0
- package/docs/V1_HELPER_REUSE_REVIEW.md +60 -0
- package/docs/V1_PERFORMANCE_DISPOSITION.md +88 -0
- package/docs/api/camera-equivalent-focal-length.md +53 -0
- package/docs/api/camera-field-of-view.md +119 -0
- package/docs/api/camera-projected-object-size.md +54 -0
- package/docs/api/capture-capture-export-metadata.md +107 -0
- package/docs/api/capture-environment-raw-producer.md +56 -0
- package/docs/api/capture-focus-control.md +419 -0
- package/docs/api/capture-linear-encoding.md +99 -0
- package/docs/api/capture-photographic-export.md +172 -0
- package/docs/api/capture-raw-frame-reconstruction.md +87 -0
- package/docs/api/capture-release-sequence.md +252 -0
- package/docs/api/capture-sensor-raw-frame.md +66 -0
- package/docs/api/capture-sensor-raw-producer.md +98 -0
- package/docs/api/capture-simulated-capture.md +179 -0
- package/docs/api/color-capture-color.md +122 -0
- package/docs/api/color-white-balance.md +386 -0
- package/docs/api/composition-image-formation-plan.md +825 -0
- package/docs/api/composition-plan-consumer.md +98 -0
- package/docs/api/core-calculation-result.md +220 -0
- package/docs/api/core-configuration-error.md +19 -0
- package/docs/api/core-image-formation.md +204 -0
- package/docs/api/core-scientific-assurance.md +214 -0
- package/docs/api/core-spectral-composition.md +268 -0
- package/docs/api/core-spectral.md +78 -0
- package/docs/api/core-validation.md +34 -0
- package/docs/api/core-version.md +14 -0
- package/docs/api/equipment-exposure-capabilities.md +270 -0
- package/docs/api/equipment-generic-tier-assets.md +82 -0
- package/docs/api/equipment-generic-tier-presets.md +103 -0
- package/docs/api/equipment-iso-capabilities.md +295 -0
- package/docs/api/equipment-metering-capabilities.md +126 -0
- package/docs/api/equipment-release-capabilities.md +91 -0
- package/docs/api/exports.json +5656 -0
- package/docs/api/exposure-exposure-duration-control.md +115 -0
- package/docs/api/exposure-exposure-mode-resolver.md +966 -0
- package/docs/api/exposure-exposure.md +150 -0
- package/docs/api/exposure-flash.md +327 -0
- package/docs/api/exposure-metering-scene-radiance.md +154 -0
- package/docs/api/exposure-metering-target.md +134 -0
- package/docs/api/exposure-metering-temporal.md +77 -0
- package/docs/api/exposure-metering.md +172 -0
- package/docs/api/exposure-program-line.md +64 -0
- package/docs/api/motion-camera-rotation.md +215 -0
- package/docs/api/motion-capture-rotation-inverse-mapping.md +128 -0
- package/docs/api/motion-capture-rotation-temporal-quadrature.md +127 -0
- package/docs/api/motion-capture-rotation-trajectory.md +132 -0
- package/docs/api/motion-capture-translation-parallax-temporal-quadrature.md +171 -0
- package/docs/api/motion-extended-object-projection.md +312 -0
- package/docs/api/motion-projected-motion.md +60 -0
- package/docs/api/optics-aperture.md +69 -0
- package/docs/api/optics-circle-of-confusion.md +46 -0
- package/docs/api/optics-complex-pupil-psf.md +193 -0
- package/docs/api/optics-depth-of-field.md +92 -0
- package/docs/api/optics-diffraction.md +46 -0
- package/docs/api/optics-focus-breathing.md +119 -0
- package/docs/api/optics-focus-state.md +64 -0
- package/docs/api/optics-front-of-lens-filter.md +263 -0
- package/docs/api/optics-illumination-vignetting.md +95 -0
- package/docs/api/optics-lateral-chromatic-aberration.md +225 -0
- package/docs/api/optics-lens-psf-profile.md +380 -0
- package/docs/api/optics-polygon-diffraction.md +68 -0
- package/docs/api/optics-profile-contract.md +47 -0
- package/docs/api/optics-psf-foundation.md +181 -0
- package/docs/api/optics-radial-distortion.md +194 -0
- package/docs/api/optics-scene-to-sensor-irradiance.md +371 -0
- package/docs/api/optics-scene-to-sensor-quadrature.md +78 -0
- package/docs/api/optics-sensor-environment-query.md +58 -0
- package/docs/api/optics-sensor-psf-quadrature.md +81 -0
- package/docs/api/optics-stray-light.md +109 -0
- package/docs/api/optics-thin-lens.md +53 -0
- package/docs/api/output-capture-corrected-sdr.md +71 -0
- package/docs/api/output-capture-geometry.md +500 -0
- package/docs/api/output-capture-sdr.md +69 -0
- package/docs/api/output-crop.md +47 -0
- package/docs/api/output-geometric-transforms.md +199 -0
- package/docs/api/output-lens-corrections.md +162 -0
- package/docs/api/output-sdr-rendering.md +122 -0
- package/docs/api/output-subject-framing-crop.md +59 -0
- package/docs/api/schema-camera.md +84 -0
- package/docs/api/schema-illumination-spectral.md +77 -0
- package/docs/api/schema-illumination-temporal.md +289 -0
- package/docs/api/schema-illumination.md +246 -0
- package/docs/api/schema-scene-radiance-temporal.md +215 -0
- package/docs/api/schema-scene-radiance.md +415 -0
- package/docs/api/schema-scene.md +105 -0
- package/docs/api/schema-validation.md +25 -0
- package/docs/api/sensor-accumulated-charge.md +273 -0
- package/docs/api/sensor-architecture.md +155 -0
- package/docs/api/sensor-camera-saturation-capacity.md +215 -0
- package/docs/api/sensor-capture-color-sampling-binding.md +323 -0
- package/docs/api/sensor-capture-mode-timing.md +123 -0
- package/docs/api/sensor-capture-mode.md +321 -0
- package/docs/api/sensor-color-sampling.md +228 -0
- package/docs/api/sensor-constant-rate-temporal-integration.md +152 -0
- package/docs/api/sensor-dark-current.md +227 -0
- package/docs/api/sensor-environment-photo-signal.md +77 -0
- package/docs/api/sensor-eqe-electron-rate.md +166 -0
- package/docs/api/sensor-eqe-local-exposure.md +68 -0
- package/docs/api/sensor-eqe-temporal-exposure.md +73 -0
- package/docs/api/sensor-exposure-window.md +183 -0
- package/docs/api/sensor-iso-signal-chain.md +264 -0
- package/docs/api/sensor-local-exposure-binding.md +126 -0
- package/docs/api/sensor-optical-stack.md +211 -0
- package/docs/api/sensor-photon-energy.md +149 -0
- package/docs/api/sensor-physical-charge-capacity.md +196 -0
- package/docs/api/sensor-pixel-pitch.md +41 -0
- package/docs/api/sensor-radiometry-readiness.md +240 -0
- package/docs/api/sensor-raw-readout.md +499 -0
- package/docs/api/sensor-raw-reconstruction.md +260 -0
- package/docs/api/sensor-readout-exposure-linkage.md +172 -0
- package/docs/api/sensor-readout-timing.md +209 -0
- package/docs/api/sensor-response-application-compatibility.md +256 -0
- package/docs/api/sensor-response-operating-range.md +293 -0
- package/docs/api/sensor-responsivity-photocurrent.md +245 -0
- package/docs/api/sensor-sampling-aperture.md +221 -0
- package/docs/api/sensor-scene-eqe-temporal-exposure.md +56 -0
- package/docs/api/sensor-sensor-geometry.md +143 -0
- package/docs/api/sensor-signal-noise.md +75 -0
- package/docs/api/sensor-spatial-sample-reduction.md +186 -0
- package/docs/api/sensor-spatial-sampling-quadrature.md +188 -0
- package/docs/api/sensor-spatio-spectral-reduction.md +217 -0
- package/docs/api/sensor-spectral-coverage.md +33 -0
- package/docs/api/sensor-spectral-quadrature.md +183 -0
- package/docs/api/sensor-spectral-response.md +291 -0
- package/docs/api/sensor-temporal-photo-signal.md +62 -0
- package/docs/api/simulation-poc-simulation.md +364 -0
- package/docs/api/stabilization-camera-shake.md +71 -0
- package/docs/api/stabilization-system.md +442 -0
- package/docs/benchmarks/2026-10-01-main-baseline.json +694 -0
- package/docs/benchmarks/2026-10-02-geometric-preparation-reuse.json +2190 -0
- package/docs/benchmarks/2026-10-02-poc-projection-reuse.json +2423 -0
- package/docs/benchmarks/2026-10-02-v1-environment-gc-profile.json +210 -0
- package/docs/benchmarks/2026-10-02-v1-environment-ordinary.json +187 -0
- package/docs/benchmarks/2026-10-02-v1-geometry-gc-profile.json +756 -0
- package/docs/benchmarks/2026-10-02-v1-geometry-ordinary-1.json +711 -0
- package/docs/benchmarks/2026-10-02-v1-geometry-ordinary-2.json +711 -0
- package/docs/benchmarks/2026-10-02-v1-poc-gc-profile.json +885 -0
- package/docs/benchmarks/2026-10-02-v1-poc-ordinary-1.json +868 -0
- package/docs/benchmarks/2026-10-02-v1-poc-ordinary-2.json +868 -0
- package/docs/examples/production-capture.mjs +34 -0
- package/docs/examples/production-request.json +4613 -0
- package/docs/examples/quick-start.mjs +12 -0
- package/docs/validation/documentation-fragments.json +212 -0
- package/docs/validation/release-audit-inventory.json +1306 -0
- package/docs/validation/tier-acceptance-report-v1.json +2538 -0
- package/package.json +18 -4
|
@@ -0,0 +1,2499 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Module boundary and integration notes.
|
|
4
|
+
* Validate an untrusted declaration and return the normalized typed contract. Unknown enum values,
|
|
5
|
+
* missing required fields and incompatible scientific data fail at this boundary. The planner owns
|
|
6
|
+
* scientific stage ordering, immutable capture identity and reproducibility. Well-formed unsupported
|
|
7
|
+
* combinations produce structured blockers; malformed declarations throw. The bounded environment
|
|
8
|
+
* route executes supplied radiance through native RAW and optional output, while sample-only and
|
|
9
|
+
* attached-RAW routes retain missing upstream blockers. FNV fingerprints are reproducibility keys, not
|
|
10
|
+
* cryptographic integrity proofs.
|
|
11
|
+
* Validate an untrusted declaration and return the normalized typed contract. Unknown enum values,
|
|
12
|
+
* missing required fields and incompatible scientific data fail at this boundary. The planner owns
|
|
13
|
+
* scientific stage ordering, immutable capture identity and reproducibility. Well-formed unsupported
|
|
14
|
+
* combinations produce structured blockers; malformed declarations throw. The bounded environment
|
|
15
|
+
* route executes supplied radiance through native RAW and optional output, while sample-only and
|
|
16
|
+
* attached-RAW routes retain missing upstream blockers. FNV fingerprints are reproducibility keys, not
|
|
17
|
+
* cryptographic integrity proofs.
|
|
18
|
+
* @see docs/PRODUCTION_ENVIRONMENT_CAPTURE.md for equations, coordinate/unit conventions, blockers and
|
|
19
|
+
* support limits.
|
|
20
|
+
*/
|
|
21
|
+
import { simulateEnvironmentSensorRawFrame } from "../capture/environment-raw-producer.js";
|
|
22
|
+
import { parseSimulatedCapture } from "../capture/simulated-capture.js";
|
|
23
|
+
import { calculateProcessedSensorRaw, validateProcessedSensorRawEnvelope } from "../capture/photographic-export.js";
|
|
24
|
+
import { stringifyCanonicalJson } from "../core/canonical-json.js";
|
|
25
|
+
import { RELEASE_SEQUENCE_VERSION } from "../capture/release-sequence.js";
|
|
26
|
+
import { parseResolvedWhiteBalanceState } from "../color/white-balance.js";
|
|
27
|
+
import { InvalidConfigurationError } from "../core/configuration-error.js";
|
|
28
|
+
import { getImageFormationContract, IMAGE_FORMATION_CONTRACT_VERSION } from "../core/image-formation.js";
|
|
29
|
+
import { ENGINE_API_VERSION } from "../core/version.js";
|
|
30
|
+
import { SCIENTIFIC_ASSURANCE_CONTRACT_VERSION, composeScientificAssurance } from "../core/scientific-assurance.js";
|
|
31
|
+
import { InvalidScientificInputError } from "../core/validation.js";
|
|
32
|
+
import { calculateCaptureRotationTemporalQuadrature } from "../motion/capture-rotation-temporal-quadrature.js";
|
|
33
|
+
import { calculateCaptureExposureWindows } from "../sensor/exposure-window.js";
|
|
34
|
+
import { calculateSensorReadoutTiming } from "../sensor/readout-timing.js";
|
|
35
|
+
import { GENERIC_EQUIPMENT_EXPOSURE_CAPABILITY_SCHEMA_VERSION } from "../equipment/exposure-capabilities.js";
|
|
36
|
+
import { parseFrontOfLensFilterProfile, resolveFrontOfLensFilterTransmission } from "../optics/front-of-lens-filter.js";
|
|
37
|
+
import { parseFocusPlane } from "../optics/focus-state.js";
|
|
38
|
+
import { calculateSceneRadianceToSensorIrradiance, parseSceneToSensorIrradianceProfile } from "../optics/scene-to-sensor-irradiance.js";
|
|
39
|
+
import { parseSceneRadianceEvaluationRequest, parseSceneRadianceEvaluationResult } from "../schema/scene-radiance.js";
|
|
40
|
+
export const PRODUCTION_IMAGE_FORMATION_PLAN_VERSION = "0.7.0";
|
|
41
|
+
export const PREPARED_IMAGE_FORMATION_CONTEXT_VERSION = "0.1.0";
|
|
42
|
+
export const PRODUCTION_CAPTURE_SNAPSHOT_VERSION = "0.3.0";
|
|
43
|
+
export const RENDERER_CAPABILITY_SCHEMA_VERSION = "0.1.0";
|
|
44
|
+
export const IMAGE_FORMATION_FIDELITY_PROFILE_SCHEMA_VERSION = "0.1.0";
|
|
45
|
+
const PROCESSED_OUTPUT_STAGES = new Set([
|
|
46
|
+
"reconstruction", "physical-orientation-transform", "output-crop-resample", "display-processing"
|
|
47
|
+
]);
|
|
48
|
+
const COMPOSER_SUPPORTED_STAGES = new Set([
|
|
49
|
+
"scene-ray-projection",
|
|
50
|
+
"scene-radiance-evaluation",
|
|
51
|
+
"lens-field-pupil-evaluation",
|
|
52
|
+
"temporal-exposure-readout"
|
|
53
|
+
]);
|
|
54
|
+
const COMPOSER_SUPPORTED_EFFECTS = new Set([
|
|
55
|
+
"illumination-vignetting",
|
|
56
|
+
"spatial-camera-rotation",
|
|
57
|
+
"rolling-readout"
|
|
58
|
+
]);
|
|
59
|
+
function requireRecord(value, path) {
|
|
60
|
+
if (typeof value !== "object" ||
|
|
61
|
+
value === null ||
|
|
62
|
+
Array.isArray(value)) {
|
|
63
|
+
throw new InvalidConfigurationError(path + " must be an object.");
|
|
64
|
+
}
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
function requireNonEmptyString(value, path) {
|
|
68
|
+
if (typeof value !== "string" ||
|
|
69
|
+
value.trim().length === 0) {
|
|
70
|
+
throw new InvalidConfigurationError(path + " must be a non-empty string.");
|
|
71
|
+
}
|
|
72
|
+
return value.trim();
|
|
73
|
+
}
|
|
74
|
+
function requireFinite(value, path) {
|
|
75
|
+
if (typeof value !== "number" ||
|
|
76
|
+
!Number.isFinite(value)) {
|
|
77
|
+
throw new InvalidConfigurationError(path + " must be finite.");
|
|
78
|
+
}
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
function requirePositiveFinite(value, path) {
|
|
82
|
+
const parsed = requireFinite(value, path);
|
|
83
|
+
if (parsed <= 0) {
|
|
84
|
+
throw new InvalidConfigurationError(path +
|
|
85
|
+
" must be greater than zero.");
|
|
86
|
+
}
|
|
87
|
+
return parsed;
|
|
88
|
+
}
|
|
89
|
+
function requireUint32(value, path) {
|
|
90
|
+
if (typeof value !== "number" ||
|
|
91
|
+
!Number.isSafeInteger(value) ||
|
|
92
|
+
value < 0 ||
|
|
93
|
+
value > 0xffff_ffff) {
|
|
94
|
+
throw new InvalidConfigurationError(path +
|
|
95
|
+
" must be an unsigned 32-bit integer.");
|
|
96
|
+
}
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
99
|
+
function contractStageIds() {
|
|
100
|
+
return new Set(getImageFormationContract()
|
|
101
|
+
.stages.map((stage) => stage.id));
|
|
102
|
+
}
|
|
103
|
+
function contractEffectIds() {
|
|
104
|
+
return new Set(getImageFormationContract()
|
|
105
|
+
.effectPlacements.map((effect) => effect.id));
|
|
106
|
+
}
|
|
107
|
+
function parseUniqueStageIds(value, path) {
|
|
108
|
+
if (!Array.isArray(value)) {
|
|
109
|
+
throw new InvalidConfigurationError(path + " must be an array.");
|
|
110
|
+
}
|
|
111
|
+
const valid = contractStageIds();
|
|
112
|
+
const seen = new Set();
|
|
113
|
+
value.forEach((entry, index) => {
|
|
114
|
+
if (typeof entry !== "string" ||
|
|
115
|
+
!valid.has(entry)) {
|
|
116
|
+
throw new InvalidConfigurationError(path +
|
|
117
|
+
"[" +
|
|
118
|
+
index +
|
|
119
|
+
"] is not a declared image-formation stage.");
|
|
120
|
+
}
|
|
121
|
+
const typed = entry;
|
|
122
|
+
if (seen.has(typed)) {
|
|
123
|
+
throw new InvalidConfigurationError(path +
|
|
124
|
+
" must not contain duplicate stage IDs.");
|
|
125
|
+
}
|
|
126
|
+
seen.add(typed);
|
|
127
|
+
});
|
|
128
|
+
return getImageFormationContract()
|
|
129
|
+
.stages
|
|
130
|
+
.map((stage) => stage.id)
|
|
131
|
+
.filter((stageId) => seen.has(stageId));
|
|
132
|
+
}
|
|
133
|
+
function parseUniqueEffectIds(value, path) {
|
|
134
|
+
if (!Array.isArray(value)) {
|
|
135
|
+
throw new InvalidConfigurationError(path + " must be an array.");
|
|
136
|
+
}
|
|
137
|
+
const valid = contractEffectIds();
|
|
138
|
+
const seen = new Set();
|
|
139
|
+
value.forEach((entry, index) => {
|
|
140
|
+
if (typeof entry !== "string" ||
|
|
141
|
+
!valid.has(entry)) {
|
|
142
|
+
throw new InvalidConfigurationError(path +
|
|
143
|
+
"[" +
|
|
144
|
+
index +
|
|
145
|
+
"] is not a declared image-formation effect.");
|
|
146
|
+
}
|
|
147
|
+
const typed = entry;
|
|
148
|
+
if (seen.has(typed)) {
|
|
149
|
+
throw new InvalidConfigurationError(path +
|
|
150
|
+
" must not contain duplicate effect IDs.");
|
|
151
|
+
}
|
|
152
|
+
seen.add(typed);
|
|
153
|
+
});
|
|
154
|
+
return getImageFormationContract()
|
|
155
|
+
.effectPlacements
|
|
156
|
+
.map((effect) => effect.id)
|
|
157
|
+
.filter((effectId) => seen.has(effectId));
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Validate an untrusted declaration and return the normalized typed contract. Unknown enum values,
|
|
161
|
+
* missing required fields and incompatible scientific data fail at this boundary.
|
|
162
|
+
*
|
|
163
|
+
* The planner owns scientific stage ordering, immutable capture identity and reproducibility.
|
|
164
|
+
* Well-formed unsupported combinations produce structured blockers; malformed declarations throw. The
|
|
165
|
+
* bounded environment route executes supplied radiance through native RAW and optional output, while
|
|
166
|
+
* sample-only and attached-RAW routes retain missing upstream blockers. FNV fingerprints are
|
|
167
|
+
* reproducibility keys, not cryptographic integrity proofs.
|
|
168
|
+
* @param value - unknown. Treated as untrusted data; static typing alone is not validation.
|
|
169
|
+
* @returns RendererCapabilityDeclaration. Return shape and scientific status are explicit; no calibration is inferred from successful execution.
|
|
170
|
+
*
|
|
171
|
+
* @see docs/API_REFERENCE.md for the root export and exact type graph.
|
|
172
|
+
*/
|
|
173
|
+
export function parseRendererCapabilityDeclaration(value) {
|
|
174
|
+
const record = requireRecord(value, "rendererCapability");
|
|
175
|
+
if (record.schemaVersion !==
|
|
176
|
+
RENDERER_CAPABILITY_SCHEMA_VERSION) {
|
|
177
|
+
throw new InvalidConfigurationError('rendererCapability.schemaVersion must be "' +
|
|
178
|
+
RENDERER_CAPABILITY_SCHEMA_VERSION +
|
|
179
|
+
'".');
|
|
180
|
+
}
|
|
181
|
+
const consumerKind = record.consumerKind;
|
|
182
|
+
if (consumerKind !==
|
|
183
|
+
"interactive-optimized" &&
|
|
184
|
+
consumerKind !== "reference") {
|
|
185
|
+
throw new InvalidConfigurationError("rendererCapability.consumerKind is invalid.");
|
|
186
|
+
}
|
|
187
|
+
const spectralCapability = record.spectralCapability;
|
|
188
|
+
if (spectralCapability !==
|
|
189
|
+
"wavelength-resolved" &&
|
|
190
|
+
spectralCapability !==
|
|
191
|
+
"wavelength-independent-approximation") {
|
|
192
|
+
throw new InvalidConfigurationError("rendererCapability.spectralCapability is invalid.");
|
|
193
|
+
}
|
|
194
|
+
const temporal = requireRecord(record.temporalSampling, "rendererCapability.temporalSampling");
|
|
195
|
+
let temporalSampling;
|
|
196
|
+
if (temporal.kind === "none") {
|
|
197
|
+
temporalSampling = {
|
|
198
|
+
kind: "none"
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
else if (temporal.kind === "bounded") {
|
|
202
|
+
const maximumSamples = temporal.maximumSamples;
|
|
203
|
+
if (typeof maximumSamples !==
|
|
204
|
+
"number" ||
|
|
205
|
+
!Number.isSafeInteger(maximumSamples) ||
|
|
206
|
+
maximumSamples <= 0) {
|
|
207
|
+
throw new InvalidConfigurationError("rendererCapability.temporalSampling.maximumSamples must be a positive safe integer.");
|
|
208
|
+
}
|
|
209
|
+
temporalSampling = {
|
|
210
|
+
kind: "bounded",
|
|
211
|
+
maximumSamples
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
throw new InvalidConfigurationError("rendererCapability.temporalSampling.kind is invalid.");
|
|
216
|
+
}
|
|
217
|
+
const depthCapability = record.depthCapability;
|
|
218
|
+
if (depthCapability !== "none" &&
|
|
219
|
+
depthCapability !== "per-layer") {
|
|
220
|
+
throw new InvalidConfigurationError("rendererCapability.depthCapability is invalid.");
|
|
221
|
+
}
|
|
222
|
+
const alphaRepresentation = record.alphaRepresentation;
|
|
223
|
+
if (alphaRepresentation !==
|
|
224
|
+
"premultiplied" &&
|
|
225
|
+
alphaRepresentation !==
|
|
226
|
+
"straight") {
|
|
227
|
+
throw new InvalidConfigurationError("rendererCapability.alphaRepresentation is invalid.");
|
|
228
|
+
}
|
|
229
|
+
for (const [key, raw] of [
|
|
230
|
+
[
|
|
231
|
+
"inverseFieldMapping",
|
|
232
|
+
record.inverseFieldMapping
|
|
233
|
+
],
|
|
234
|
+
[
|
|
235
|
+
"preservesDepthOrderAcrossWarps",
|
|
236
|
+
record
|
|
237
|
+
.preservesDepthOrderAcrossWarps
|
|
238
|
+
],
|
|
239
|
+
[
|
|
240
|
+
"sensorDomainProcessing",
|
|
241
|
+
record.sensorDomainProcessing
|
|
242
|
+
]
|
|
243
|
+
]) {
|
|
244
|
+
if (typeof raw !== "boolean") {
|
|
245
|
+
throw new InvalidConfigurationError("rendererCapability." +
|
|
246
|
+
key +
|
|
247
|
+
" must be boolean.");
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return {
|
|
251
|
+
schemaVersion: RENDERER_CAPABILITY_SCHEMA_VERSION,
|
|
252
|
+
rendererId: requireNonEmptyString(record.rendererId, "rendererCapability.rendererId"),
|
|
253
|
+
rendererVersion: requireNonEmptyString(record.rendererVersion, "rendererCapability.rendererVersion"),
|
|
254
|
+
consumerKind,
|
|
255
|
+
supportedStages: parseUniqueStageIds(record.supportedStages, "rendererCapability.supportedStages"),
|
|
256
|
+
supportedEffects: parseUniqueEffectIds(record.supportedEffects, "rendererCapability.supportedEffects"),
|
|
257
|
+
spectralCapability,
|
|
258
|
+
temporalSampling,
|
|
259
|
+
depthCapability,
|
|
260
|
+
inverseFieldMapping: record
|
|
261
|
+
.inverseFieldMapping,
|
|
262
|
+
alphaRepresentation,
|
|
263
|
+
preservesDepthOrderAcrossWarps: record
|
|
264
|
+
.preservesDepthOrderAcrossWarps,
|
|
265
|
+
sensorDomainProcessing: record
|
|
266
|
+
.sensorDomainProcessing
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Validate an untrusted declaration and return the normalized typed contract. Unknown enum values,
|
|
271
|
+
* missing required fields and incompatible scientific data fail at this boundary.
|
|
272
|
+
*
|
|
273
|
+
* The planner owns scientific stage ordering, immutable capture identity and reproducibility.
|
|
274
|
+
* Well-formed unsupported combinations produce structured blockers; malformed declarations throw. The
|
|
275
|
+
* bounded environment route executes supplied radiance through native RAW and optional output, while
|
|
276
|
+
* sample-only and attached-RAW routes retain missing upstream blockers. FNV fingerprints are
|
|
277
|
+
* reproducibility keys, not cryptographic integrity proofs.
|
|
278
|
+
* @param value - unknown. Treated as untrusted data; static typing alone is not validation.
|
|
279
|
+
* @returns ImageFormationFidelityProfile. Return shape and scientific status are explicit; no calibration is inferred from successful execution.
|
|
280
|
+
*
|
|
281
|
+
* @see docs/API_REFERENCE.md for the root export and exact type graph.
|
|
282
|
+
*/
|
|
283
|
+
export function parseImageFormationFidelityProfile(value) {
|
|
284
|
+
const record = requireRecord(value, "imageFormationFidelityProfile");
|
|
285
|
+
if (record.schemaVersion !==
|
|
286
|
+
IMAGE_FORMATION_FIDELITY_PROFILE_SCHEMA_VERSION) {
|
|
287
|
+
throw new InvalidConfigurationError('imageFormationFidelityProfile.schemaVersion must be "' +
|
|
288
|
+
IMAGE_FORMATION_FIDELITY_PROFILE_SCHEMA_VERSION +
|
|
289
|
+
'".');
|
|
290
|
+
}
|
|
291
|
+
if (!Array.isArray(record.requiredEffects)) {
|
|
292
|
+
throw new InvalidConfigurationError("imageFormationFidelityProfile.requiredEffects must be an array.");
|
|
293
|
+
}
|
|
294
|
+
const validEffects = contractEffectIds();
|
|
295
|
+
const seenEffects = new Set();
|
|
296
|
+
const requestedEffects = record.requiredEffects.map((entry, index) => {
|
|
297
|
+
const path = "imageFormationFidelityProfile.requiredEffects[" +
|
|
298
|
+
index +
|
|
299
|
+
"]";
|
|
300
|
+
const effect = requireRecord(entry, path);
|
|
301
|
+
const effectId = effect.effectId;
|
|
302
|
+
if (typeof effectId !==
|
|
303
|
+
"string" ||
|
|
304
|
+
!validEffects.has(effectId)) {
|
|
305
|
+
throw new InvalidConfigurationError(path +
|
|
306
|
+
".effectId is not a declared image-formation effect.");
|
|
307
|
+
}
|
|
308
|
+
const typed = effectId;
|
|
309
|
+
if (seenEffects.has(typed)) {
|
|
310
|
+
throw new InvalidConfigurationError("imageFormationFidelityProfile.requiredEffects must not contain duplicate effect IDs.");
|
|
311
|
+
}
|
|
312
|
+
seenEffects.add(typed);
|
|
313
|
+
return {
|
|
314
|
+
effectId: typed,
|
|
315
|
+
modelId: requireNonEmptyString(effect.modelId, path + ".modelId"),
|
|
316
|
+
modelVersion: requireNonEmptyString(effect.modelVersion, path +
|
|
317
|
+
".modelVersion")
|
|
318
|
+
};
|
|
319
|
+
});
|
|
320
|
+
const effectOrder = new Map(getImageFormationContract()
|
|
321
|
+
.effectPlacements.map((effect, index) => [
|
|
322
|
+
effect.id,
|
|
323
|
+
index
|
|
324
|
+
]));
|
|
325
|
+
const requiredEffects = [
|
|
326
|
+
...requestedEffects
|
|
327
|
+
].sort((left, right) => (effectOrder.get(left.effectId) ?? 0) -
|
|
328
|
+
(effectOrder.get(right.effectId) ?? 0));
|
|
329
|
+
const requirements = requireRecord(record.rendererRequirements, "imageFormationFidelityProfile.rendererRequirements");
|
|
330
|
+
const spectral = requirements.spectral;
|
|
331
|
+
if (spectral !==
|
|
332
|
+
"wavelength-resolved" &&
|
|
333
|
+
spectral !==
|
|
334
|
+
"wavelength-independent-approximation") {
|
|
335
|
+
throw new InvalidConfigurationError("imageFormationFidelityProfile.rendererRequirements.spectral is invalid.");
|
|
336
|
+
}
|
|
337
|
+
if (typeof requirements
|
|
338
|
+
.sensorDomainProcessing !==
|
|
339
|
+
"boolean") {
|
|
340
|
+
throw new InvalidConfigurationError("imageFormationFidelityProfile.rendererRequirements.sensorDomainProcessing must be boolean.");
|
|
341
|
+
}
|
|
342
|
+
const depth = requirements.depth;
|
|
343
|
+
if (depth !== "none" &&
|
|
344
|
+
depth !== "per-layer") {
|
|
345
|
+
throw new InvalidConfigurationError("imageFormationFidelityProfile.rendererRequirements.depth is invalid.");
|
|
346
|
+
}
|
|
347
|
+
return {
|
|
348
|
+
schemaVersion: IMAGE_FORMATION_FIDELITY_PROFILE_SCHEMA_VERSION,
|
|
349
|
+
profileId: requireNonEmptyString(record.profileId, "imageFormationFidelityProfile.profileId"),
|
|
350
|
+
profileVersion: requireNonEmptyString(record.profileVersion, "imageFormationFidelityProfile.profileVersion"),
|
|
351
|
+
requiredStages: parseUniqueStageIds(record.requiredStages, "imageFormationFidelityProfile.requiredStages"),
|
|
352
|
+
requiredEffects,
|
|
353
|
+
rendererRequirements: {
|
|
354
|
+
spectral,
|
|
355
|
+
sensorDomainProcessing: requirements
|
|
356
|
+
.sensorDomainProcessing,
|
|
357
|
+
depth
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
function validateEquipmentCapabilities(value) {
|
|
362
|
+
if (value.schemaVersion !==
|
|
363
|
+
GENERIC_EQUIPMENT_EXPOSURE_CAPABILITY_SCHEMA_VERSION ||
|
|
364
|
+
value.scientificStatus !==
|
|
365
|
+
"approximation") {
|
|
366
|
+
throw new InvalidConfigurationError("equipmentCapabilities do not satisfy the resolved generic equipment capability contract.");
|
|
367
|
+
}
|
|
368
|
+
requireNonEmptyString(value.bodyProfile.profileId, "equipmentCapabilities.bodyProfile.profileId");
|
|
369
|
+
requireNonEmptyString(value.bodyProfile.profileVersion, "equipmentCapabilities.bodyProfile.profileVersion");
|
|
370
|
+
requireNonEmptyString(value.lensProfile.profileId, "equipmentCapabilities.lensProfile.profileId");
|
|
371
|
+
requireNonEmptyString(value.lensProfile.profileVersion, "equipmentCapabilities.lensProfile.profileVersion");
|
|
372
|
+
requirePositiveFinite(value.selectedFocalLengthMm, "equipmentCapabilities.selectedFocalLengthMm");
|
|
373
|
+
requirePositiveFinite(value.aperture
|
|
374
|
+
.widestAvailableFNumber, "equipmentCapabilities.aperture.widestAvailableFNumber");
|
|
375
|
+
requirePositiveFinite(value.aperture
|
|
376
|
+
.narrowestAvailableFNumber, "equipmentCapabilities.aperture.narrowestAvailableFNumber");
|
|
377
|
+
requirePositiveFinite(value.shutter.minimumSeconds, "equipmentCapabilities.shutter.minimumSeconds");
|
|
378
|
+
requirePositiveFinite(value.shutter.maximumSeconds, "equipmentCapabilities.shutter.maximumSeconds");
|
|
379
|
+
requirePositiveFinite(value.iso.minimum, "equipmentCapabilities.iso.minimum");
|
|
380
|
+
requirePositiveFinite(value.iso.maximum, "equipmentCapabilities.iso.maximum");
|
|
381
|
+
}
|
|
382
|
+
function settingEquals(left, right) {
|
|
383
|
+
const scale = Math.max(1, Math.abs(left), Math.abs(right));
|
|
384
|
+
return (Math.abs(left - right) <=
|
|
385
|
+
Number.EPSILON *
|
|
386
|
+
32 *
|
|
387
|
+
scale);
|
|
388
|
+
}
|
|
389
|
+
function validateSetting(value, minimum, maximum, grid, path) {
|
|
390
|
+
if (value < minimum ||
|
|
391
|
+
value > maximum) {
|
|
392
|
+
throw new InvalidScientificInputError(path +
|
|
393
|
+
" lies outside the prepared equipment capability range.");
|
|
394
|
+
}
|
|
395
|
+
if (grid.kind ===
|
|
396
|
+
"discrete-values" &&
|
|
397
|
+
!grid.values.some((candidate) => settingEquals(candidate, value))) {
|
|
398
|
+
throw new InvalidScientificInputError(path +
|
|
399
|
+
" is not present in the prepared discrete equipment setting grid.");
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
function cloneJson(value) {
|
|
403
|
+
return JSON.parse(JSON.stringify(value));
|
|
404
|
+
}
|
|
405
|
+
function deepFreeze(value) {
|
|
406
|
+
if (typeof value !== "object" ||
|
|
407
|
+
value === null ||
|
|
408
|
+
Object.isFrozen(value)) {
|
|
409
|
+
return value;
|
|
410
|
+
}
|
|
411
|
+
Object.freeze(value);
|
|
412
|
+
for (const child of Object.values(value)) {
|
|
413
|
+
deepFreeze(child);
|
|
414
|
+
}
|
|
415
|
+
return value;
|
|
416
|
+
}
|
|
417
|
+
function canonicalStringify(value) {
|
|
418
|
+
return stringifyCanonicalJson(value, {
|
|
419
|
+
undefinedObjectProperties: "omit",
|
|
420
|
+
nonFiniteNumberMessage: "Canonical plan serialization does not permit non-finite numbers.",
|
|
421
|
+
unsupportedValueMessage: "Canonical plan serialization encountered an unsupported value type."
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
function fingerprintText(text) {
|
|
425
|
+
let hash = 0x811c9dc5;
|
|
426
|
+
const bytes = new TextEncoder().encode(text);
|
|
427
|
+
for (const byte of bytes) {
|
|
428
|
+
hash ^= byte;
|
|
429
|
+
hash =
|
|
430
|
+
Math.imul(hash, 0x01000193) >>> 0;
|
|
431
|
+
}
|
|
432
|
+
return hash
|
|
433
|
+
.toString(16)
|
|
434
|
+
.padStart(8, "0");
|
|
435
|
+
}
|
|
436
|
+
function fingerprintValue(value) {
|
|
437
|
+
return fingerprintText(canonicalStringify(value));
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Return deterministic canonical JSON of a validated production plan. Sorted object keys preserve
|
|
441
|
+
* array order; the representation contains data and excludes executable provider callbacks.
|
|
442
|
+
*
|
|
443
|
+
* The planner owns scientific stage ordering, immutable capture identity and reproducibility.
|
|
444
|
+
* Well-formed unsupported combinations produce structured blockers; malformed declarations throw. The
|
|
445
|
+
* bounded environment route executes supplied radiance through native RAW and optional output, while
|
|
446
|
+
* sample-only and attached-RAW routes retain missing upstream blockers. FNV fingerprints are
|
|
447
|
+
* reproducibility keys, not cryptographic integrity proofs.
|
|
448
|
+
* @param plan - ProductionImageFormationPlan. See the linked contract for coordinate, unit and profile binding semantics.
|
|
449
|
+
* @returns string. Return shape and scientific status are explicit; no calibration is inferred from successful execution.
|
|
450
|
+
*
|
|
451
|
+
* @see docs/API_REFERENCE.md for the root export and exact type graph.
|
|
452
|
+
*/
|
|
453
|
+
export function serializeProductionImageFormationPlan(plan) {
|
|
454
|
+
return canonicalStringify(plan);
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Validate an untrusted declaration and return the normalized typed contract. Unknown enum values,
|
|
458
|
+
* missing required fields and incompatible scientific data fail at this boundary.
|
|
459
|
+
*
|
|
460
|
+
* The planner owns scientific stage ordering, immutable capture identity and reproducibility.
|
|
461
|
+
* Well-formed unsupported combinations produce structured blockers; malformed declarations throw. The
|
|
462
|
+
* bounded environment route executes supplied radiance through native RAW and optional output, while
|
|
463
|
+
* sample-only and attached-RAW routes retain missing upstream blockers. FNV fingerprints are
|
|
464
|
+
* reproducibility keys, not cryptographic integrity proofs.
|
|
465
|
+
* @param value - unknown. Treated as untrusted data; static typing alone is not validation.
|
|
466
|
+
* @returns PreparedImageFormationContext. Return shape and scientific status are explicit; no calibration is inferred from successful execution.
|
|
467
|
+
*
|
|
468
|
+
* @see docs/API_REFERENCE.md for the root export and exact type graph.
|
|
469
|
+
*/
|
|
470
|
+
export function parsePreparedImageFormationContext(value) {
|
|
471
|
+
const record = requireRecord(value, "preparedImageFormationContext");
|
|
472
|
+
if (record.version !==
|
|
473
|
+
PREPARED_IMAGE_FORMATION_CONTEXT_VERSION) {
|
|
474
|
+
throw new InvalidConfigurationError('preparedImageFormationContext.version must be "' +
|
|
475
|
+
PREPARED_IMAGE_FORMATION_CONTEXT_VERSION +
|
|
476
|
+
'".');
|
|
477
|
+
}
|
|
478
|
+
const parsed = prepareImageFormationContext({
|
|
479
|
+
contextId: record.contextId,
|
|
480
|
+
sceneId: record.sceneId,
|
|
481
|
+
sceneRadianceProviderProfileId: record
|
|
482
|
+
.sceneRadianceProviderProfileId,
|
|
483
|
+
outputGeometryProfileId: record
|
|
484
|
+
.outputGeometryProfileId,
|
|
485
|
+
equipmentCapabilities: record
|
|
486
|
+
.equipmentCapabilities,
|
|
487
|
+
...(record
|
|
488
|
+
.opticalBridgeProfile ===
|
|
489
|
+
undefined
|
|
490
|
+
? {}
|
|
491
|
+
: {
|
|
492
|
+
opticalBridgeProfile: record
|
|
493
|
+
.opticalBridgeProfile
|
|
494
|
+
}),
|
|
495
|
+
renderer: record.renderer,
|
|
496
|
+
fidelity: record.fidelity
|
|
497
|
+
});
|
|
498
|
+
if (typeof record.fingerprint !==
|
|
499
|
+
"object" ||
|
|
500
|
+
record.fingerprint === null) {
|
|
501
|
+
throw new InvalidConfigurationError("preparedImageFormationContext.fingerprint must be present.");
|
|
502
|
+
}
|
|
503
|
+
const supplied = requireRecord(record.fingerprint, "preparedImageFormationContext.fingerprint");
|
|
504
|
+
if (supplied.algorithm !==
|
|
505
|
+
"fnv1a-32-non-cryptographic" ||
|
|
506
|
+
supplied.value !==
|
|
507
|
+
parsed.fingerprint.value) {
|
|
508
|
+
throw new InvalidConfigurationError("preparedImageFormationContext fingerprint does not match its parsed content.");
|
|
509
|
+
}
|
|
510
|
+
return parsed;
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Parse, own and freeze relatively static equipment, scene, renderer and fidelity declarations for
|
|
514
|
+
* reuse by later immutable captures. This prepares semantic state and does not render a frame.
|
|
515
|
+
*
|
|
516
|
+
* The planner owns scientific stage ordering, immutable capture identity and reproducibility.
|
|
517
|
+
* Well-formed unsupported combinations produce structured blockers; malformed declarations throw. The
|
|
518
|
+
* bounded environment route executes supplied radiance through native RAW and optional output, while
|
|
519
|
+
* sample-only and attached-RAW routes retain missing upstream blockers. FNV fingerprints are
|
|
520
|
+
* reproducibility keys, not cryptographic integrity proofs.
|
|
521
|
+
* @param input - PrepareImageFormationContextInput. See the linked contract for coordinate, unit and profile binding semantics.
|
|
522
|
+
* @returns PreparedImageFormationContext. Return shape and scientific status are explicit; no calibration is inferred from successful execution.
|
|
523
|
+
*
|
|
524
|
+
* @see docs/API_REFERENCE.md for the root export and exact type graph.
|
|
525
|
+
*/
|
|
526
|
+
export function prepareImageFormationContext(input) {
|
|
527
|
+
validateEquipmentCapabilities(input.equipmentCapabilities);
|
|
528
|
+
const renderer = parseRendererCapabilityDeclaration(input.renderer);
|
|
529
|
+
const fidelity = parseImageFormationFidelityProfile(input.fidelity);
|
|
530
|
+
const opticalBridgeProfile = input.opticalBridgeProfile ===
|
|
531
|
+
undefined
|
|
532
|
+
? undefined
|
|
533
|
+
: parseSceneToSensorIrradianceProfile(input
|
|
534
|
+
.opticalBridgeProfile);
|
|
535
|
+
if (opticalBridgeProfile !==
|
|
536
|
+
undefined &&
|
|
537
|
+
opticalBridgeProfile
|
|
538
|
+
.lensProfileId !==
|
|
539
|
+
input
|
|
540
|
+
.equipmentCapabilities
|
|
541
|
+
.lensProfile.profileId) {
|
|
542
|
+
throw new InvalidConfigurationError("opticalBridgeProfile.lensProfileId must match equipmentCapabilities.lensProfile.profileId.");
|
|
543
|
+
}
|
|
544
|
+
const core = {
|
|
545
|
+
version: PREPARED_IMAGE_FORMATION_CONTEXT_VERSION,
|
|
546
|
+
contextId: requireNonEmptyString(input.contextId, "contextId"),
|
|
547
|
+
sceneId: requireNonEmptyString(input.sceneId, "sceneId"),
|
|
548
|
+
sceneRadianceProviderProfileId: requireNonEmptyString(input
|
|
549
|
+
.sceneRadianceProviderProfileId, "sceneRadianceProviderProfileId"),
|
|
550
|
+
outputGeometryProfileId: requireNonEmptyString(input.outputGeometryProfileId, "outputGeometryProfileId"),
|
|
551
|
+
equipmentCapabilities: cloneJson(input
|
|
552
|
+
.equipmentCapabilities),
|
|
553
|
+
...(opticalBridgeProfile ===
|
|
554
|
+
undefined
|
|
555
|
+
? {}
|
|
556
|
+
: {
|
|
557
|
+
opticalBridgeProfile: cloneJson(opticalBridgeProfile)
|
|
558
|
+
}),
|
|
559
|
+
renderer,
|
|
560
|
+
fidelity
|
|
561
|
+
};
|
|
562
|
+
const prepared = {
|
|
563
|
+
...core,
|
|
564
|
+
fingerprint: {
|
|
565
|
+
algorithm: "fnv1a-32-non-cryptographic",
|
|
566
|
+
value: fingerprintValue(core)
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
return deepFreeze(cloneJson(prepared));
|
|
570
|
+
}
|
|
571
|
+
function validatePhysicalSceneSample(sample, sceneTimeSecondsFromExposureStart) {
|
|
572
|
+
const request = parseSceneRadianceEvaluationRequest(sample.sceneRadianceRequest);
|
|
573
|
+
const result = parseSceneRadianceEvaluationResult(sample.sceneRadianceResult);
|
|
574
|
+
if (request
|
|
575
|
+
.timeSecondsFromExposureStart !==
|
|
576
|
+
sceneTimeSecondsFromExposureStart) {
|
|
577
|
+
throw new InvalidConfigurationError("physicalSceneSample.sceneRadianceRequest.timeSecondsFromExposureStart must equal the capture snapshot scene time.");
|
|
578
|
+
}
|
|
579
|
+
const imagePoint = requireRecord(sample.imagePointMm, "physicalSceneSample.imagePointMm");
|
|
580
|
+
return cloneJson({
|
|
581
|
+
sceneRadianceRequest: request,
|
|
582
|
+
sceneRadianceResult: result,
|
|
583
|
+
focus: sample.focus,
|
|
584
|
+
imagePointMm: {
|
|
585
|
+
x: requireFinite(imagePoint.x, "physicalSceneSample.imagePointMm.x"),
|
|
586
|
+
y: requireFinite(imagePoint.y, "physicalSceneSample.imagePointMm.y")
|
|
587
|
+
},
|
|
588
|
+
fieldThroughput: sample.fieldThroughput,
|
|
589
|
+
...(sample.frontOfLensFilters ===
|
|
590
|
+
undefined
|
|
591
|
+
? {}
|
|
592
|
+
: {
|
|
593
|
+
frontOfLensFilters: sample.frontOfLensFilters.map((profile) => parseFrontOfLensFilterProfile(profile))
|
|
594
|
+
})
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
const CAPTURE_ORIENTATIONS = new Set([
|
|
598
|
+
"landscape",
|
|
599
|
+
"portrait-clockwise",
|
|
600
|
+
"landscape-inverted",
|
|
601
|
+
"portrait-counter-clockwise"
|
|
602
|
+
]);
|
|
603
|
+
function validateTemporalCaptureInput(input, shutterSeconds) {
|
|
604
|
+
const exposureWindows = calculateCaptureExposureWindows(input.exposureWindowInput).value;
|
|
605
|
+
if (!settingEquals(exposureWindows
|
|
606
|
+
.nominalExposureDurationSeconds
|
|
607
|
+
.value, shutterSeconds)) {
|
|
608
|
+
throw new InvalidConfigurationError("temporalCapture.exposureWindowInput.nominalExposureDurationSeconds must match exposure.shutterSeconds.");
|
|
609
|
+
}
|
|
610
|
+
requirePositiveFinite(input.imagingArea.widthMm, "temporalCapture.imagingArea.widthMm");
|
|
611
|
+
requirePositiveFinite(input.imagingArea.heightMm, "temporalCapture.imagingArea.heightMm");
|
|
612
|
+
if (!CAPTURE_ORIENTATIONS.has(input.orientation)) {
|
|
613
|
+
throw new InvalidConfigurationError("temporalCapture.orientation is invalid.");
|
|
614
|
+
}
|
|
615
|
+
if (input.readout !== undefined) {
|
|
616
|
+
calculateSensorReadoutTiming({
|
|
617
|
+
nativeRaster: input.exposureWindowInput
|
|
618
|
+
.nativeRaster,
|
|
619
|
+
...(input.exposureWindowInput
|
|
620
|
+
.activeCaptureRect ===
|
|
621
|
+
undefined
|
|
622
|
+
? {}
|
|
623
|
+
: {
|
|
624
|
+
activeCaptureRect: input
|
|
625
|
+
.exposureWindowInput
|
|
626
|
+
.activeCaptureRect
|
|
627
|
+
}),
|
|
628
|
+
shutterMechanism: input.exposureWindowInput
|
|
629
|
+
.shutterMechanism,
|
|
630
|
+
readout: input.readout,
|
|
631
|
+
...(input.exposureWindowInput
|
|
632
|
+
.samplePointsNative ===
|
|
633
|
+
undefined
|
|
634
|
+
? {}
|
|
635
|
+
: {
|
|
636
|
+
samplePointsNative: input
|
|
637
|
+
.exposureWindowInput
|
|
638
|
+
.samplePointsNative
|
|
639
|
+
})
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
if (input.rotation !== undefined) {
|
|
643
|
+
const sampleCount = input.rotation
|
|
644
|
+
.temporalSampleCount;
|
|
645
|
+
if (!Number.isSafeInteger(sampleCount) ||
|
|
646
|
+
sampleCount <= 0) {
|
|
647
|
+
throw new InvalidConfigurationError("temporalCapture.rotation.temporalSampleCount must be a positive safe integer.");
|
|
648
|
+
}
|
|
649
|
+
for (const [axis, value] of [
|
|
650
|
+
[
|
|
651
|
+
"pitch",
|
|
652
|
+
input.rotation
|
|
653
|
+
.angularVelocityRadPerSec
|
|
654
|
+
.pitch
|
|
655
|
+
],
|
|
656
|
+
[
|
|
657
|
+
"yaw",
|
|
658
|
+
input.rotation
|
|
659
|
+
.angularVelocityRadPerSec
|
|
660
|
+
.yaw
|
|
661
|
+
],
|
|
662
|
+
[
|
|
663
|
+
"roll",
|
|
664
|
+
input.rotation
|
|
665
|
+
.angularVelocityRadPerSec
|
|
666
|
+
.roll
|
|
667
|
+
]
|
|
668
|
+
]) {
|
|
669
|
+
requireFinite(value, "temporalCapture.rotation.angularVelocityRadPerSec." +
|
|
670
|
+
axis);
|
|
671
|
+
}
|
|
672
|
+
if (input.rotation
|
|
673
|
+
.focusDistanceM !==
|
|
674
|
+
undefined) {
|
|
675
|
+
requirePositiveFinite(input.rotation
|
|
676
|
+
.focusDistanceM, "temporalCapture.rotation.focusDistanceM");
|
|
677
|
+
}
|
|
678
|
+
if (!Array.isArray(input.exposureWindowInput
|
|
679
|
+
.samplePointsNative) ||
|
|
680
|
+
input.exposureWindowInput
|
|
681
|
+
.samplePointsNative
|
|
682
|
+
.length === 0) {
|
|
683
|
+
throw new InvalidConfigurationError("temporalCapture.rotation requires non-empty exposureWindowInput.samplePointsNative.");
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
return cloneJson(input);
|
|
687
|
+
}
|
|
688
|
+
const RELEASE_TIMING_CONSTRAINTS = new Set([
|
|
689
|
+
"requested-cadence",
|
|
690
|
+
"body-maximum-cadence",
|
|
691
|
+
"exposure-duration",
|
|
692
|
+
"minimum-inter-frame-gap"
|
|
693
|
+
]);
|
|
694
|
+
function validateReleaseAutomationState(value, path) {
|
|
695
|
+
if (value !== "manual" &&
|
|
696
|
+
value !== "locked" &&
|
|
697
|
+
value !== "continuous") {
|
|
698
|
+
throw new InvalidConfigurationError(path + " is invalid.");
|
|
699
|
+
}
|
|
700
|
+
return value;
|
|
701
|
+
}
|
|
702
|
+
function validateProductionReleaseFrameBinding(binding, releaseFrameId, exposure, stochasticSeedUint32) {
|
|
703
|
+
if (binding.releaseSequenceVersion !==
|
|
704
|
+
RELEASE_SEQUENCE_VERSION) {
|
|
705
|
+
throw new InvalidConfigurationError('releaseFrameBinding.releaseSequenceVersion must be "' +
|
|
706
|
+
RELEASE_SEQUENCE_VERSION +
|
|
707
|
+
'".');
|
|
708
|
+
}
|
|
709
|
+
const frameIndex = binding.frameIndex;
|
|
710
|
+
if (!Number.isSafeInteger(frameIndex) ||
|
|
711
|
+
frameIndex < 0) {
|
|
712
|
+
throw new InvalidConfigurationError("releaseFrameBinding.frameIndex must be a non-negative safe integer.");
|
|
713
|
+
}
|
|
714
|
+
const start = requireFinite(binding.exposureStartTimeSeconds, "releaseFrameBinding.exposureStartTimeSeconds");
|
|
715
|
+
const end = requireFinite(binding.exposureEndTimeSeconds, "releaseFrameBinding.exposureEndTimeSeconds");
|
|
716
|
+
if (end <= start) {
|
|
717
|
+
throw new InvalidConfigurationError("releaseFrameBinding exposure end must be after exposure start.");
|
|
718
|
+
}
|
|
719
|
+
if (!settingEquals(end - start, exposure.shutterSeconds)) {
|
|
720
|
+
throw new InvalidConfigurationError("releaseFrameBinding exposure duration must match exposure.shutterSeconds.");
|
|
721
|
+
}
|
|
722
|
+
const sceneTime = requireFinite(binding.sceneTimeSecondsFromSequenceStart, "releaseFrameBinding.sceneTimeSecondsFromSequenceStart");
|
|
723
|
+
if (sceneTime < 0) {
|
|
724
|
+
throw new InvalidConfigurationError("releaseFrameBinding.sceneTimeSecondsFromSequenceStart must be greater than or equal to zero.");
|
|
725
|
+
}
|
|
726
|
+
let interval = null;
|
|
727
|
+
if (binding.startIntervalFromPreviousSeconds !==
|
|
728
|
+
null) {
|
|
729
|
+
interval =
|
|
730
|
+
requirePositiveFinite(binding.startIntervalFromPreviousSeconds, "releaseFrameBinding.startIntervalFromPreviousSeconds");
|
|
731
|
+
}
|
|
732
|
+
if (frameIndex === 0 &&
|
|
733
|
+
interval !== null) {
|
|
734
|
+
throw new InvalidConfigurationError("The first release frame must not declare a previous-frame interval.");
|
|
735
|
+
}
|
|
736
|
+
if (frameIndex > 0 &&
|
|
737
|
+
interval === null) {
|
|
738
|
+
throw new InvalidConfigurationError("A non-first release frame must declare its previous-frame interval.");
|
|
739
|
+
}
|
|
740
|
+
if (!Array.isArray(binding.timingConstraints)) {
|
|
741
|
+
throw new InvalidConfigurationError("releaseFrameBinding.timingConstraints must be an array.");
|
|
742
|
+
}
|
|
743
|
+
const timingConstraints = binding.timingConstraints.map((constraint, index) => {
|
|
744
|
+
if (!RELEASE_TIMING_CONSTRAINTS.has(constraint)) {
|
|
745
|
+
throw new InvalidConfigurationError("releaseFrameBinding.timingConstraints[" +
|
|
746
|
+
index +
|
|
747
|
+
"] is invalid.");
|
|
748
|
+
}
|
|
749
|
+
return constraint;
|
|
750
|
+
});
|
|
751
|
+
if (new Set(timingConstraints).size !==
|
|
752
|
+
timingConstraints.length) {
|
|
753
|
+
throw new InvalidConfigurationError("releaseFrameBinding.timingConstraints must not contain duplicates.");
|
|
754
|
+
}
|
|
755
|
+
const automationRecord = requireRecord(binding.automation, "releaseFrameBinding.automation");
|
|
756
|
+
const automation = {
|
|
757
|
+
ae: validateReleaseAutomationState(automationRecord.ae, "releaseFrameBinding.automation.ae"),
|
|
758
|
+
af: validateReleaseAutomationState(automationRecord.af, "releaseFrameBinding.automation.af"),
|
|
759
|
+
awb: validateReleaseAutomationState(automationRecord.awb, "releaseFrameBinding.automation.awb")
|
|
760
|
+
};
|
|
761
|
+
const whiteBalanceStateId = binding.whiteBalanceStateId ===
|
|
762
|
+
undefined
|
|
763
|
+
? undefined
|
|
764
|
+
: requireNonEmptyString(binding.whiteBalanceStateId, "releaseFrameBinding.whiteBalanceStateId");
|
|
765
|
+
const exposureRecord = requireRecord(binding.exposure, "releaseFrameBinding.exposure");
|
|
766
|
+
const parsedExposure = {
|
|
767
|
+
aperture: requirePositiveFinite(exposureRecord.aperture, "releaseFrameBinding.exposure.aperture"),
|
|
768
|
+
shutterSeconds: requirePositiveFinite(exposureRecord.shutterSeconds, "releaseFrameBinding.exposure.shutterSeconds"),
|
|
769
|
+
iso: requirePositiveFinite(exposureRecord.iso, "releaseFrameBinding.exposure.iso")
|
|
770
|
+
};
|
|
771
|
+
const parsedSeed = requireUint32(binding.stochasticSeedUint32, "releaseFrameBinding.stochasticSeedUint32");
|
|
772
|
+
if (!settingEquals(parsedExposure.aperture, exposure.aperture) ||
|
|
773
|
+
!settingEquals(parsedExposure.shutterSeconds, exposure.shutterSeconds) ||
|
|
774
|
+
!settingEquals(parsedExposure.iso, exposure.iso)) {
|
|
775
|
+
throw new InvalidConfigurationError("releaseFrameBinding.exposure must match the committed capture exposure.");
|
|
776
|
+
}
|
|
777
|
+
if (parsedSeed !==
|
|
778
|
+
stochasticSeedUint32) {
|
|
779
|
+
throw new InvalidConfigurationError("releaseFrameBinding.stochasticSeedUint32 must match the committed capture seed.");
|
|
780
|
+
}
|
|
781
|
+
const parsed = {
|
|
782
|
+
releaseSequenceVersion: RELEASE_SEQUENCE_VERSION,
|
|
783
|
+
sequenceId: requireNonEmptyString(binding.sequenceId, "releaseFrameBinding.sequenceId"),
|
|
784
|
+
releaseFrameId: requireNonEmptyString(binding.releaseFrameId, "releaseFrameBinding.releaseFrameId"),
|
|
785
|
+
frameIndex,
|
|
786
|
+
exposure: parsedExposure,
|
|
787
|
+
stochasticSeedUint32: parsedSeed,
|
|
788
|
+
exposureStartTimeSeconds: start,
|
|
789
|
+
exposureEndTimeSeconds: end,
|
|
790
|
+
sceneTimeSecondsFromSequenceStart: sceneTime,
|
|
791
|
+
startIntervalFromPreviousSeconds: interval,
|
|
792
|
+
timingConstraints,
|
|
793
|
+
focus: parseFocusPlane(binding.focus),
|
|
794
|
+
automation,
|
|
795
|
+
...(whiteBalanceStateId ===
|
|
796
|
+
undefined
|
|
797
|
+
? {}
|
|
798
|
+
: {
|
|
799
|
+
whiteBalanceStateId
|
|
800
|
+
})
|
|
801
|
+
};
|
|
802
|
+
if (parsed.releaseFrameId !==
|
|
803
|
+
releaseFrameId) {
|
|
804
|
+
throw new InvalidConfigurationError("releaseFrameBinding.releaseFrameId must match releaseFrameId.");
|
|
805
|
+
}
|
|
806
|
+
return parsed;
|
|
807
|
+
}
|
|
808
|
+
function releaseFocusMatchesOpticalFocus(releaseFocus, opticalFocus) {
|
|
809
|
+
if (releaseFocus.kind ===
|
|
810
|
+
"infinity") {
|
|
811
|
+
return (opticalFocus.kind ===
|
|
812
|
+
"infinity-focus" ||
|
|
813
|
+
(opticalFocus.kind ===
|
|
814
|
+
"supplied-working-f-number" &&
|
|
815
|
+
opticalFocus.focus.kind ===
|
|
816
|
+
"infinity"));
|
|
817
|
+
}
|
|
818
|
+
if (opticalFocus.kind ===
|
|
819
|
+
"ideal-symmetric-thin-lens") {
|
|
820
|
+
return settingEquals(opticalFocus.objectDistanceM, releaseFocus.distanceM);
|
|
821
|
+
}
|
|
822
|
+
if (opticalFocus.kind ===
|
|
823
|
+
"supplied-working-f-number" &&
|
|
824
|
+
opticalFocus.focus.kind ===
|
|
825
|
+
"finite") {
|
|
826
|
+
return settingEquals(opticalFocus.focus.objectDistanceM, releaseFocus.distanceM);
|
|
827
|
+
}
|
|
828
|
+
return false;
|
|
829
|
+
}
|
|
830
|
+
function validateCommittedWhiteBalanceState(state, binding) {
|
|
831
|
+
const parsed = state === undefined
|
|
832
|
+
? undefined
|
|
833
|
+
: parseResolvedWhiteBalanceState(state);
|
|
834
|
+
if (binding === undefined) {
|
|
835
|
+
return parsed;
|
|
836
|
+
}
|
|
837
|
+
if (binding.whiteBalanceStateId ===
|
|
838
|
+
undefined) {
|
|
839
|
+
if (parsed !== undefined) {
|
|
840
|
+
throw new InvalidConfigurationError("A release frame without whiteBalanceStateId must not receive a committed whiteBalanceState.");
|
|
841
|
+
}
|
|
842
|
+
return undefined;
|
|
843
|
+
}
|
|
844
|
+
if (parsed === undefined) {
|
|
845
|
+
throw new InvalidConfigurationError("A release frame with whiteBalanceStateId requires the committed resolved white-balance state.");
|
|
846
|
+
}
|
|
847
|
+
if (parsed.stateId !==
|
|
848
|
+
binding.whiteBalanceStateId) {
|
|
849
|
+
throw new InvalidConfigurationError("whiteBalanceState.stateId must match releaseFrameBinding.whiteBalanceStateId.");
|
|
850
|
+
}
|
|
851
|
+
if (binding.automation.awb ===
|
|
852
|
+
"locked" &&
|
|
853
|
+
!parsed.locked) {
|
|
854
|
+
throw new InvalidConfigurationError("A release frame with locked AWB requires a locked committed white-balance state.");
|
|
855
|
+
}
|
|
856
|
+
return parsed;
|
|
857
|
+
}
|
|
858
|
+
/**
|
|
859
|
+
* Commit resolved capture settings, scene time/identity, stochastic seed and optional
|
|
860
|
+
* temporal/focus/WB state by value. Later application mutations cannot alter this scientific event.
|
|
861
|
+
*
|
|
862
|
+
* The planner owns scientific stage ordering, immutable capture identity and reproducibility.
|
|
863
|
+
* Well-formed unsupported combinations produce structured blockers; malformed declarations throw. The
|
|
864
|
+
* bounded environment route executes supplied radiance through native RAW and optional output, while
|
|
865
|
+
* sample-only and attached-RAW routes retain missing upstream blockers. FNV fingerprints are
|
|
866
|
+
* reproducibility keys, not cryptographic integrity proofs.
|
|
867
|
+
* @param input - CreateProductionCaptureSnapshotInput. See the linked contract for coordinate, unit and profile binding semantics.
|
|
868
|
+
* @returns ProductionCaptureSnapshot. Return shape and scientific status are explicit; no calibration is inferred from successful execution.
|
|
869
|
+
*
|
|
870
|
+
* @see docs/API_REFERENCE.md for the root export and exact type graph.
|
|
871
|
+
*/
|
|
872
|
+
export function createProductionCaptureSnapshot(input) {
|
|
873
|
+
const sceneTime = requireFinite(input
|
|
874
|
+
.sceneTimeSecondsFromExposureStart, "sceneTimeSecondsFromExposureStart");
|
|
875
|
+
if (sceneTime < 0) {
|
|
876
|
+
throw new InvalidConfigurationError("sceneTimeSecondsFromExposureStart must be greater than or equal to zero.");
|
|
877
|
+
}
|
|
878
|
+
const releaseFrameId = requireNonEmptyString(input.releaseFrameId, "releaseFrameId");
|
|
879
|
+
const exposure = {
|
|
880
|
+
aperture: requirePositiveFinite(input.exposure.aperture, "exposure.aperture"),
|
|
881
|
+
shutterSeconds: requirePositiveFinite(input.exposure
|
|
882
|
+
.shutterSeconds, "exposure.shutterSeconds"),
|
|
883
|
+
iso: requirePositiveFinite(input.exposure.iso, "exposure.iso")
|
|
884
|
+
};
|
|
885
|
+
const releaseFrameBinding = input.releaseFrameBinding ===
|
|
886
|
+
undefined
|
|
887
|
+
? undefined
|
|
888
|
+
: validateProductionReleaseFrameBinding(input.releaseFrameBinding, releaseFrameId, exposure, requireUint32(input.stochasticSeedUint32, "stochasticSeedUint32"));
|
|
889
|
+
const whiteBalanceState = validateCommittedWhiteBalanceState(input.whiteBalanceState, releaseFrameBinding);
|
|
890
|
+
const physicalSceneSample = input.physicalSceneSample ===
|
|
891
|
+
undefined
|
|
892
|
+
? undefined
|
|
893
|
+
: validatePhysicalSceneSample(input.physicalSceneSample, sceneTime);
|
|
894
|
+
if (releaseFrameBinding !==
|
|
895
|
+
undefined &&
|
|
896
|
+
physicalSceneSample !==
|
|
897
|
+
undefined &&
|
|
898
|
+
!releaseFocusMatchesOpticalFocus(releaseFrameBinding.focus, physicalSceneSample.focus)) {
|
|
899
|
+
throw new InvalidConfigurationError("physicalSceneSample.focus must match the committed release-frame focus.");
|
|
900
|
+
}
|
|
901
|
+
const core = {
|
|
902
|
+
version: PRODUCTION_CAPTURE_SNAPSHOT_VERSION,
|
|
903
|
+
captureId: requireNonEmptyString(input.captureId, "captureId"),
|
|
904
|
+
releaseFrameId,
|
|
905
|
+
sceneStateId: requireNonEmptyString(input.sceneStateId, "sceneStateId"),
|
|
906
|
+
sceneTimeSecondsFromExposureStart: sceneTime,
|
|
907
|
+
outputStateId: requireNonEmptyString(input.outputStateId, "outputStateId"),
|
|
908
|
+
exposure,
|
|
909
|
+
stochasticSeedUint32: requireUint32(input.stochasticSeedUint32, "stochasticSeedUint32"),
|
|
910
|
+
...(releaseFrameBinding ===
|
|
911
|
+
undefined
|
|
912
|
+
? {}
|
|
913
|
+
: {
|
|
914
|
+
releaseFrameBinding
|
|
915
|
+
}),
|
|
916
|
+
...(whiteBalanceState ===
|
|
917
|
+
undefined
|
|
918
|
+
? {}
|
|
919
|
+
: {
|
|
920
|
+
whiteBalanceState
|
|
921
|
+
}),
|
|
922
|
+
...(input.temporalCapture ===
|
|
923
|
+
undefined
|
|
924
|
+
? {}
|
|
925
|
+
: {
|
|
926
|
+
temporalCapture: validateTemporalCaptureInput(input.temporalCapture, exposure.shutterSeconds)
|
|
927
|
+
}),
|
|
928
|
+
...(physicalSceneSample ===
|
|
929
|
+
undefined
|
|
930
|
+
? {}
|
|
931
|
+
: {
|
|
932
|
+
physicalSceneSample
|
|
933
|
+
})
|
|
934
|
+
};
|
|
935
|
+
const snapshot = {
|
|
936
|
+
...core,
|
|
937
|
+
fingerprint: {
|
|
938
|
+
algorithm: "fnv1a-32-non-cryptographic",
|
|
939
|
+
value: fingerprintValue(core)
|
|
940
|
+
}
|
|
941
|
+
};
|
|
942
|
+
return deepFreeze(cloneJson(snapshot));
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* Creates one immutable production snapshot directly from a committed #105
|
|
946
|
+
* release frame. The release frame owns exposure/focus/automation/seed
|
|
947
|
+
* identity; callers provide only the scene/output-local state that is not
|
|
948
|
+
* owned by the release sequence.
|
|
949
|
+
*/
|
|
950
|
+
export function createProductionCaptureSnapshotFromReleaseFrame(input) {
|
|
951
|
+
const frame = input.releaseFrame;
|
|
952
|
+
const releaseFrameBinding = {
|
|
953
|
+
releaseSequenceVersion: RELEASE_SEQUENCE_VERSION,
|
|
954
|
+
sequenceId: requireNonEmptyString(frame.sequenceId, "releaseFrame.sequenceId"),
|
|
955
|
+
releaseFrameId: requireNonEmptyString(frame.releaseFrameId, "releaseFrame.releaseFrameId"),
|
|
956
|
+
frameIndex: frame.frameIndex,
|
|
957
|
+
exposure: {
|
|
958
|
+
...frame.exposure
|
|
959
|
+
},
|
|
960
|
+
stochasticSeedUint32: frame.stochasticSeedUint32,
|
|
961
|
+
exposureStartTimeSeconds: frame.exposureStartTimeSeconds,
|
|
962
|
+
exposureEndTimeSeconds: frame.exposureEndTimeSeconds,
|
|
963
|
+
sceneTimeSecondsFromSequenceStart: frame.sceneTimeSecondsFromSequenceStart,
|
|
964
|
+
startIntervalFromPreviousSeconds: frame.startIntervalFromPreviousSeconds,
|
|
965
|
+
timingConstraints: [
|
|
966
|
+
...frame.timingConstraints
|
|
967
|
+
],
|
|
968
|
+
focus: parseFocusPlane(frame.focus),
|
|
969
|
+
automation: {
|
|
970
|
+
...frame.automation
|
|
971
|
+
},
|
|
972
|
+
...(frame.whiteBalanceStateId ===
|
|
973
|
+
undefined
|
|
974
|
+
? {}
|
|
975
|
+
: {
|
|
976
|
+
whiteBalanceStateId: frame.whiteBalanceStateId
|
|
977
|
+
})
|
|
978
|
+
};
|
|
979
|
+
return createProductionCaptureSnapshot({
|
|
980
|
+
captureId: input.captureId,
|
|
981
|
+
releaseFrameId: frame.releaseFrameId,
|
|
982
|
+
sceneStateId: input.sceneStateId,
|
|
983
|
+
sceneTimeSecondsFromExposureStart: input
|
|
984
|
+
.sceneTimeSecondsFromExposureStart,
|
|
985
|
+
outputStateId: input.outputStateId,
|
|
986
|
+
exposure: {
|
|
987
|
+
...frame.exposure
|
|
988
|
+
},
|
|
989
|
+
stochasticSeedUint32: frame.stochasticSeedUint32,
|
|
990
|
+
releaseFrameBinding,
|
|
991
|
+
...(input.whiteBalanceState ===
|
|
992
|
+
undefined
|
|
993
|
+
? {}
|
|
994
|
+
: {
|
|
995
|
+
whiteBalanceState: input.whiteBalanceState
|
|
996
|
+
}),
|
|
997
|
+
...(input.physicalSceneSample ===
|
|
998
|
+
undefined
|
|
999
|
+
? {}
|
|
1000
|
+
: {
|
|
1001
|
+
physicalSceneSample: input.physicalSceneSample
|
|
1002
|
+
}),
|
|
1003
|
+
...(input.temporalCapture ===
|
|
1004
|
+
undefined
|
|
1005
|
+
? {}
|
|
1006
|
+
: {
|
|
1007
|
+
temporalCapture: input.temporalCapture
|
|
1008
|
+
})
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
/**
|
|
1012
|
+
* Validate an untrusted declaration and return the normalized typed contract. Unknown enum values,
|
|
1013
|
+
* missing required fields and incompatible scientific data fail at this boundary.
|
|
1014
|
+
*
|
|
1015
|
+
* The planner owns scientific stage ordering, immutable capture identity and reproducibility.
|
|
1016
|
+
* Well-formed unsupported combinations produce structured blockers; malformed declarations throw. The
|
|
1017
|
+
* bounded environment route executes supplied radiance through native RAW and optional output, while
|
|
1018
|
+
* sample-only and attached-RAW routes retain missing upstream blockers. FNV fingerprints are
|
|
1019
|
+
* reproducibility keys, not cryptographic integrity proofs.
|
|
1020
|
+
* @param value - unknown. Treated as untrusted data; static typing alone is not validation.
|
|
1021
|
+
* @returns ProductionCaptureSnapshot. Return shape and scientific status are explicit; no calibration is inferred from successful execution.
|
|
1022
|
+
*
|
|
1023
|
+
* @see docs/API_REFERENCE.md for the root export and exact type graph.
|
|
1024
|
+
*/
|
|
1025
|
+
export function parseProductionCaptureSnapshot(value) {
|
|
1026
|
+
const record = requireRecord(value, "productionCaptureSnapshot");
|
|
1027
|
+
if (record.version !==
|
|
1028
|
+
PRODUCTION_CAPTURE_SNAPSHOT_VERSION) {
|
|
1029
|
+
throw new InvalidConfigurationError('productionCaptureSnapshot.version must be "' +
|
|
1030
|
+
PRODUCTION_CAPTURE_SNAPSHOT_VERSION +
|
|
1031
|
+
'".');
|
|
1032
|
+
}
|
|
1033
|
+
const parsed = createProductionCaptureSnapshot({
|
|
1034
|
+
captureId: record.captureId,
|
|
1035
|
+
releaseFrameId: record.releaseFrameId,
|
|
1036
|
+
sceneStateId: record.sceneStateId,
|
|
1037
|
+
sceneTimeSecondsFromExposureStart: record
|
|
1038
|
+
.sceneTimeSecondsFromExposureStart,
|
|
1039
|
+
outputStateId: record.outputStateId,
|
|
1040
|
+
exposure: record.exposure,
|
|
1041
|
+
stochasticSeedUint32: record
|
|
1042
|
+
.stochasticSeedUint32,
|
|
1043
|
+
...(record
|
|
1044
|
+
.releaseFrameBinding ===
|
|
1045
|
+
undefined
|
|
1046
|
+
? {}
|
|
1047
|
+
: {
|
|
1048
|
+
releaseFrameBinding: record
|
|
1049
|
+
.releaseFrameBinding
|
|
1050
|
+
}),
|
|
1051
|
+
...(record
|
|
1052
|
+
.whiteBalanceState ===
|
|
1053
|
+
undefined
|
|
1054
|
+
? {}
|
|
1055
|
+
: {
|
|
1056
|
+
whiteBalanceState: record
|
|
1057
|
+
.whiteBalanceState
|
|
1058
|
+
}),
|
|
1059
|
+
...(record
|
|
1060
|
+
.physicalSceneSample ===
|
|
1061
|
+
undefined
|
|
1062
|
+
? {}
|
|
1063
|
+
: {
|
|
1064
|
+
physicalSceneSample: record
|
|
1065
|
+
.physicalSceneSample
|
|
1066
|
+
}),
|
|
1067
|
+
...(record.temporalCapture ===
|
|
1068
|
+
undefined
|
|
1069
|
+
? {}
|
|
1070
|
+
: {
|
|
1071
|
+
temporalCapture: record
|
|
1072
|
+
.temporalCapture
|
|
1073
|
+
})
|
|
1074
|
+
});
|
|
1075
|
+
const supplied = requireRecord(record.fingerprint, "productionCaptureSnapshot.fingerprint");
|
|
1076
|
+
if (supplied.algorithm !==
|
|
1077
|
+
"fnv1a-32-non-cryptographic" ||
|
|
1078
|
+
supplied.value !==
|
|
1079
|
+
parsed.fingerprint.value) {
|
|
1080
|
+
throw new InvalidConfigurationError("productionCaptureSnapshot fingerprint does not match its parsed content.");
|
|
1081
|
+
}
|
|
1082
|
+
return parsed;
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Expand the requested graph through hard upstream dependencies in the engine
|
|
1086
|
+
* contract. Shared/coupled effects remain distinct from serialization order.
|
|
1087
|
+
* Dependency presence is not execution: deriveStagePlan still assesses declared
|
|
1088
|
+
* inputs, renderer capabilities and scientific support for every expanded stage.
|
|
1089
|
+
*/
|
|
1090
|
+
function expandRequiredStages(fidelity) {
|
|
1091
|
+
const contract = getImageFormationContract();
|
|
1092
|
+
const byId = new Map(contract.stages.map((stage) => [
|
|
1093
|
+
stage.id,
|
|
1094
|
+
stage
|
|
1095
|
+
]));
|
|
1096
|
+
const effects = new Map(contract.effectPlacements.map((effect) => [
|
|
1097
|
+
effect.id,
|
|
1098
|
+
effect
|
|
1099
|
+
]));
|
|
1100
|
+
const required = new Set();
|
|
1101
|
+
const addStage = (stageId) => {
|
|
1102
|
+
if (required.has(stageId)) {
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
required.add(stageId);
|
|
1106
|
+
const stage = byId.get(stageId);
|
|
1107
|
+
if (stage === undefined) {
|
|
1108
|
+
throw new InvalidConfigurationError("Image-formation contract is missing stage " +
|
|
1109
|
+
stageId +
|
|
1110
|
+
".");
|
|
1111
|
+
}
|
|
1112
|
+
for (const upstream of stage.requiredUpstreamStages) {
|
|
1113
|
+
addStage(upstream);
|
|
1114
|
+
}
|
|
1115
|
+
};
|
|
1116
|
+
for (const stageId of fidelity.requiredStages) {
|
|
1117
|
+
addStage(stageId);
|
|
1118
|
+
}
|
|
1119
|
+
for (const requiredEffect of fidelity.requiredEffects) {
|
|
1120
|
+
const placement = effects.get(requiredEffect.effectId);
|
|
1121
|
+
if (placement === undefined) {
|
|
1122
|
+
throw new InvalidConfigurationError("Image-formation contract is missing effect " +
|
|
1123
|
+
requiredEffect.effectId +
|
|
1124
|
+
".");
|
|
1125
|
+
}
|
|
1126
|
+
addStage(placement.primaryStage);
|
|
1127
|
+
for (const coupled of placement.coupledStages) {
|
|
1128
|
+
addStage(coupled);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
return required;
|
|
1132
|
+
}
|
|
1133
|
+
function validateSnapshotAgainstContext(prepared, snapshot) {
|
|
1134
|
+
if (prepared.outputGeometryProfileId !==
|
|
1135
|
+
snapshot.outputStateId) {
|
|
1136
|
+
throw new InvalidScientificInputError("captureSnapshot.outputStateId must match the prepared outputGeometryProfileId in plan schema " +
|
|
1137
|
+
PRODUCTION_IMAGE_FORMATION_PLAN_VERSION +
|
|
1138
|
+
".");
|
|
1139
|
+
}
|
|
1140
|
+
validateSetting(snapshot.exposure.aperture, prepared
|
|
1141
|
+
.equipmentCapabilities
|
|
1142
|
+
.aperture
|
|
1143
|
+
.widestAvailableFNumber, prepared
|
|
1144
|
+
.equipmentCapabilities
|
|
1145
|
+
.aperture
|
|
1146
|
+
.narrowestAvailableFNumber, prepared
|
|
1147
|
+
.equipmentCapabilities
|
|
1148
|
+
.aperture.settingGrid, "captureSnapshot.exposure.aperture");
|
|
1149
|
+
validateSetting(snapshot.exposure
|
|
1150
|
+
.shutterSeconds, prepared
|
|
1151
|
+
.equipmentCapabilities
|
|
1152
|
+
.shutter.minimumSeconds, prepared
|
|
1153
|
+
.equipmentCapabilities
|
|
1154
|
+
.shutter.maximumSeconds, prepared
|
|
1155
|
+
.equipmentCapabilities
|
|
1156
|
+
.shutter.settingGrid, "captureSnapshot.exposure.shutterSeconds");
|
|
1157
|
+
validateSetting(snapshot.exposure.iso, prepared
|
|
1158
|
+
.equipmentCapabilities
|
|
1159
|
+
.iso.minimum, prepared
|
|
1160
|
+
.equipmentCapabilities
|
|
1161
|
+
.iso.maximum, prepared
|
|
1162
|
+
.equipmentCapabilities
|
|
1163
|
+
.iso.settingGrid, "captureSnapshot.exposure.iso");
|
|
1164
|
+
const sample = snapshot
|
|
1165
|
+
.physicalSceneSample;
|
|
1166
|
+
if (sample !== undefined) {
|
|
1167
|
+
if (sample.sceneRadianceRequest
|
|
1168
|
+
.sceneId !==
|
|
1169
|
+
prepared.sceneId ||
|
|
1170
|
+
sample.sceneRadianceResult
|
|
1171
|
+
.sceneId !==
|
|
1172
|
+
prepared.sceneId) {
|
|
1173
|
+
throw new InvalidScientificInputError("Physical scene sample sceneId must match the prepared sceneId.");
|
|
1174
|
+
}
|
|
1175
|
+
if (sample.sceneRadianceRequest
|
|
1176
|
+
.providerProfileId !==
|
|
1177
|
+
prepared
|
|
1178
|
+
.sceneRadianceProviderProfileId ||
|
|
1179
|
+
sample.sceneRadianceResult
|
|
1180
|
+
.providerProfileId !==
|
|
1181
|
+
prepared
|
|
1182
|
+
.sceneRadianceProviderProfileId) {
|
|
1183
|
+
throw new InvalidScientificInputError("Physical scene sample providerProfileId must match the prepared scene-radiance provider.");
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
function addBlocker(blockers, blocker) {
|
|
1188
|
+
if (!blockers.some((existing) => canonicalStringify(existing) ===
|
|
1189
|
+
canonicalStringify(blocker))) {
|
|
1190
|
+
blockers.push(blocker);
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
function evaluateRendererRequirements(prepared, requiredStages, blockers) {
|
|
1194
|
+
const renderer = prepared.renderer;
|
|
1195
|
+
const fidelity = prepared.fidelity;
|
|
1196
|
+
if (fidelity.rendererRequirements
|
|
1197
|
+
.spectral ===
|
|
1198
|
+
"wavelength-resolved" &&
|
|
1199
|
+
renderer.spectralCapability !==
|
|
1200
|
+
"wavelength-resolved") {
|
|
1201
|
+
addBlocker(blockers, {
|
|
1202
|
+
code: "renderer-spectral-capability-insufficient",
|
|
1203
|
+
message: "Requested fidelity requires wavelength-resolved renderer support."
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
if (fidelity.rendererRequirements
|
|
1207
|
+
.sensorDomainProcessing &&
|
|
1208
|
+
!renderer
|
|
1209
|
+
.sensorDomainProcessing) {
|
|
1210
|
+
addBlocker(blockers, {
|
|
1211
|
+
code: "renderer-sensor-domain-processing-unavailable",
|
|
1212
|
+
message: "Requested fidelity requires renderer sensor-domain processing support."
|
|
1213
|
+
});
|
|
1214
|
+
}
|
|
1215
|
+
if (fidelity.rendererRequirements
|
|
1216
|
+
.depth === "per-layer" &&
|
|
1217
|
+
renderer.depthCapability !==
|
|
1218
|
+
"per-layer") {
|
|
1219
|
+
addBlocker(blockers, {
|
|
1220
|
+
code: "renderer-depth-capability-insufficient",
|
|
1221
|
+
message: "Requested fidelity requires per-layer depth capability."
|
|
1222
|
+
});
|
|
1223
|
+
}
|
|
1224
|
+
if (!renderer.inverseFieldMapping &&
|
|
1225
|
+
[...requiredStages].some((stageId) => stageId ===
|
|
1226
|
+
"lens-field-pupil-evaluation" ||
|
|
1227
|
+
stageId ===
|
|
1228
|
+
"field-wavelength-psf")) {
|
|
1229
|
+
addBlocker(blockers, {
|
|
1230
|
+
code: "renderer-inverse-field-mapping-required",
|
|
1231
|
+
message: "The requested lens/field plan requires inverse field mapping semantics."
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
if (renderer.alphaRepresentation !==
|
|
1235
|
+
"premultiplied") {
|
|
1236
|
+
addBlocker(blockers, {
|
|
1237
|
+
code: "renderer-premultiplied-alpha-required",
|
|
1238
|
+
message: "The image-formation renderer contract requires premultiplied alpha."
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
if (!renderer
|
|
1242
|
+
.preservesDepthOrderAcrossWarps) {
|
|
1243
|
+
addBlocker(blockers, {
|
|
1244
|
+
code: "renderer-depth-order-preservation-required",
|
|
1245
|
+
message: "The image-formation renderer contract requires depth-order preservation across warps."
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
function requestedEffectMap(fidelity) {
|
|
1250
|
+
return new Map(fidelity.requiredEffects.map((effect) => [
|
|
1251
|
+
effect.effectId,
|
|
1252
|
+
effect
|
|
1253
|
+
]));
|
|
1254
|
+
}
|
|
1255
|
+
function hasActiveUndeclaredFieldThroughput(prepared, snapshot) {
|
|
1256
|
+
const sample = snapshot.physicalSceneSample;
|
|
1257
|
+
if (sample === undefined ||
|
|
1258
|
+
sample.fieldThroughput.kind ===
|
|
1259
|
+
"unity") {
|
|
1260
|
+
return false;
|
|
1261
|
+
}
|
|
1262
|
+
return !prepared.fidelity
|
|
1263
|
+
.requiredEffects.some((effect) => effect.effectId ===
|
|
1264
|
+
"illumination-vignetting");
|
|
1265
|
+
}
|
|
1266
|
+
function angularVelocityIsZero(velocity) {
|
|
1267
|
+
return (velocity.pitch === 0 &&
|
|
1268
|
+
velocity.yaw === 0 &&
|
|
1269
|
+
velocity.roll === 0);
|
|
1270
|
+
}
|
|
1271
|
+
function computeTemporalResult(prepared, snapshot, requiredStages, blockers) {
|
|
1272
|
+
if (!requiredStages.has("temporal-exposure-readout")) {
|
|
1273
|
+
return undefined;
|
|
1274
|
+
}
|
|
1275
|
+
const temporal = snapshot.temporalCapture;
|
|
1276
|
+
if (temporal === undefined) {
|
|
1277
|
+
addBlocker(blockers, {
|
|
1278
|
+
code: "missing-temporal-capture-input",
|
|
1279
|
+
stageId: "temporal-exposure-readout",
|
|
1280
|
+
message: "Temporal exposure/readout planning requires immutable temporal capture input."
|
|
1281
|
+
});
|
|
1282
|
+
return undefined;
|
|
1283
|
+
}
|
|
1284
|
+
const requestedEffects = requestedEffectMap(prepared.fidelity);
|
|
1285
|
+
if (requestedEffects.has("spatial-camera-rotation") &&
|
|
1286
|
+
temporal.rotation === undefined) {
|
|
1287
|
+
addBlocker(blockers, {
|
|
1288
|
+
code: "missing-camera-rotation-model",
|
|
1289
|
+
stageId: "temporal-exposure-readout",
|
|
1290
|
+
effectId: "spatial-camera-rotation",
|
|
1291
|
+
message: "Requested spatial camera rotation requires an explicit capture rotation model."
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
if (requestedEffects.has("rolling-readout") &&
|
|
1295
|
+
temporal.readout === undefined) {
|
|
1296
|
+
addBlocker(blockers, {
|
|
1297
|
+
code: "missing-sensor-readout-timing",
|
|
1298
|
+
stageId: "temporal-exposure-readout",
|
|
1299
|
+
effectId: "rolling-readout",
|
|
1300
|
+
message: "Requested rolling-readout semantics require an explicit sensor readout declaration."
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
const rotationRequested = requestedEffects.has("spatial-camera-rotation");
|
|
1304
|
+
if (rotationRequested &&
|
|
1305
|
+
temporal.rotation !== undefined) {
|
|
1306
|
+
const rendererTemporal = prepared.renderer
|
|
1307
|
+
.temporalSampling;
|
|
1308
|
+
if (rendererTemporal.kind ===
|
|
1309
|
+
"none" ||
|
|
1310
|
+
rendererTemporal
|
|
1311
|
+
.maximumSamples <
|
|
1312
|
+
temporal.rotation
|
|
1313
|
+
.temporalSampleCount) {
|
|
1314
|
+
addBlocker(blockers, {
|
|
1315
|
+
code: "renderer-temporal-sampling-insufficient",
|
|
1316
|
+
stageId: "temporal-exposure-readout",
|
|
1317
|
+
effectId: "spatial-camera-rotation",
|
|
1318
|
+
message: "Renderer temporal-sampling capability is insufficient for the committed rotation quadrature sample count."
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
try {
|
|
1323
|
+
const exposureWindows = calculateCaptureExposureWindows(temporal
|
|
1324
|
+
.exposureWindowInput).value;
|
|
1325
|
+
const sensorReadoutTiming = temporal.readout === undefined
|
|
1326
|
+
? undefined
|
|
1327
|
+
: calculateSensorReadoutTiming({
|
|
1328
|
+
nativeRaster: temporal
|
|
1329
|
+
.exposureWindowInput
|
|
1330
|
+
.nativeRaster,
|
|
1331
|
+
...(temporal
|
|
1332
|
+
.exposureWindowInput
|
|
1333
|
+
.activeCaptureRect ===
|
|
1334
|
+
undefined
|
|
1335
|
+
? {}
|
|
1336
|
+
: {
|
|
1337
|
+
activeCaptureRect: temporal
|
|
1338
|
+
.exposureWindowInput
|
|
1339
|
+
.activeCaptureRect
|
|
1340
|
+
}),
|
|
1341
|
+
shutterMechanism: temporal
|
|
1342
|
+
.exposureWindowInput
|
|
1343
|
+
.shutterMechanism,
|
|
1344
|
+
readout: temporal.readout,
|
|
1345
|
+
...(temporal
|
|
1346
|
+
.exposureWindowInput
|
|
1347
|
+
.samplePointsNative ===
|
|
1348
|
+
undefined
|
|
1349
|
+
? {}
|
|
1350
|
+
: {
|
|
1351
|
+
samplePointsNative: temporal
|
|
1352
|
+
.exposureWindowInput
|
|
1353
|
+
.samplePointsNative
|
|
1354
|
+
})
|
|
1355
|
+
}).value;
|
|
1356
|
+
const rotationQuadrature = !rotationRequested ||
|
|
1357
|
+
temporal.rotation === undefined
|
|
1358
|
+
? undefined
|
|
1359
|
+
: calculateCaptureRotationTemporalQuadrature({
|
|
1360
|
+
...temporal
|
|
1361
|
+
.exposureWindowInput,
|
|
1362
|
+
imagingArea: temporal.imagingArea,
|
|
1363
|
+
focalLengthMm: prepared
|
|
1364
|
+
.equipmentCapabilities
|
|
1365
|
+
.selectedFocalLengthMm,
|
|
1366
|
+
...(temporal.rotation
|
|
1367
|
+
.focusDistanceM ===
|
|
1368
|
+
undefined
|
|
1369
|
+
? {}
|
|
1370
|
+
: {
|
|
1371
|
+
focusDistanceM: temporal.rotation
|
|
1372
|
+
.focusDistanceM
|
|
1373
|
+
}),
|
|
1374
|
+
angularVelocityRadPerSec: temporal.rotation
|
|
1375
|
+
.angularVelocityRadPerSec,
|
|
1376
|
+
orientation: temporal.orientation,
|
|
1377
|
+
temporalSampleCount: temporal.rotation
|
|
1378
|
+
.temporalSampleCount,
|
|
1379
|
+
samplePointsNative: temporal
|
|
1380
|
+
.exposureWindowInput
|
|
1381
|
+
.samplePointsNative
|
|
1382
|
+
}).value;
|
|
1383
|
+
return {
|
|
1384
|
+
exposureWindows,
|
|
1385
|
+
...(sensorReadoutTiming ===
|
|
1386
|
+
undefined
|
|
1387
|
+
? {}
|
|
1388
|
+
: {
|
|
1389
|
+
sensorReadoutTiming
|
|
1390
|
+
}),
|
|
1391
|
+
...(rotationQuadrature ===
|
|
1392
|
+
undefined
|
|
1393
|
+
? {}
|
|
1394
|
+
: {
|
|
1395
|
+
rotationQuadrature
|
|
1396
|
+
}),
|
|
1397
|
+
exposureTimeReference: "first-opening-boundary-phase",
|
|
1398
|
+
readoutExposureSynchronization: "not-assumed",
|
|
1399
|
+
sensorReadoutTimingRemainsSeparate: true,
|
|
1400
|
+
temporalRadianceIntegrated: false
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
catch (error) {
|
|
1404
|
+
if (error instanceof
|
|
1405
|
+
InvalidConfigurationError ||
|
|
1406
|
+
error instanceof
|
|
1407
|
+
InvalidScientificInputError) {
|
|
1408
|
+
addBlocker(blockers, {
|
|
1409
|
+
code: "temporal-evaluation-blocked",
|
|
1410
|
+
stageId: "temporal-exposure-readout",
|
|
1411
|
+
message: error.message
|
|
1412
|
+
});
|
|
1413
|
+
return undefined;
|
|
1414
|
+
}
|
|
1415
|
+
throw error;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
function deriveEffectPlan(prepared, snapshot, temporalResult, blockers) {
|
|
1419
|
+
const contract = getImageFormationContract();
|
|
1420
|
+
const requested = requestedEffectMap(prepared.fidelity);
|
|
1421
|
+
const rendererEffects = new Set(prepared.renderer
|
|
1422
|
+
.supportedEffects);
|
|
1423
|
+
return contract
|
|
1424
|
+
.effectPlacements.map((placement) => {
|
|
1425
|
+
const request = requested.get(placement.id);
|
|
1426
|
+
if (request === undefined) {
|
|
1427
|
+
return {
|
|
1428
|
+
effectId: placement.id,
|
|
1429
|
+
primaryStage: placement.primaryStage,
|
|
1430
|
+
coupledStages: [
|
|
1431
|
+
...placement
|
|
1432
|
+
.coupledStages
|
|
1433
|
+
],
|
|
1434
|
+
state: "omitted-by-fidelity",
|
|
1435
|
+
scientificStatus: "not-applicable",
|
|
1436
|
+
requiredByFidelity: false,
|
|
1437
|
+
blockerCodes: []
|
|
1438
|
+
};
|
|
1439
|
+
}
|
|
1440
|
+
if (!COMPOSER_SUPPORTED_EFFECTS.has(placement.id)) {
|
|
1441
|
+
const blocker = {
|
|
1442
|
+
code: "engine-effect-not-composed",
|
|
1443
|
+
effectId: placement.id,
|
|
1444
|
+
message: "Production plan schema " +
|
|
1445
|
+
PRODUCTION_IMAGE_FORMATION_PLAN_VERSION +
|
|
1446
|
+
" does not yet compose required effect " +
|
|
1447
|
+
placement.id +
|
|
1448
|
+
"."
|
|
1449
|
+
};
|
|
1450
|
+
addBlocker(blockers, blocker);
|
|
1451
|
+
return {
|
|
1452
|
+
effectId: placement.id,
|
|
1453
|
+
primaryStage: placement.primaryStage,
|
|
1454
|
+
coupledStages: [
|
|
1455
|
+
...placement
|
|
1456
|
+
.coupledStages
|
|
1457
|
+
],
|
|
1458
|
+
state: "unsupported",
|
|
1459
|
+
scientificStatus: "not-applicable",
|
|
1460
|
+
requiredByFidelity: true,
|
|
1461
|
+
modelId: request.modelId,
|
|
1462
|
+
modelVersion: request.modelVersion,
|
|
1463
|
+
blockerCodes: [
|
|
1464
|
+
blocker.code
|
|
1465
|
+
]
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
if (!rendererEffects.has(placement.id)) {
|
|
1469
|
+
const blocker = {
|
|
1470
|
+
code: "renderer-effect-unsupported",
|
|
1471
|
+
effectId: placement.id,
|
|
1472
|
+
message: "Renderer does not declare support for required effect " +
|
|
1473
|
+
placement.id +
|
|
1474
|
+
"."
|
|
1475
|
+
};
|
|
1476
|
+
addBlocker(blockers, blocker);
|
|
1477
|
+
return {
|
|
1478
|
+
effectId: placement.id,
|
|
1479
|
+
primaryStage: placement.primaryStage,
|
|
1480
|
+
coupledStages: [
|
|
1481
|
+
...placement
|
|
1482
|
+
.coupledStages
|
|
1483
|
+
],
|
|
1484
|
+
state: "blocked",
|
|
1485
|
+
scientificStatus: "not-applicable",
|
|
1486
|
+
requiredByFidelity: true,
|
|
1487
|
+
modelId: request.modelId,
|
|
1488
|
+
modelVersion: request.modelVersion,
|
|
1489
|
+
blockerCodes: [
|
|
1490
|
+
blocker.code
|
|
1491
|
+
]
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
if (placement.id ===
|
|
1495
|
+
"illumination-vignetting") {
|
|
1496
|
+
const field = snapshot
|
|
1497
|
+
.physicalSceneSample
|
|
1498
|
+
?.fieldThroughput;
|
|
1499
|
+
if (field === undefined) {
|
|
1500
|
+
const blocker = {
|
|
1501
|
+
code: "missing-physical-scene-sample",
|
|
1502
|
+
effectId: placement.id,
|
|
1503
|
+
message: "Illumination-vignetting planning requires a physical scene sample with explicit field throughput."
|
|
1504
|
+
};
|
|
1505
|
+
addBlocker(blockers, blocker);
|
|
1506
|
+
return {
|
|
1507
|
+
effectId: placement.id,
|
|
1508
|
+
primaryStage: placement.primaryStage,
|
|
1509
|
+
coupledStages: [],
|
|
1510
|
+
state: "blocked",
|
|
1511
|
+
scientificStatus: "not-applicable",
|
|
1512
|
+
requiredByFidelity: true,
|
|
1513
|
+
modelId: request.modelId,
|
|
1514
|
+
modelVersion: request.modelVersion,
|
|
1515
|
+
blockerCodes: [
|
|
1516
|
+
blocker.code
|
|
1517
|
+
]
|
|
1518
|
+
};
|
|
1519
|
+
}
|
|
1520
|
+
const modeledZero = field.kind ===
|
|
1521
|
+
"unity" ||
|
|
1522
|
+
field.result
|
|
1523
|
+
.linearThroughputFactor ===
|
|
1524
|
+
1;
|
|
1525
|
+
return {
|
|
1526
|
+
effectId: placement.id,
|
|
1527
|
+
primaryStage: placement.primaryStage,
|
|
1528
|
+
coupledStages: [],
|
|
1529
|
+
state: modeledZero
|
|
1530
|
+
? "modeled-zero"
|
|
1531
|
+
: "active",
|
|
1532
|
+
scientificStatus: "approximation",
|
|
1533
|
+
requiredByFidelity: true,
|
|
1534
|
+
modelId: request.modelId,
|
|
1535
|
+
modelVersion: request.modelVersion,
|
|
1536
|
+
blockerCodes: []
|
|
1537
|
+
};
|
|
1538
|
+
}
|
|
1539
|
+
if (placement.id ===
|
|
1540
|
+
"spatial-camera-rotation") {
|
|
1541
|
+
const rotation = snapshot.temporalCapture
|
|
1542
|
+
?.rotation;
|
|
1543
|
+
if (rotation === undefined) {
|
|
1544
|
+
const blocker = {
|
|
1545
|
+
code: "missing-camera-rotation-model",
|
|
1546
|
+
effectId: placement.id,
|
|
1547
|
+
stageId: "temporal-exposure-readout",
|
|
1548
|
+
message: "Spatial camera rotation requires committed rotation input."
|
|
1549
|
+
};
|
|
1550
|
+
addBlocker(blockers, blocker);
|
|
1551
|
+
return {
|
|
1552
|
+
effectId: placement.id,
|
|
1553
|
+
primaryStage: placement.primaryStage,
|
|
1554
|
+
coupledStages: [
|
|
1555
|
+
...placement
|
|
1556
|
+
.coupledStages
|
|
1557
|
+
],
|
|
1558
|
+
state: "blocked",
|
|
1559
|
+
scientificStatus: "not-applicable",
|
|
1560
|
+
requiredByFidelity: true,
|
|
1561
|
+
modelId: request.modelId,
|
|
1562
|
+
modelVersion: request.modelVersion,
|
|
1563
|
+
blockerCodes: [
|
|
1564
|
+
blocker.code
|
|
1565
|
+
]
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1568
|
+
const blocked = temporalResult
|
|
1569
|
+
?.rotationQuadrature ===
|
|
1570
|
+
undefined &&
|
|
1571
|
+
!angularVelocityIsZero(rotation
|
|
1572
|
+
.angularVelocityRadPerSec);
|
|
1573
|
+
return {
|
|
1574
|
+
effectId: placement.id,
|
|
1575
|
+
primaryStage: placement.primaryStage,
|
|
1576
|
+
coupledStages: [
|
|
1577
|
+
...placement
|
|
1578
|
+
.coupledStages
|
|
1579
|
+
],
|
|
1580
|
+
state: blocked
|
|
1581
|
+
? "blocked"
|
|
1582
|
+
: angularVelocityIsZero(rotation
|
|
1583
|
+
.angularVelocityRadPerSec)
|
|
1584
|
+
? "modeled-zero"
|
|
1585
|
+
: "active",
|
|
1586
|
+
scientificStatus: blocked
|
|
1587
|
+
? "not-applicable"
|
|
1588
|
+
: "approximation",
|
|
1589
|
+
requiredByFidelity: true,
|
|
1590
|
+
modelId: request.modelId,
|
|
1591
|
+
modelVersion: request.modelVersion,
|
|
1592
|
+
blockerCodes: blocked
|
|
1593
|
+
? blockers
|
|
1594
|
+
.filter((blocker) => blocker.effectId ===
|
|
1595
|
+
placement.id ||
|
|
1596
|
+
blocker.stageId ===
|
|
1597
|
+
placement
|
|
1598
|
+
.primaryStage)
|
|
1599
|
+
.map((blocker) => blocker.code)
|
|
1600
|
+
: []
|
|
1601
|
+
};
|
|
1602
|
+
}
|
|
1603
|
+
if (placement.id ===
|
|
1604
|
+
"rolling-readout") {
|
|
1605
|
+
const timing = temporalResult
|
|
1606
|
+
?.sensorReadoutTiming;
|
|
1607
|
+
if (timing === undefined) {
|
|
1608
|
+
const blocker = {
|
|
1609
|
+
code: "missing-sensor-readout-timing",
|
|
1610
|
+
effectId: placement.id,
|
|
1611
|
+
stageId: "temporal-exposure-readout",
|
|
1612
|
+
message: "Rolling-readout effect requires resolved sensor readout timing."
|
|
1613
|
+
};
|
|
1614
|
+
addBlocker(blockers, blocker);
|
|
1615
|
+
return {
|
|
1616
|
+
effectId: placement.id,
|
|
1617
|
+
primaryStage: placement.primaryStage,
|
|
1618
|
+
coupledStages: [
|
|
1619
|
+
...placement
|
|
1620
|
+
.coupledStages
|
|
1621
|
+
],
|
|
1622
|
+
state: "blocked",
|
|
1623
|
+
scientificStatus: "not-applicable",
|
|
1624
|
+
requiredByFidelity: true,
|
|
1625
|
+
modelId: request.modelId,
|
|
1626
|
+
modelVersion: request.modelVersion,
|
|
1627
|
+
blockerCodes: [
|
|
1628
|
+
blocker.code
|
|
1629
|
+
]
|
|
1630
|
+
};
|
|
1631
|
+
}
|
|
1632
|
+
const modeledZero = timing.readoutMode ===
|
|
1633
|
+
"global" ||
|
|
1634
|
+
timing
|
|
1635
|
+
.maximumSpatialSamplingSkewSeconds ===
|
|
1636
|
+
0;
|
|
1637
|
+
return {
|
|
1638
|
+
effectId: placement.id,
|
|
1639
|
+
primaryStage: placement.primaryStage,
|
|
1640
|
+
coupledStages: [
|
|
1641
|
+
...placement
|
|
1642
|
+
.coupledStages
|
|
1643
|
+
],
|
|
1644
|
+
state: modeledZero
|
|
1645
|
+
? "modeled-zero"
|
|
1646
|
+
: "active",
|
|
1647
|
+
scientificStatus: "approximation",
|
|
1648
|
+
requiredByFidelity: true,
|
|
1649
|
+
modelId: request.modelId,
|
|
1650
|
+
modelVersion: request.modelVersion,
|
|
1651
|
+
blockerCodes: []
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1654
|
+
throw new InvalidConfigurationError("Unexpected composed effect " +
|
|
1655
|
+
placement.id +
|
|
1656
|
+
".");
|
|
1657
|
+
});
|
|
1658
|
+
}
|
|
1659
|
+
function computePhysicalResult(prepared, snapshot, requiredStages, blockers) {
|
|
1660
|
+
if (!requiredStages.has("lens-field-pupil-evaluation")) {
|
|
1661
|
+
return undefined;
|
|
1662
|
+
}
|
|
1663
|
+
if (prepared.opticalBridgeProfile ===
|
|
1664
|
+
undefined) {
|
|
1665
|
+
addBlocker(blockers, {
|
|
1666
|
+
code: "missing-optical-bridge-profile",
|
|
1667
|
+
stageId: "lens-field-pupil-evaluation",
|
|
1668
|
+
message: "Physical lens-throughput planning requires a prepared #110 optical bridge profile."
|
|
1669
|
+
});
|
|
1670
|
+
return undefined;
|
|
1671
|
+
}
|
|
1672
|
+
const sample = snapshot
|
|
1673
|
+
.physicalSceneSample;
|
|
1674
|
+
if (sample === undefined) {
|
|
1675
|
+
addBlocker(blockers, {
|
|
1676
|
+
code: "missing-physical-scene-sample",
|
|
1677
|
+
stageId: "lens-field-pupil-evaluation",
|
|
1678
|
+
message: "Physical lens-throughput planning requires one immutable scene-radiance/field sample."
|
|
1679
|
+
});
|
|
1680
|
+
return undefined;
|
|
1681
|
+
}
|
|
1682
|
+
if (hasActiveUndeclaredFieldThroughput(prepared, snapshot)) {
|
|
1683
|
+
addBlocker(blockers, {
|
|
1684
|
+
code: "undeclared-field-throughput-effect",
|
|
1685
|
+
stageId: "lens-field-pupil-evaluation",
|
|
1686
|
+
effectId: "illumination-vignetting",
|
|
1687
|
+
message: "A non-unity field-throughput input was supplied without requesting illumination-vignetting in the fidelity profile."
|
|
1688
|
+
});
|
|
1689
|
+
return undefined;
|
|
1690
|
+
}
|
|
1691
|
+
try {
|
|
1692
|
+
return calculateSceneRadianceToSensorIrradiance({
|
|
1693
|
+
sceneRadianceRequest: sample
|
|
1694
|
+
.sceneRadianceRequest,
|
|
1695
|
+
sceneRadianceResult: sample
|
|
1696
|
+
.sceneRadianceResult,
|
|
1697
|
+
profile: prepared
|
|
1698
|
+
.opticalBridgeProfile,
|
|
1699
|
+
focalLengthMm: prepared
|
|
1700
|
+
.equipmentCapabilities
|
|
1701
|
+
.selectedFocalLengthMm,
|
|
1702
|
+
nominalFNumber: snapshot.exposure
|
|
1703
|
+
.aperture,
|
|
1704
|
+
focus: sample.focus,
|
|
1705
|
+
imagePointMm: sample.imagePointMm,
|
|
1706
|
+
fieldThroughput: sample.fieldThroughput,
|
|
1707
|
+
...(sample.frontOfLensFilters ===
|
|
1708
|
+
undefined
|
|
1709
|
+
? {}
|
|
1710
|
+
: {
|
|
1711
|
+
frontOfLensFilters: sample.frontOfLensFilters
|
|
1712
|
+
})
|
|
1713
|
+
}).value;
|
|
1714
|
+
}
|
|
1715
|
+
catch (error) {
|
|
1716
|
+
if (error instanceof
|
|
1717
|
+
InvalidConfigurationError ||
|
|
1718
|
+
error instanceof
|
|
1719
|
+
InvalidScientificInputError) {
|
|
1720
|
+
addBlocker(blockers, {
|
|
1721
|
+
code: "physical-radiometry-evaluation-blocked",
|
|
1722
|
+
stageId: "lens-field-pupil-evaluation",
|
|
1723
|
+
message: error.message
|
|
1724
|
+
});
|
|
1725
|
+
return undefined;
|
|
1726
|
+
}
|
|
1727
|
+
throw error;
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
function deriveStagePlan(prepared, snapshot, requiredStages, physicalResult, temporalResult, blockers, processedResult, environmentResult, environmentRequested = false) {
|
|
1731
|
+
const contract = getImageFormationContract();
|
|
1732
|
+
const rendererStages = new Set(prepared.renderer
|
|
1733
|
+
.supportedStages);
|
|
1734
|
+
return contract.stages.map((stage) => {
|
|
1735
|
+
if (!requiredStages.has(stage.id)) {
|
|
1736
|
+
return {
|
|
1737
|
+
stageId: stage.id,
|
|
1738
|
+
contractStatus: stage.status,
|
|
1739
|
+
state: "omitted-by-fidelity",
|
|
1740
|
+
scientificStatus: "not-applicable",
|
|
1741
|
+
requiredByFidelity: false,
|
|
1742
|
+
requiredUpstreamStages: [
|
|
1743
|
+
...stage
|
|
1744
|
+
.requiredUpstreamStages
|
|
1745
|
+
],
|
|
1746
|
+
coupledStages: [
|
|
1747
|
+
...stage.coupledStages
|
|
1748
|
+
],
|
|
1749
|
+
blockerCodes: []
|
|
1750
|
+
};
|
|
1751
|
+
}
|
|
1752
|
+
const stageBlockers = [];
|
|
1753
|
+
if (environmentRequested && environmentResult === undefined) {
|
|
1754
|
+
return { stageId: stage.id, contractStatus: stage.status, state: "blocked",
|
|
1755
|
+
scientificStatus: "not-applicable", requiredByFidelity: true,
|
|
1756
|
+
requiredUpstreamStages: [...stage.requiredUpstreamStages], coupledStages: [...stage.coupledStages],
|
|
1757
|
+
blockerCodes: ["environment-capture-evaluation-blocked"] };
|
|
1758
|
+
}
|
|
1759
|
+
if (environmentResult !== undefined && !PROCESSED_OUTPUT_STAGES.has(stage.id)) {
|
|
1760
|
+
const supported = rendererStages.has(stage.id);
|
|
1761
|
+
const psfOmitted = stage.id === "field-wavelength-psf" &&
|
|
1762
|
+
environmentResult.value.sites.every(site => !site.value.psfRedistributionApplied);
|
|
1763
|
+
if (!supported)
|
|
1764
|
+
addBlocker(blockers, { code: "renderer-stage-unsupported", stageId: stage.id,
|
|
1765
|
+
message: "Renderer does not declare the executed environment stage " + stage.id + "." });
|
|
1766
|
+
return { stageId: stage.id, contractStatus: stage.status,
|
|
1767
|
+
state: !supported ? "blocked" : psfOmitted ? "modeled-zero" : "active",
|
|
1768
|
+
scientificStatus: supported ? "approximation" : "not-applicable",
|
|
1769
|
+
requiredByFidelity: true, requiredUpstreamStages: [...stage.requiredUpstreamStages],
|
|
1770
|
+
coupledStages: [...stage.coupledStages], modelId: "environment-native-raw-producer", modelVersion: "0.1.0",
|
|
1771
|
+
resultIdentity: environmentResult.value.raw.value.frame.frameId,
|
|
1772
|
+
blockerCodes: supported ? [] : ["renderer-stage-unsupported"] };
|
|
1773
|
+
}
|
|
1774
|
+
if (processedResult !== undefined && PROCESSED_OUTPUT_STAGES.has(stage.id)) {
|
|
1775
|
+
const supported = rendererStages.has(stage.id);
|
|
1776
|
+
if (!supported)
|
|
1777
|
+
addBlocker(blockers, { code: "renderer-stage-unsupported", stageId: stage.id,
|
|
1778
|
+
message: "Renderer does not declare support for processed output stage " + stage.id + "." });
|
|
1779
|
+
return { stageId: stage.id, contractStatus: stage.status, state: supported ? "active" : "blocked",
|
|
1780
|
+
scientificStatus: supported ? "approximation" : "not-applicable", requiredByFidelity: true,
|
|
1781
|
+
requiredUpstreamStages: [...stage.requiredUpstreamStages], coupledStages: [...stage.coupledStages],
|
|
1782
|
+
modelId: "processed-sensor-raw-sdr", modelVersion: "0.1.0",
|
|
1783
|
+
resultIdentity: processedResult.value.source.value.rawFrame.frameId,
|
|
1784
|
+
blockerCodes: supported ? [] : ["renderer-stage-unsupported"] };
|
|
1785
|
+
}
|
|
1786
|
+
if (!COMPOSER_SUPPORTED_STAGES.has(stage.id)) {
|
|
1787
|
+
const blocker = {
|
|
1788
|
+
code: "engine-stage-not-composed",
|
|
1789
|
+
stageId: stage.id,
|
|
1790
|
+
message: "Production plan schema " +
|
|
1791
|
+
PRODUCTION_IMAGE_FORMATION_PLAN_VERSION +
|
|
1792
|
+
" does not yet compose required stage " +
|
|
1793
|
+
stage.id +
|
|
1794
|
+
"."
|
|
1795
|
+
};
|
|
1796
|
+
addBlocker(blockers, blocker);
|
|
1797
|
+
stageBlockers.push(blocker.code);
|
|
1798
|
+
return {
|
|
1799
|
+
stageId: stage.id,
|
|
1800
|
+
contractStatus: stage.status,
|
|
1801
|
+
state: "unsupported",
|
|
1802
|
+
scientificStatus: "not-applicable",
|
|
1803
|
+
requiredByFidelity: true,
|
|
1804
|
+
requiredUpstreamStages: [
|
|
1805
|
+
...stage
|
|
1806
|
+
.requiredUpstreamStages
|
|
1807
|
+
],
|
|
1808
|
+
coupledStages: [
|
|
1809
|
+
...stage.coupledStages
|
|
1810
|
+
],
|
|
1811
|
+
blockerCodes: stageBlockers
|
|
1812
|
+
};
|
|
1813
|
+
}
|
|
1814
|
+
if (!rendererStages.has(stage.id)) {
|
|
1815
|
+
const blocker = {
|
|
1816
|
+
code: "renderer-stage-unsupported",
|
|
1817
|
+
stageId: stage.id,
|
|
1818
|
+
message: "Renderer does not declare support for required stage " +
|
|
1819
|
+
stage.id +
|
|
1820
|
+
"."
|
|
1821
|
+
};
|
|
1822
|
+
addBlocker(blockers, blocker);
|
|
1823
|
+
stageBlockers.push(blocker.code);
|
|
1824
|
+
}
|
|
1825
|
+
if (stage.id ===
|
|
1826
|
+
"lens-field-pupil-evaluation" &&
|
|
1827
|
+
physicalResult ===
|
|
1828
|
+
undefined) {
|
|
1829
|
+
const relevant = blockers
|
|
1830
|
+
.filter((blocker) => blocker.stageId ===
|
|
1831
|
+
stage.id)
|
|
1832
|
+
.map((blocker) => blocker.code);
|
|
1833
|
+
stageBlockers.push(...relevant);
|
|
1834
|
+
}
|
|
1835
|
+
if (stageBlockers.length > 0) {
|
|
1836
|
+
return {
|
|
1837
|
+
stageId: stage.id,
|
|
1838
|
+
contractStatus: stage.status,
|
|
1839
|
+
state: "blocked",
|
|
1840
|
+
scientificStatus: "not-applicable",
|
|
1841
|
+
requiredByFidelity: true,
|
|
1842
|
+
requiredUpstreamStages: [
|
|
1843
|
+
...stage
|
|
1844
|
+
.requiredUpstreamStages
|
|
1845
|
+
],
|
|
1846
|
+
coupledStages: [
|
|
1847
|
+
...stage.coupledStages
|
|
1848
|
+
],
|
|
1849
|
+
blockerCodes: [
|
|
1850
|
+
...new Set(stageBlockers)
|
|
1851
|
+
]
|
|
1852
|
+
};
|
|
1853
|
+
}
|
|
1854
|
+
if (stage.id ===
|
|
1855
|
+
"scene-ray-projection") {
|
|
1856
|
+
const target = snapshot
|
|
1857
|
+
.physicalSceneSample
|
|
1858
|
+
?.sceneRadianceRequest
|
|
1859
|
+
.target;
|
|
1860
|
+
const modeledZero = target?.kind ===
|
|
1861
|
+
"environment-direction";
|
|
1862
|
+
return {
|
|
1863
|
+
stageId: stage.id,
|
|
1864
|
+
contractStatus: stage.status,
|
|
1865
|
+
state: modeledZero
|
|
1866
|
+
? "modeled-zero"
|
|
1867
|
+
: "active",
|
|
1868
|
+
scientificStatus: "approximation",
|
|
1869
|
+
requiredByFidelity: true,
|
|
1870
|
+
requiredUpstreamStages: [],
|
|
1871
|
+
coupledStages: [
|
|
1872
|
+
...stage.coupledStages
|
|
1873
|
+
],
|
|
1874
|
+
modelId: "capture-bound-scene-ray-target",
|
|
1875
|
+
modelVersion: "1.0.0",
|
|
1876
|
+
resultIdentity: target?.kind ??
|
|
1877
|
+
"unresolved-target",
|
|
1878
|
+
blockerCodes: []
|
|
1879
|
+
};
|
|
1880
|
+
}
|
|
1881
|
+
if (stage.id ===
|
|
1882
|
+
"scene-radiance-evaluation") {
|
|
1883
|
+
const sample = snapshot
|
|
1884
|
+
.physicalSceneSample;
|
|
1885
|
+
if (sample === undefined) {
|
|
1886
|
+
const blocker = {
|
|
1887
|
+
code: "missing-physical-scene-sample",
|
|
1888
|
+
stageId: stage.id,
|
|
1889
|
+
message: "Scene-radiance stage requires one immutable #85 request/result sample."
|
|
1890
|
+
};
|
|
1891
|
+
addBlocker(blockers, blocker);
|
|
1892
|
+
return {
|
|
1893
|
+
stageId: stage.id,
|
|
1894
|
+
contractStatus: stage.status,
|
|
1895
|
+
state: "blocked",
|
|
1896
|
+
scientificStatus: "not-applicable",
|
|
1897
|
+
requiredByFidelity: true,
|
|
1898
|
+
requiredUpstreamStages: [
|
|
1899
|
+
...stage
|
|
1900
|
+
.requiredUpstreamStages
|
|
1901
|
+
],
|
|
1902
|
+
coupledStages: [
|
|
1903
|
+
...stage
|
|
1904
|
+
.coupledStages
|
|
1905
|
+
],
|
|
1906
|
+
blockerCodes: [
|
|
1907
|
+
blocker.code
|
|
1908
|
+
]
|
|
1909
|
+
};
|
|
1910
|
+
}
|
|
1911
|
+
return {
|
|
1912
|
+
stageId: stage.id,
|
|
1913
|
+
contractStatus: stage.status,
|
|
1914
|
+
state: "active",
|
|
1915
|
+
scientificStatus: "approximation",
|
|
1916
|
+
requiredByFidelity: true,
|
|
1917
|
+
requiredUpstreamStages: [
|
|
1918
|
+
...stage
|
|
1919
|
+
.requiredUpstreamStages
|
|
1920
|
+
],
|
|
1921
|
+
coupledStages: [
|
|
1922
|
+
...stage.coupledStages
|
|
1923
|
+
],
|
|
1924
|
+
modelId: "scene-radiance-provider-result",
|
|
1925
|
+
modelVersion: sample
|
|
1926
|
+
.sceneRadianceResult
|
|
1927
|
+
.schemaVersion,
|
|
1928
|
+
resultIdentity: sample
|
|
1929
|
+
.sceneRadianceResult
|
|
1930
|
+
.sampleId,
|
|
1931
|
+
blockerCodes: []
|
|
1932
|
+
};
|
|
1933
|
+
}
|
|
1934
|
+
if (stage.id ===
|
|
1935
|
+
"lens-field-pupil-evaluation") {
|
|
1936
|
+
const result = physicalResult;
|
|
1937
|
+
return {
|
|
1938
|
+
stageId: stage.id,
|
|
1939
|
+
contractStatus: stage.status,
|
|
1940
|
+
state: "active",
|
|
1941
|
+
scientificStatus: result.scientificStatus,
|
|
1942
|
+
requiredByFidelity: true,
|
|
1943
|
+
requiredUpstreamStages: [
|
|
1944
|
+
...stage
|
|
1945
|
+
.requiredUpstreamStages
|
|
1946
|
+
],
|
|
1947
|
+
coupledStages: [
|
|
1948
|
+
...stage.coupledStages
|
|
1949
|
+
],
|
|
1950
|
+
modelId: "scene-radiance-to-sensor-irradiance",
|
|
1951
|
+
modelVersion: "1.0.0",
|
|
1952
|
+
resultIdentity: result.sampleId,
|
|
1953
|
+
blockerCodes: []
|
|
1954
|
+
};
|
|
1955
|
+
}
|
|
1956
|
+
if (stage.id ===
|
|
1957
|
+
"temporal-exposure-readout") {
|
|
1958
|
+
if (temporalResult === undefined) {
|
|
1959
|
+
const relevant = blockers
|
|
1960
|
+
.filter((blocker) => blocker.stageId ===
|
|
1961
|
+
stage.id)
|
|
1962
|
+
.map((blocker) => blocker.code);
|
|
1963
|
+
return {
|
|
1964
|
+
stageId: stage.id,
|
|
1965
|
+
contractStatus: stage.status,
|
|
1966
|
+
state: "blocked",
|
|
1967
|
+
scientificStatus: "not-applicable",
|
|
1968
|
+
requiredByFidelity: true,
|
|
1969
|
+
requiredUpstreamStages: [
|
|
1970
|
+
...stage
|
|
1971
|
+
.requiredUpstreamStages
|
|
1972
|
+
],
|
|
1973
|
+
coupledStages: [
|
|
1974
|
+
...stage.coupledStages
|
|
1975
|
+
],
|
|
1976
|
+
modelId: "capture-temporal-schedule",
|
|
1977
|
+
modelVersion: "1.0.0",
|
|
1978
|
+
blockerCodes: [
|
|
1979
|
+
...new Set(relevant)
|
|
1980
|
+
]
|
|
1981
|
+
};
|
|
1982
|
+
}
|
|
1983
|
+
return {
|
|
1984
|
+
stageId: stage.id,
|
|
1985
|
+
contractStatus: stage.status,
|
|
1986
|
+
state: "active",
|
|
1987
|
+
scientificStatus: "approximation",
|
|
1988
|
+
requiredByFidelity: true,
|
|
1989
|
+
requiredUpstreamStages: [
|
|
1990
|
+
...stage
|
|
1991
|
+
.requiredUpstreamStages
|
|
1992
|
+
],
|
|
1993
|
+
coupledStages: [
|
|
1994
|
+
...stage.coupledStages
|
|
1995
|
+
],
|
|
1996
|
+
modelId: "capture-temporal-schedule",
|
|
1997
|
+
modelVersion: "1.0.0",
|
|
1998
|
+
resultIdentity: snapshot
|
|
1999
|
+
.fingerprint.value,
|
|
2000
|
+
blockerCodes: []
|
|
2001
|
+
};
|
|
2002
|
+
}
|
|
2003
|
+
throw new InvalidConfigurationError("Unexpected composed stage.");
|
|
2004
|
+
});
|
|
2005
|
+
}
|
|
2006
|
+
function toAssuranceUncertainty(uncertainty) {
|
|
2007
|
+
if (uncertainty.kind ===
|
|
2008
|
+
"relative") {
|
|
2009
|
+
return {
|
|
2010
|
+
kind: "relative",
|
|
2011
|
+
fraction: uncertainty.fraction,
|
|
2012
|
+
basis: uncertainty.basis
|
|
2013
|
+
};
|
|
2014
|
+
}
|
|
2015
|
+
return {
|
|
2016
|
+
kind: "not-quantified",
|
|
2017
|
+
limitation: uncertainty.limitation
|
|
2018
|
+
};
|
|
2019
|
+
}
|
|
2020
|
+
function physicalAssuranceComponents(prepared, snapshot, physicalResult) {
|
|
2021
|
+
const sample = snapshot.physicalSceneSample;
|
|
2022
|
+
const optical = prepared.opticalBridgeProfile;
|
|
2023
|
+
if (sample === undefined ||
|
|
2024
|
+
optical === undefined) {
|
|
2025
|
+
throw new InvalidConfigurationError("Physical scientific assurance requires the committed physical sample and optical profile.");
|
|
2026
|
+
}
|
|
2027
|
+
const components = [
|
|
2028
|
+
{
|
|
2029
|
+
componentId: "scene-radiance",
|
|
2030
|
+
role: "outgoing scene spectral radiance",
|
|
2031
|
+
required: true,
|
|
2032
|
+
sourceIdentity: {
|
|
2033
|
+
kind: "result",
|
|
2034
|
+
id: sample
|
|
2035
|
+
.sceneRadianceResult
|
|
2036
|
+
.sampleId,
|
|
2037
|
+
version: sample
|
|
2038
|
+
.sceneRadianceResult
|
|
2039
|
+
.schemaVersion
|
|
2040
|
+
},
|
|
2041
|
+
basisKind: "evidence-backed-fact",
|
|
2042
|
+
scientificStatus: sample
|
|
2043
|
+
.sceneRadianceResult
|
|
2044
|
+
.scientificStatus,
|
|
2045
|
+
evidenceRequirement: "required",
|
|
2046
|
+
evidence: sample
|
|
2047
|
+
.sceneRadianceResult
|
|
2048
|
+
.evidence,
|
|
2049
|
+
uncertainty: toAssuranceUncertainty(sample
|
|
2050
|
+
.sceneRadianceResult
|
|
2051
|
+
.uncertainty),
|
|
2052
|
+
limitations: [
|
|
2053
|
+
...sample
|
|
2054
|
+
.sceneRadianceResult
|
|
2055
|
+
.limitations
|
|
2056
|
+
]
|
|
2057
|
+
},
|
|
2058
|
+
{
|
|
2059
|
+
componentId: "optical-throughput-profile",
|
|
2060
|
+
role: "lens throughput and working-aperture profile",
|
|
2061
|
+
required: true,
|
|
2062
|
+
sourceIdentity: {
|
|
2063
|
+
kind: "profile",
|
|
2064
|
+
id: optical.profileId,
|
|
2065
|
+
version: optical.profileVersion
|
|
2066
|
+
},
|
|
2067
|
+
basisKind: "evidence-backed-fact",
|
|
2068
|
+
scientificStatus: optical
|
|
2069
|
+
.scientificStatus,
|
|
2070
|
+
evidenceRequirement: "required",
|
|
2071
|
+
evidence: [
|
|
2072
|
+
...physicalResult
|
|
2073
|
+
.componentEvidence
|
|
2074
|
+
.opticalProfile,
|
|
2075
|
+
...physicalResult
|
|
2076
|
+
.componentEvidence
|
|
2077
|
+
.transmission,
|
|
2078
|
+
...physicalResult
|
|
2079
|
+
.componentEvidence
|
|
2080
|
+
.workingFNumber
|
|
2081
|
+
],
|
|
2082
|
+
uncertainty: toAssuranceUncertainty(optical
|
|
2083
|
+
.transmission
|
|
2084
|
+
.uncertainty),
|
|
2085
|
+
limitations: [
|
|
2086
|
+
...optical.limitations
|
|
2087
|
+
]
|
|
2088
|
+
},
|
|
2089
|
+
{
|
|
2090
|
+
componentId: "primary-optics-bridge-model",
|
|
2091
|
+
role: "scene-radiance to pre-sensor-stack irradiance composition model",
|
|
2092
|
+
required: true,
|
|
2093
|
+
sourceIdentity: {
|
|
2094
|
+
kind: "model",
|
|
2095
|
+
id: "scene-radiance-to-sensor-irradiance",
|
|
2096
|
+
version: "1.1.0"
|
|
2097
|
+
},
|
|
2098
|
+
basisKind: "photivra-model-assumption",
|
|
2099
|
+
scientificStatus: "approximation",
|
|
2100
|
+
evidenceRequirement: "not-required",
|
|
2101
|
+
evidence: [],
|
|
2102
|
+
uncertainty: {
|
|
2103
|
+
kind: "not-quantified",
|
|
2104
|
+
limitation: "The paraxial primary-optics bridge is an explicit approximation with no defensible aggregate model-error bound yet."
|
|
2105
|
+
},
|
|
2106
|
+
limitations: [
|
|
2107
|
+
"Paraxial circular-pupil acceptance is not a full ray-traced pupil solution.",
|
|
2108
|
+
"PSF redistribution, sensor optical-stack response and polarization remain outside this bridge."
|
|
2109
|
+
]
|
|
2110
|
+
}
|
|
2111
|
+
];
|
|
2112
|
+
for (let index = 0; index <
|
|
2113
|
+
(sample.frontOfLensFilters
|
|
2114
|
+
?.length ?? 0); index += 1) {
|
|
2115
|
+
const profile = sample
|
|
2116
|
+
.frontOfLensFilters[index];
|
|
2117
|
+
const resolved = resolveFrontOfLensFilterTransmission({
|
|
2118
|
+
profile,
|
|
2119
|
+
wavelengthNanometers: physicalResult
|
|
2120
|
+
.wavelengthNanometers,
|
|
2121
|
+
wavelengthBasis: physicalResult
|
|
2122
|
+
.wavelengthBasis
|
|
2123
|
+
});
|
|
2124
|
+
components.push({
|
|
2125
|
+
componentId: "front-filter:" +
|
|
2126
|
+
index,
|
|
2127
|
+
role: "front-of-lens transmission filter",
|
|
2128
|
+
required: true,
|
|
2129
|
+
sourceIdentity: {
|
|
2130
|
+
kind: "profile",
|
|
2131
|
+
id: resolved.filterId,
|
|
2132
|
+
version: resolved.profileVersion
|
|
2133
|
+
},
|
|
2134
|
+
basisKind: "evidence-backed-fact",
|
|
2135
|
+
scientificStatus: resolved
|
|
2136
|
+
.scientificStatus,
|
|
2137
|
+
evidenceRequirement: "required",
|
|
2138
|
+
evidence: resolved.evidence,
|
|
2139
|
+
uncertainty: toAssuranceUncertainty(resolved.uncertainty),
|
|
2140
|
+
limitations: [
|
|
2141
|
+
...resolved.limitations
|
|
2142
|
+
]
|
|
2143
|
+
});
|
|
2144
|
+
}
|
|
2145
|
+
if (sample.fieldThroughput.kind !==
|
|
2146
|
+
"unity") {
|
|
2147
|
+
components.push({
|
|
2148
|
+
componentId: "field-throughput-model",
|
|
2149
|
+
role: "field-dependent illumination throughput",
|
|
2150
|
+
required: true,
|
|
2151
|
+
sourceIdentity: {
|
|
2152
|
+
kind: "model",
|
|
2153
|
+
id: "generic-illumination-vignetting",
|
|
2154
|
+
version: "1.0.0"
|
|
2155
|
+
},
|
|
2156
|
+
basisKind: "photivra-model-assumption",
|
|
2157
|
+
scientificStatus: "approximation",
|
|
2158
|
+
evidenceRequirement: "not-required",
|
|
2159
|
+
evidence: [],
|
|
2160
|
+
uncertainty: {
|
|
2161
|
+
kind: "not-quantified",
|
|
2162
|
+
limitation: "The capture snapshot retains the evaluated vignetting value but not the source CalculationResult quality envelope; no numeric uncertainty is inferred."
|
|
2163
|
+
},
|
|
2164
|
+
limitations: [
|
|
2165
|
+
"Field-throughput model status remains separate from lens transmission and front-filter transmission.",
|
|
2166
|
+
"Future capture contracts must explicitly preserve source quality if calibrated field-throughput uncertainty is required downstream."
|
|
2167
|
+
]
|
|
2168
|
+
});
|
|
2169
|
+
}
|
|
2170
|
+
return components;
|
|
2171
|
+
}
|
|
2172
|
+
function temporalAssuranceComponent(snapshot) {
|
|
2173
|
+
return {
|
|
2174
|
+
componentId: "temporal-capture-schedule",
|
|
2175
|
+
role: "capture exposure/readout temporal composition",
|
|
2176
|
+
required: true,
|
|
2177
|
+
sourceIdentity: {
|
|
2178
|
+
kind: "model",
|
|
2179
|
+
id: "capture-temporal-schedule",
|
|
2180
|
+
version: "1.0.0"
|
|
2181
|
+
},
|
|
2182
|
+
basisKind: "photivra-model-assumption",
|
|
2183
|
+
scientificStatus: "approximation",
|
|
2184
|
+
evidenceRequirement: "not-required",
|
|
2185
|
+
evidence: [],
|
|
2186
|
+
uncertainty: {
|
|
2187
|
+
kind: "not-quantified",
|
|
2188
|
+
limitation: "Temporal schedule composition preserves declared timing facts but does not claim a generic aggregate timing/model uncertainty."
|
|
2189
|
+
},
|
|
2190
|
+
limitations: [
|
|
2191
|
+
"Readout/exposure synchronization is not assumed unless explicitly modeled.",
|
|
2192
|
+
"Renderer execution timing does not redefine the committed capture schedule.",
|
|
2193
|
+
"Capture identity " +
|
|
2194
|
+
snapshot.captureId +
|
|
2195
|
+
" owns the committed temporal state."
|
|
2196
|
+
]
|
|
2197
|
+
};
|
|
2198
|
+
}
|
|
2199
|
+
/**
|
|
2200
|
+
* Compose evidence/status from actual child stages without averaging uncertainty
|
|
2201
|
+
* or upgrading declared approximation to calibration. Requested blockers and
|
|
2202
|
+
* executed lineage are part of assurance; a complete graph alone cannot establish
|
|
2203
|
+
* physical accuracy or validate an external provider's numerical transport.
|
|
2204
|
+
*/
|
|
2205
|
+
function composeProductionScientificAssurance(prepared, snapshot, physicalResult, temporalResult, processedResult, environmentResult) {
|
|
2206
|
+
const components = [];
|
|
2207
|
+
if (physicalResult !== undefined) {
|
|
2208
|
+
components.push(...physicalAssuranceComponents(prepared, snapshot, physicalResult));
|
|
2209
|
+
}
|
|
2210
|
+
if (temporalResult !== undefined) {
|
|
2211
|
+
components.push(temporalAssuranceComponent(snapshot));
|
|
2212
|
+
}
|
|
2213
|
+
if (environmentResult !== undefined) {
|
|
2214
|
+
components.push({ componentId: "environment-native-raw-producer", role: "bounded executed environment-to-ADC capture", required: true,
|
|
2215
|
+
sourceIdentity: { kind: "model", id: "environment-native-raw-producer", version: "0.1.0" },
|
|
2216
|
+
basisKind: "photivra-model-assumption", scientificStatus: "approximation", evidenceRequirement: "required",
|
|
2217
|
+
evidence: environmentResult.value.sceneBinding.evidence,
|
|
2218
|
+
uncertainty: { kind: "not-quantified", limitation: "Child source, optical, response and readout uncertainty is not propagated into a combined capture uncertainty." },
|
|
2219
|
+
limitations: ["Provider code executes, but transport, visibility and calibrated source truth remain unverified.",
|
|
2220
|
+
"Destination-local PSF and explicitly bounded native sampling do not establish global energy conservation or high-resolution performance."] });
|
|
2221
|
+
}
|
|
2222
|
+
if (processedResult !== undefined) {
|
|
2223
|
+
components.push({ componentId: "processed-sensor-raw-sdr", role: "committed post-capture SDR development", required: true,
|
|
2224
|
+
sourceIdentity: { kind: "model", id: "processed-sensor-raw-sdr", version: "0.1.0" }, basisKind: "photivra-model-assumption",
|
|
2225
|
+
scientificStatus: "approximation", evidenceRequirement: "required", evidence: processedResult.value.colorProfile.evidence,
|
|
2226
|
+
uncertainty: { kind: "not-quantified", limitation: "Reconstruction, color interpretation and optional correction uncertainty are not propagated into a combined output uncertainty." },
|
|
2227
|
+
limitations: [...processedResult.value.colorProfile.limitations,
|
|
2228
|
+
environmentResult === undefined ? "Attached RAW origin is declared; downstream execution does not verify missing upstream production stages." :
|
|
2229
|
+
"RAW origin is executed by this bounded plan; provider transport and calibrated accuracy remain unverified.",
|
|
2230
|
+
"SDR rendering is a deterministic rendering choice; platform viewing adaptation is external."] });
|
|
2231
|
+
}
|
|
2232
|
+
if (components.length === 0) {
|
|
2233
|
+
return undefined;
|
|
2234
|
+
}
|
|
2235
|
+
return composeScientificAssurance({
|
|
2236
|
+
components
|
|
2237
|
+
});
|
|
2238
|
+
}
|
|
2239
|
+
/** Check immutable event ownership before source code or downstream pixel work. */
|
|
2240
|
+
function validateProductionRawCapture(capture, snapshot, label = "Processed RAW") {
|
|
2241
|
+
if (capture.captureId !== snapshot.captureId || capture.sceneStateId !== snapshot.sceneStateId ||
|
|
2242
|
+
capture.sceneTimeSeconds !== snapshot.sceneTimeSecondsFromExposureStart || capture.noise.seedUint32 !== snapshot.stochasticSeedUint32 ||
|
|
2243
|
+
capture.exposure.aperture !== snapshot.exposure.aperture || capture.exposure.shutterSeconds !== snapshot.exposure.shutterSeconds || capture.exposure.iso !== snapshot.exposure.iso ||
|
|
2244
|
+
(capture.whiteBalanceIntent?.stateId !== snapshot.whiteBalanceState?.stateId) ||
|
|
2245
|
+
(capture.whiteBalanceIntent !== null && ["red", "green", "blue"].some((channel) => capture.whiteBalanceIntent.channelGains[channel] !== snapshot.whiteBalanceState?.channelGains[channel]))) {
|
|
2246
|
+
throw new InvalidConfigurationError(label + " capture identity, scene clock, seed, exposure or WB differs from the immutable production snapshot.");
|
|
2247
|
+
}
|
|
2248
|
+
const releaseFocus = snapshot.releaseFrameBinding?.focus;
|
|
2249
|
+
if (releaseFocus !== undefined && (releaseFocus.kind !== capture.focus.kind ||
|
|
2250
|
+
(releaseFocus.kind === "finite" && capture.focus.kind === "finite" && releaseFocus.distanceM !== capture.focus.distanceM))) {
|
|
2251
|
+
throw new InvalidConfigurationError(label + " focus differs from the committed release frame.");
|
|
2252
|
+
}
|
|
2253
|
+
if (snapshot.physicalSceneSample !== undefined && !releaseFocusMatchesOpticalFocus(capture.focus, snapshot.physicalSceneSample.focus)) {
|
|
2254
|
+
throw new InvalidConfigurationError(label + " focus differs from the committed optical sample.");
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
/** Execute the committed post-capture boundary without blessing absent upstream stages. */
|
|
2258
|
+
function computeProcessedOutput(input, prepared, snapshot, required, blockers) {
|
|
2259
|
+
if (input === undefined)
|
|
2260
|
+
return undefined;
|
|
2261
|
+
try {
|
|
2262
|
+
const declaration = requireRecord(input, "processedOutput");
|
|
2263
|
+
if (Object.keys(declaration).some(key => key !== "outputStateId" && key !== "processing"))
|
|
2264
|
+
throw new InvalidConfigurationError("Unknown processed output declaration fields.");
|
|
2265
|
+
if (!required.has("display-processing") || !prepared.renderer.sensorDomainProcessing)
|
|
2266
|
+
throw new InvalidConfigurationError("Processed RAW requires requested display processing and renderer sensor-domain support.");
|
|
2267
|
+
if (input.outputStateId !== snapshot.outputStateId)
|
|
2268
|
+
throw new InvalidConfigurationError("Processed RAW output state differs from the immutable production snapshot.");
|
|
2269
|
+
const result = calculateProcessedSensorRaw(input.processing), capture = result.value.source.value.rawFrame.capture;
|
|
2270
|
+
validateProductionRawCapture(capture, snapshot);
|
|
2271
|
+
return result;
|
|
2272
|
+
}
|
|
2273
|
+
catch (error) {
|
|
2274
|
+
if (!(error instanceof InvalidConfigurationError || error instanceof InvalidScientificInputError))
|
|
2275
|
+
throw error;
|
|
2276
|
+
addBlocker(blockers, { code: "processed-output-evaluation-blocked", stageId: "display-processing", message: error.message });
|
|
2277
|
+
return undefined;
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
/**
|
|
2281
|
+
* Commit the execution envelope before invoking supplied provider code. A failed
|
|
2282
|
+
* preflight returns a structured blocker and never invokes that code. The existing
|
|
2283
|
+
* environment producer owns all node budgets, response validity and seeded RAW.
|
|
2284
|
+
*/
|
|
2285
|
+
/**
|
|
2286
|
+
* Validate shared capture/context, renderer and supported-envelope commitments
|
|
2287
|
+
* before any trusted provider callback runs. Snapshot owned scientific and output
|
|
2288
|
+
* policy data first, then delegate to the one existing environment RAW producer.
|
|
2289
|
+
* Callback failure propagates; a successful call proves invocation and data binding,
|
|
2290
|
+
* not physical transport, visibility, calibration or a fallback renderer.
|
|
2291
|
+
*/
|
|
2292
|
+
function computeEnvironmentCapture(input, externalOutput, prepared, snapshot, required, blockers) {
|
|
2293
|
+
if (input === undefined)
|
|
2294
|
+
return undefined;
|
|
2295
|
+
try {
|
|
2296
|
+
const declaration = requireRecord(input, "environmentCapture");
|
|
2297
|
+
if (Object.keys(declaration).some(key => key !== "capture" && key !== "processing")) {
|
|
2298
|
+
throw new InvalidConfigurationError("Unknown environment capture declaration fields.");
|
|
2299
|
+
}
|
|
2300
|
+
if (externalOutput !== undefined || snapshot.physicalSceneSample !== undefined) {
|
|
2301
|
+
throw new InvalidConfigurationError("Executed environment capture cannot mix declared scene samples or external RAW output sources.");
|
|
2302
|
+
}
|
|
2303
|
+
const { evaluateRadiance, ...data } = input.capture;
|
|
2304
|
+
const owned = structuredClone(data);
|
|
2305
|
+
const capture = parseSimulatedCapture(owned.frame.capture);
|
|
2306
|
+
validateProductionRawCapture(capture, snapshot, "Production RAW");
|
|
2307
|
+
if (!required.has("adc-quantization") || !prepared.renderer.sensorDomainProcessing ||
|
|
2308
|
+
prepared.renderer.spectralCapability !== "wavelength-resolved" ||
|
|
2309
|
+
[...required].some(stage => !prepared.renderer.supportedStages.includes(stage))) {
|
|
2310
|
+
throw new InvalidConfigurationError("Executed environment capture requires the complete requested ADC graph and renderer stage/spectral/sensor support.");
|
|
2311
|
+
}
|
|
2312
|
+
if (blockers.length !== 0)
|
|
2313
|
+
throw new InvalidConfigurationError("Environment execution is blocked by production renderer or temporal preflight.");
|
|
2314
|
+
if (input.processing !== undefined) {
|
|
2315
|
+
validateProcessedSensorRawEnvelope({ rawFrame: { capture }, region: input.processing.reconstruction.region }, input.processing.correction !== undefined);
|
|
2316
|
+
}
|
|
2317
|
+
const temporal = snapshot.temporalCapture;
|
|
2318
|
+
if (temporal === undefined)
|
|
2319
|
+
throw new InvalidConfigurationError("Environment execution requires committed temporal capture input.");
|
|
2320
|
+
const { nativeRaster, samplePointsNative: _, activeCaptureRect, ...schedule } = temporal.exposureWindowInput;
|
|
2321
|
+
void _;
|
|
2322
|
+
if (canonicalStringify(schedule) !== canonicalStringify(owned.exposureWindow) ||
|
|
2323
|
+
canonicalStringify(nativeRaster) !== canonicalStringify(capture.geometry.nativeRaster) ||
|
|
2324
|
+
canonicalStringify(temporal.imagingArea) !== canonicalStringify(capture.geometry.imagingArea) ||
|
|
2325
|
+
temporal.orientation !== capture.geometry.orientation ||
|
|
2326
|
+
(activeCaptureRect !== undefined && canonicalStringify(activeCaptureRect) !== canonicalStringify({ x: 0, y: 0,
|
|
2327
|
+
width: capture.geometry.nativeRaster.pixelWidth, height: capture.geometry.nativeRaster.pixelHeight })) ||
|
|
2328
|
+
capture.exposure.focalLengthMm !== prepared.equipmentCapabilities.selectedFocalLengthMm ||
|
|
2329
|
+
owned.sceneBinding.providerSceneId !== prepared.sceneId) {
|
|
2330
|
+
throw new InvalidConfigurationError("Environment geometry, scene, focal length and shutter schedule differ from the committed production context.");
|
|
2331
|
+
}
|
|
2332
|
+
const rendererTemporal = prepared.renderer.temporalSampling;
|
|
2333
|
+
for (const site of owned.sites) {
|
|
2334
|
+
const e = site.environment;
|
|
2335
|
+
if (e.sceneBindings.providerProfile.profileId !== prepared.sceneRadianceProviderProfileId ||
|
|
2336
|
+
canonicalStringify(e.optics.profile) !== canonicalStringify(prepared.opticalBridgeProfile) ||
|
|
2337
|
+
rendererTemporal.kind !== "bounded" || e.temporalSampleCount > rendererTemporal.maximumSamples ||
|
|
2338
|
+
(temporal.rotation === undefined && !angularVelocityIsZero(e.motion.angularVelocityRadPerSec)) ||
|
|
2339
|
+
(temporal.rotation !== undefined && (canonicalStringify(e.motion.angularVelocityRadPerSec) !== canonicalStringify(temporal.rotation.angularVelocityRadPerSec) ||
|
|
2340
|
+
e.temporalSampleCount !== temporal.rotation.temporalSampleCount ||
|
|
2341
|
+
(capture.focus.kind === "finite" ? capture.focus.distanceM : undefined) !== temporal.rotation.focusDistanceM))) {
|
|
2342
|
+
throw new InvalidConfigurationError("Environment provider, optics, motion or temporal sampling differs from the committed production context.");
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
const requestFingerprint = fingerprintValue(owned);
|
|
2346
|
+
return { result: simulateEnvironmentSensorRawFrame({ ...owned, evaluateRadiance }), requestFingerprint };
|
|
2347
|
+
}
|
|
2348
|
+
catch (error) {
|
|
2349
|
+
if (!(error instanceof InvalidConfigurationError || error instanceof InvalidScientificInputError))
|
|
2350
|
+
throw error;
|
|
2351
|
+
addBlocker(blockers, { code: "environment-capture-evaluation-blocked", stageId: "adc-quantization", message: error.message });
|
|
2352
|
+
return undefined;
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
/**
|
|
2356
|
+
* Expand requested stages through the authoritative graph, validate shared commitments and capability
|
|
2357
|
+
* limits, then execute only supported declared input routes. Return explicit
|
|
2358
|
+
* active/zero/omitted/blocked dispositions and child evidence rather than silently skipping a
|
|
2359
|
+
* requested effect.
|
|
2360
|
+
*
|
|
2361
|
+
* The planner owns scientific stage ordering, immutable capture identity and reproducibility.
|
|
2362
|
+
* Well-formed unsupported combinations produce structured blockers; malformed declarations throw. The
|
|
2363
|
+
* bounded environment route executes supplied radiance through native RAW and optional output, while
|
|
2364
|
+
* sample-only and attached-RAW routes retain missing upstream blockers. FNV fingerprints are
|
|
2365
|
+
* reproducibility keys, not cryptographic integrity proofs.
|
|
2366
|
+
* @param input - CreateProductionImageFormationPlanInput. See the linked contract for coordinate, unit and profile binding semantics.
|
|
2367
|
+
* @returns ProductionImageFormationPlan. Return shape and scientific status are explicit; no calibration is inferred from successful execution.
|
|
2368
|
+
*
|
|
2369
|
+
* @see docs/API_REFERENCE.md for the root export and exact type graph.
|
|
2370
|
+
*/
|
|
2371
|
+
export function createProductionImageFormationPlan(input) {
|
|
2372
|
+
const prepared = parsePreparedImageFormationContext(input.preparedContext);
|
|
2373
|
+
const snapshot = parseProductionCaptureSnapshot(input.captureSnapshot);
|
|
2374
|
+
validateSnapshotAgainstContext(prepared, snapshot);
|
|
2375
|
+
const requiredStages = expandRequiredStages(prepared.fidelity);
|
|
2376
|
+
const blockers = [];
|
|
2377
|
+
evaluateRendererRequirements(prepared, requiredStages, blockers);
|
|
2378
|
+
const physicalResult = input.environmentCapture !== undefined ? undefined :
|
|
2379
|
+
computePhysicalResult(prepared, snapshot, requiredStages, blockers);
|
|
2380
|
+
const temporalResult = computeTemporalResult(prepared, snapshot, requiredStages, blockers);
|
|
2381
|
+
const effectPlan = deriveEffectPlan(prepared, snapshot, temporalResult, blockers);
|
|
2382
|
+
// Snapshot post-capture policy before a provider can mutate caller-owned state.
|
|
2383
|
+
const environmentProcessing = input.environmentCapture?.processing === undefined ? undefined :
|
|
2384
|
+
structuredClone(input.environmentCapture.processing);
|
|
2385
|
+
const environmentExecution = computeEnvironmentCapture(input.environmentCapture, input.processedOutput, prepared, snapshot, requiredStages, blockers);
|
|
2386
|
+
const environmentCaptureResult = environmentExecution?.result;
|
|
2387
|
+
const executedOutput = environmentCaptureResult === undefined || environmentProcessing === undefined ? undefined : {
|
|
2388
|
+
outputStateId: snapshot.outputStateId,
|
|
2389
|
+
processing: { ...environmentProcessing, reconstruction: { ...environmentProcessing.reconstruction,
|
|
2390
|
+
rawFrame: environmentCaptureResult.value.raw.value.frame } }
|
|
2391
|
+
};
|
|
2392
|
+
const processedOutputResult = computeProcessedOutput(input.environmentCapture === undefined ? input.processedOutput : executedOutput, prepared, snapshot, requiredStages, blockers);
|
|
2393
|
+
const scientificAssurance = composeProductionScientificAssurance(prepared, snapshot, physicalResult, temporalResult, processedOutputResult, environmentCaptureResult);
|
|
2394
|
+
const stagePlan = deriveStagePlan(prepared, snapshot, requiredStages, physicalResult, temporalResult, blockers, processedOutputResult, environmentCaptureResult, input.environmentCapture !== undefined);
|
|
2395
|
+
const core = {
|
|
2396
|
+
version: PRODUCTION_IMAGE_FORMATION_PLAN_VERSION,
|
|
2397
|
+
status: blockers.length === 0
|
|
2398
|
+
? "ready"
|
|
2399
|
+
: "blocked",
|
|
2400
|
+
versions: {
|
|
2401
|
+
engineApi: ENGINE_API_VERSION,
|
|
2402
|
+
imageFormationContract: IMAGE_FORMATION_CONTRACT_VERSION,
|
|
2403
|
+
plan: PRODUCTION_IMAGE_FORMATION_PLAN_VERSION,
|
|
2404
|
+
preparedContext: PREPARED_IMAGE_FORMATION_CONTEXT_VERSION,
|
|
2405
|
+
captureSnapshot: PRODUCTION_CAPTURE_SNAPSHOT_VERSION,
|
|
2406
|
+
rendererCapability: RENDERER_CAPABILITY_SCHEMA_VERSION,
|
|
2407
|
+
fidelityProfile: IMAGE_FORMATION_FIDELITY_PROFILE_SCHEMA_VERSION,
|
|
2408
|
+
scientificAssurance: SCIENTIFIC_ASSURANCE_CONTRACT_VERSION
|
|
2409
|
+
},
|
|
2410
|
+
contextIdentity: {
|
|
2411
|
+
contextId: prepared.contextId,
|
|
2412
|
+
preparedContextFingerprint: prepared
|
|
2413
|
+
.fingerprint.value
|
|
2414
|
+
},
|
|
2415
|
+
captureIdentity: {
|
|
2416
|
+
captureId: snapshot.captureId,
|
|
2417
|
+
releaseFrameId: snapshot.releaseFrameId,
|
|
2418
|
+
sceneStateId: snapshot.sceneStateId,
|
|
2419
|
+
...(snapshot
|
|
2420
|
+
.releaseFrameBinding ===
|
|
2421
|
+
undefined
|
|
2422
|
+
? {}
|
|
2423
|
+
: {
|
|
2424
|
+
releaseSequenceId: snapshot
|
|
2425
|
+
.releaseFrameBinding
|
|
2426
|
+
.sequenceId,
|
|
2427
|
+
releaseFrameIndex: snapshot
|
|
2428
|
+
.releaseFrameBinding
|
|
2429
|
+
.frameIndex
|
|
2430
|
+
}),
|
|
2431
|
+
...(snapshot
|
|
2432
|
+
.whiteBalanceState ===
|
|
2433
|
+
undefined
|
|
2434
|
+
? {}
|
|
2435
|
+
: {
|
|
2436
|
+
whiteBalanceStateId: snapshot
|
|
2437
|
+
.whiteBalanceState
|
|
2438
|
+
.stateId
|
|
2439
|
+
}),
|
|
2440
|
+
captureSnapshotFingerprint: snapshot
|
|
2441
|
+
.fingerprint.value
|
|
2442
|
+
},
|
|
2443
|
+
rendererIdentity: {
|
|
2444
|
+
rendererId: prepared.renderer
|
|
2445
|
+
.rendererId,
|
|
2446
|
+
rendererVersion: prepared.renderer
|
|
2447
|
+
.rendererVersion,
|
|
2448
|
+
consumerKind: prepared.renderer
|
|
2449
|
+
.consumerKind
|
|
2450
|
+
},
|
|
2451
|
+
fidelityIdentity: {
|
|
2452
|
+
profileId: prepared.fidelity
|
|
2453
|
+
.profileId,
|
|
2454
|
+
profileVersion: prepared.fidelity
|
|
2455
|
+
.profileVersion
|
|
2456
|
+
},
|
|
2457
|
+
stagePlan,
|
|
2458
|
+
...(environmentExecution === undefined ? {} : { environmentCaptureResult: environmentExecution.result,
|
|
2459
|
+
environmentCaptureRequestFingerprint: environmentExecution.requestFingerprint }),
|
|
2460
|
+
...(processedOutputResult === undefined ? {} : { processedOutputResult }),
|
|
2461
|
+
effectPlan,
|
|
2462
|
+
blockers,
|
|
2463
|
+
...(physicalResult ===
|
|
2464
|
+
undefined
|
|
2465
|
+
? {}
|
|
2466
|
+
: {
|
|
2467
|
+
physicalSceneToSensorResult: physicalResult
|
|
2468
|
+
}),
|
|
2469
|
+
...(temporalResult ===
|
|
2470
|
+
undefined
|
|
2471
|
+
? {}
|
|
2472
|
+
: {
|
|
2473
|
+
temporalCaptureResult: temporalResult
|
|
2474
|
+
}),
|
|
2475
|
+
...(scientificAssurance ===
|
|
2476
|
+
undefined
|
|
2477
|
+
? {}
|
|
2478
|
+
: {
|
|
2479
|
+
scientificAssurance
|
|
2480
|
+
}),
|
|
2481
|
+
stochastic: {
|
|
2482
|
+
captureSeedUint32: snapshot
|
|
2483
|
+
.stochasticSeedUint32,
|
|
2484
|
+
backendRandomnessMayRedefineScientificResult: false
|
|
2485
|
+
},
|
|
2486
|
+
immutableCaptureSnapshot: true,
|
|
2487
|
+
legacyPocModified: false
|
|
2488
|
+
};
|
|
2489
|
+
const plan = {
|
|
2490
|
+
...core,
|
|
2491
|
+
fingerprint: {
|
|
2492
|
+
algorithm: "fnv1a-32-non-cryptographic",
|
|
2493
|
+
value: fingerprintValue(core),
|
|
2494
|
+
purpose: "deterministic-reproducibility-key-not-integrity-security"
|
|
2495
|
+
}
|
|
2496
|
+
};
|
|
2497
|
+
return deepFreeze(cloneJson(plan));
|
|
2498
|
+
}
|
|
2499
|
+
//# sourceMappingURL=image-formation-plan.js.map
|