@photivra/engine 0.6.0 → 1.0.1
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 +191 -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 +139 -72
- 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 +9 -0
- package/dist/core/version.d.ts.map +1 -0
- package/dist/core/version.js +10 -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 +87 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +85 -6
- package/dist/index.js.map +1 -1
- package/dist/motion/camera-rotation.d.ts +16 -0
- package/dist/motion/camera-rotation.d.ts.map +1 -1
- package/dist/motion/camera-rotation.js +16 -0
- package/dist/motion/camera-rotation.js.map +1 -1
- package/dist/motion/capture-rotation-inverse-mapping.d.ts +13 -0
- package/dist/motion/capture-rotation-inverse-mapping.d.ts.map +1 -1
- package/dist/motion/capture-rotation-inverse-mapping.js +13 -0
- package/dist/motion/capture-rotation-inverse-mapping.js.map +1 -1
- package/dist/motion/capture-rotation-temporal-quadrature.d.ts +15 -0
- package/dist/motion/capture-rotation-temporal-quadrature.d.ts.map +1 -1
- package/dist/motion/capture-rotation-temporal-quadrature.js +15 -0
- package/dist/motion/capture-rotation-temporal-quadrature.js.map +1 -1
- package/dist/motion/capture-rotation-trajectory.d.ts +15 -0
- package/dist/motion/capture-rotation-trajectory.d.ts.map +1 -1
- package/dist/motion/capture-rotation-trajectory.js +15 -0
- package/dist/motion/capture-rotation-trajectory.js.map +1 -1
- 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 +11 -0
- 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 +18 -0
- package/dist/optics/radial-distortion.d.ts.map +1 -1
- package/dist/optics/radial-distortion.js +18 -0
- 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 +8 -0
- 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 +1 -0
- package/dist/sensor/capture-color-sampling-binding.d.ts.map +1 -1
- package/dist/sensor/capture-color-sampling-binding.js +14 -0
- package/dist/sensor/capture-color-sampling-binding.js.map +1 -1
- 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.map +1 -1
- package/dist/sensor/capture-mode.js +13 -0
- package/dist/sensor/capture-mode.js.map +1 -1
- package/dist/sensor/color-sampling.d.ts.map +1 -1
- package/dist/sensor/color-sampling.js +19 -0
- package/dist/sensor/color-sampling.js.map +1 -1
- 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 +14 -0
- package/dist/sensor/exposure-window.d.ts.map +1 -1
- package/dist/sensor/exposure-window.js +14 -0
- package/dist/sensor/exposure-window.js.map +1 -1
- 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.map +1 -1
- package/dist/sensor/optical-stack.js +17 -0
- package/dist/sensor/optical-stack.js.map +1 -1
- 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 +12 -0
- package/dist/sensor/readout-exposure-linkage.d.ts.map +1 -1
- package/dist/sensor/readout-exposure-linkage.js +12 -0
- package/dist/sensor/readout-exposure-linkage.js.map +1 -1
- package/dist/sensor/readout-timing.d.ts +13 -0
- package/dist/sensor/readout-timing.d.ts.map +1 -1
- package/dist/sensor/readout-timing.js +13 -0
- package/dist/sensor/readout-timing.js.map +1 -1
- 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.map +1 -1
- package/dist/sensor/sampling-aperture.js +14 -0
- package/dist/sensor/sampling-aperture.js.map +1 -1
- 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 +44 -1
- package/dist/sensor/spatial-sample-reduction.d.ts.map +1 -1
- package/dist/sensor/spatial-sample-reduction.js +51 -5
- package/dist/sensor/spatial-sample-reduction.js.map +1 -1
- package/dist/sensor/spatial-sampling-quadrature.d.ts +24 -0
- package/dist/sensor/spatial-sampling-quadrature.d.ts.map +1 -1
- package/dist/sensor/spatial-sampling-quadrature.js +27 -0
- package/dist/sensor/spatial-sampling-quadrature.js.map +1 -1
- 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 +139 -3
- package/docs/ARCHITECTURE.md +134 -24
- package/docs/AUTOMATION_COORDINATION.md +2 -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 +80 -10
- package/docs/LINEAR_CAPTURE_ENCODING.md +25 -0
- package/docs/MOTION_AND_SIGNAL.md +5 -3
- 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 +66 -0
- package/docs/RELEASE_1_0_1.md +61 -0
- package/docs/RELEASE_AUDIT.md +39 -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 +5050 -1047
- 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 +15 -3
|
@@ -0,0 +1,1905 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Module boundary and integration notes.
|
|
4
|
+
* Resolves Manual exposure with either manual ISO or Auto ISO. Aperture and shutter are always
|
|
5
|
+
* caller-owned and are never changed. The relative meter target is normalized by an explicit reference
|
|
6
|
+
* exposure anchor; no ISO-100 or other hidden absolute calibration is assumed.
|
|
7
|
+
* Resolves Aperture Priority with manual ISO. Aperture and ISO remain caller-owned. Shutter is the
|
|
8
|
+
* only automatic axis. The target/reference relationship is the same relative exposure model used by
|
|
9
|
+
* Manual + Auto ISO; no new mode-specific exposure equation is introduced.
|
|
10
|
+
* @see docs/USAGE.md for equations, coordinate/unit conventions, blockers and support limits.
|
|
11
|
+
*/
|
|
12
|
+
import { InvalidScientificInputError } from "../core/validation.js";
|
|
13
|
+
import { EXPOSURE_METER_TARGET_SCHEMA_VERSION } from "./metering-target.js";
|
|
14
|
+
import { parseExposureProgramLineProfile } from "./program-line.js";
|
|
15
|
+
export const EXPOSURE_MODE_RESOLVER_VERSION = "0.5.0";
|
|
16
|
+
function requireRecord(value, path) {
|
|
17
|
+
if (typeof value !== "object" ||
|
|
18
|
+
value === null ||
|
|
19
|
+
Array.isArray(value)) {
|
|
20
|
+
throw new InvalidScientificInputError(path + " must be an object.");
|
|
21
|
+
}
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
function requirePositiveFinite(value, path) {
|
|
25
|
+
if (typeof value !== "number" ||
|
|
26
|
+
!Number.isFinite(value) ||
|
|
27
|
+
value <= 0) {
|
|
28
|
+
throw new InvalidScientificInputError(path +
|
|
29
|
+
" must be finite and greater than zero.");
|
|
30
|
+
}
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
function settingEquals(left, right) {
|
|
34
|
+
const scale = Math.max(Math.abs(left), Math.abs(right), 1);
|
|
35
|
+
return (Math.abs(left - right) <=
|
|
36
|
+
Number.EPSILON * 32 * scale);
|
|
37
|
+
}
|
|
38
|
+
function validateSettingAgainstGrid(value, minimum, maximum, grid, path) {
|
|
39
|
+
if (value < minimum ||
|
|
40
|
+
value > maximum) {
|
|
41
|
+
throw new InvalidScientificInputError(path +
|
|
42
|
+
" lies outside the resolved equipment capability range.");
|
|
43
|
+
}
|
|
44
|
+
if (grid.kind ===
|
|
45
|
+
"discrete-values" &&
|
|
46
|
+
!grid.values.some((candidate) => settingEquals(candidate, value))) {
|
|
47
|
+
throw new InvalidScientificInputError(path +
|
|
48
|
+
" is not present in the resolved discrete equipment setting grid.");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function validateTarget(value) {
|
|
52
|
+
const record = requireRecord(value, "target");
|
|
53
|
+
if (record.schemaVersion !==
|
|
54
|
+
EXPOSURE_METER_TARGET_SCHEMA_VERSION) {
|
|
55
|
+
throw new InvalidScientificInputError('target.schemaVersion must be "' +
|
|
56
|
+
EXPOSURE_METER_TARGET_SCHEMA_VERSION +
|
|
57
|
+
'".');
|
|
58
|
+
}
|
|
59
|
+
if (record.scientificStatus !==
|
|
60
|
+
"approximation" ||
|
|
61
|
+
record.inputDomain !==
|
|
62
|
+
"relative-pre-exposure-linear-signal" ||
|
|
63
|
+
record.intendedConsumer !==
|
|
64
|
+
"exposure-mode-resolver" ||
|
|
65
|
+
record.automaticExposureResolved !==
|
|
66
|
+
false ||
|
|
67
|
+
record.sourceMeterSnapshotPreserved !==
|
|
68
|
+
true) {
|
|
69
|
+
throw new InvalidScientificInputError("target does not satisfy the exposure-mode resolver boundary.");
|
|
70
|
+
}
|
|
71
|
+
const targetId = record.targetId;
|
|
72
|
+
if (typeof targetId !== "string" ||
|
|
73
|
+
targetId.trim().length === 0) {
|
|
74
|
+
throw new InvalidScientificInputError("target.targetId must be a non-empty string.");
|
|
75
|
+
}
|
|
76
|
+
requireRecord(record.sourceMeterSnapshot, "target.sourceMeterSnapshot");
|
|
77
|
+
if (record.status === "resolved") {
|
|
78
|
+
requirePositiveFinite(record
|
|
79
|
+
.requiredExposureScaleToTarget, "target.requiredExposureScaleToTarget");
|
|
80
|
+
if (typeof record
|
|
81
|
+
.exposureOffsetStopsToTarget !==
|
|
82
|
+
"number" ||
|
|
83
|
+
!Number.isFinite(record
|
|
84
|
+
.exposureOffsetStopsToTarget)) {
|
|
85
|
+
throw new InvalidScientificInputError("target.exposureOffsetStopsToTarget must be finite.");
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else if (record.status !== "no-signal") {
|
|
89
|
+
throw new InvalidScientificInputError("target.status is invalid.");
|
|
90
|
+
}
|
|
91
|
+
return value;
|
|
92
|
+
}
|
|
93
|
+
function requireNonEmptyString(value, path) {
|
|
94
|
+
if (typeof value !== "string" ||
|
|
95
|
+
value.trim().length === 0) {
|
|
96
|
+
throw new InvalidScientificInputError(path + " must be a non-empty string.");
|
|
97
|
+
}
|
|
98
|
+
return value.trim();
|
|
99
|
+
}
|
|
100
|
+
function validateResolvedGrid(grid, minimum, maximum, path) {
|
|
101
|
+
if (grid.kind ===
|
|
102
|
+
"continuous-within-range") {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (grid.kind !== "discrete-values" ||
|
|
106
|
+
!Array.isArray(grid.values) ||
|
|
107
|
+
grid.values.length === 0) {
|
|
108
|
+
throw new InvalidScientificInputError(path +
|
|
109
|
+
" must be a continuous grid or a non-empty discrete-values grid.");
|
|
110
|
+
}
|
|
111
|
+
let previous = Number.NEGATIVE_INFINITY;
|
|
112
|
+
for (let index = 0; index < grid.values.length; index += 1) {
|
|
113
|
+
const value = requirePositiveFinite(grid.values[index], path +
|
|
114
|
+
".values[" +
|
|
115
|
+
index +
|
|
116
|
+
"]");
|
|
117
|
+
if (value <= previous) {
|
|
118
|
+
throw new InvalidScientificInputError(path +
|
|
119
|
+
".values must be strictly increasing with no duplicates.");
|
|
120
|
+
}
|
|
121
|
+
if (value < minimum ||
|
|
122
|
+
value > maximum) {
|
|
123
|
+
throw new InvalidScientificInputError(path +
|
|
124
|
+
".values must stay inside the resolved capability range.");
|
|
125
|
+
}
|
|
126
|
+
previous = value;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function validateCapabilities(value) {
|
|
130
|
+
if (value.schemaVersion !== "0.1.0" ||
|
|
131
|
+
value.scientificStatus !==
|
|
132
|
+
"approximation") {
|
|
133
|
+
throw new InvalidScientificInputError("capabilities do not satisfy the generic equipment exposure-capability boundary.");
|
|
134
|
+
}
|
|
135
|
+
requirePositiveFinite(value.selectedFocalLengthMm, "capabilities.selectedFocalLengthMm");
|
|
136
|
+
requirePositiveFinite(value.aperture
|
|
137
|
+
.widestAvailableFNumber, "capabilities.aperture.widestAvailableFNumber");
|
|
138
|
+
requirePositiveFinite(value.aperture
|
|
139
|
+
.narrowestAvailableFNumber, "capabilities.aperture.narrowestAvailableFNumber");
|
|
140
|
+
requirePositiveFinite(value.shutter.minimumSeconds, "capabilities.shutter.minimumSeconds");
|
|
141
|
+
requirePositiveFinite(value.shutter.maximumSeconds, "capabilities.shutter.maximumSeconds");
|
|
142
|
+
requirePositiveFinite(value.iso.minimum, "capabilities.iso.minimum");
|
|
143
|
+
requirePositiveFinite(value.iso.maximum, "capabilities.iso.maximum");
|
|
144
|
+
if (value.aperture
|
|
145
|
+
.widestAvailableFNumber >
|
|
146
|
+
value.aperture
|
|
147
|
+
.narrowestAvailableFNumber) {
|
|
148
|
+
throw new InvalidScientificInputError("capabilities aperture range is invalid.");
|
|
149
|
+
}
|
|
150
|
+
if (value.shutter.minimumSeconds >
|
|
151
|
+
value.shutter.maximumSeconds) {
|
|
152
|
+
throw new InvalidScientificInputError("capabilities shutter range is invalid.");
|
|
153
|
+
}
|
|
154
|
+
if (value.iso.minimum >
|
|
155
|
+
value.iso.maximum) {
|
|
156
|
+
throw new InvalidScientificInputError("capabilities ISO range is invalid.");
|
|
157
|
+
}
|
|
158
|
+
const hasAutoMinimum = value.iso.autoIsoMinimum !==
|
|
159
|
+
undefined;
|
|
160
|
+
const hasAutoMaximum = value.iso.autoIsoMaximum !==
|
|
161
|
+
undefined;
|
|
162
|
+
if (hasAutoMinimum !==
|
|
163
|
+
hasAutoMaximum) {
|
|
164
|
+
throw new InvalidScientificInputError("capabilities Auto ISO bounds must provide both minimum and maximum or neither.");
|
|
165
|
+
}
|
|
166
|
+
if (hasAutoMinimum &&
|
|
167
|
+
hasAutoMaximum) {
|
|
168
|
+
const autoMinimum = requirePositiveFinite(value.iso.autoIsoMinimum, "capabilities.iso.autoIsoMinimum");
|
|
169
|
+
const autoMaximum = requirePositiveFinite(value.iso.autoIsoMaximum, "capabilities.iso.autoIsoMaximum");
|
|
170
|
+
if (value.iso
|
|
171
|
+
.autoIsoAvailability !==
|
|
172
|
+
"supported" ||
|
|
173
|
+
autoMinimum >
|
|
174
|
+
autoMaximum ||
|
|
175
|
+
autoMinimum <
|
|
176
|
+
value.iso.minimum ||
|
|
177
|
+
autoMaximum >
|
|
178
|
+
value.iso.maximum) {
|
|
179
|
+
throw new InvalidScientificInputError("capabilities Auto ISO bounds must be ordered, inside the manual ISO range, and used only when Auto ISO is supported.");
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
requireNonEmptyString(value.bodyProfile.profileId, "capabilities.bodyProfile.profileId");
|
|
183
|
+
requireNonEmptyString(value.bodyProfile.profileVersion, "capabilities.bodyProfile.profileVersion");
|
|
184
|
+
requireNonEmptyString(value.lensProfile.profileId, "capabilities.lensProfile.profileId");
|
|
185
|
+
requireNonEmptyString(value.lensProfile.profileVersion, "capabilities.lensProfile.profileVersion");
|
|
186
|
+
if (value.iso
|
|
187
|
+
.autoIsoAvailability !==
|
|
188
|
+
"supported" &&
|
|
189
|
+
value.iso
|
|
190
|
+
.autoIsoAvailability !==
|
|
191
|
+
"unsupported" &&
|
|
192
|
+
value.iso
|
|
193
|
+
.autoIsoAvailability !==
|
|
194
|
+
"unknown") {
|
|
195
|
+
throw new InvalidScientificInputError("capabilities.iso.autoIsoAvailability is invalid.");
|
|
196
|
+
}
|
|
197
|
+
validateResolvedGrid(value.aperture.settingGrid, value.aperture
|
|
198
|
+
.widestAvailableFNumber, value.aperture
|
|
199
|
+
.narrowestAvailableFNumber, "capabilities.aperture.settingGrid");
|
|
200
|
+
validateResolvedGrid(value.shutter.settingGrid, value.shutter.minimumSeconds, value.shutter.maximumSeconds, "capabilities.shutter.settingGrid");
|
|
201
|
+
validateResolvedGrid(value.iso.settingGrid, value.iso.minimum, value.iso.maximum, "capabilities.iso.settingGrid");
|
|
202
|
+
}
|
|
203
|
+
function validateReferenceExposure(reference, capabilities) {
|
|
204
|
+
const aperture = requirePositiveFinite(reference.aperture, "referenceExposure.aperture");
|
|
205
|
+
const shutterSeconds = requirePositiveFinite(reference.shutterSeconds, "referenceExposure.shutterSeconds");
|
|
206
|
+
const iso = requirePositiveFinite(reference.iso, "referenceExposure.iso");
|
|
207
|
+
validateSettingAgainstGrid(aperture, capabilities.aperture
|
|
208
|
+
.widestAvailableFNumber, capabilities.aperture
|
|
209
|
+
.narrowestAvailableFNumber, capabilities.aperture
|
|
210
|
+
.settingGrid, "referenceExposure.aperture");
|
|
211
|
+
validateSettingAgainstGrid(shutterSeconds, capabilities.shutter
|
|
212
|
+
.minimumSeconds, capabilities.shutter
|
|
213
|
+
.maximumSeconds, capabilities.shutter
|
|
214
|
+
.settingGrid, "referenceExposure.shutterSeconds");
|
|
215
|
+
validateSettingAgainstGrid(iso, capabilities.iso.minimum, capabilities.iso.maximum, capabilities.iso.settingGrid, "referenceExposure.iso");
|
|
216
|
+
return {
|
|
217
|
+
aperture,
|
|
218
|
+
shutterSeconds,
|
|
219
|
+
iso
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function opticalExposureFactor(aperture, shutterSeconds, reference) {
|
|
223
|
+
const current = shutterSeconds /
|
|
224
|
+
(aperture * aperture);
|
|
225
|
+
const referenceOptical = reference.shutterSeconds /
|
|
226
|
+
(reference.aperture *
|
|
227
|
+
reference.aperture);
|
|
228
|
+
const factor = current /
|
|
229
|
+
referenceOptical;
|
|
230
|
+
if (!Number.isFinite(factor) ||
|
|
231
|
+
factor <= 0) {
|
|
232
|
+
throw new InvalidScientificInputError("Optical exposure factor must remain finite and greater than zero.");
|
|
233
|
+
}
|
|
234
|
+
return factor;
|
|
235
|
+
}
|
|
236
|
+
function targetResidual(target, achievedScale, limitingConstraint) {
|
|
237
|
+
if (target.status ===
|
|
238
|
+
"no-signal") {
|
|
239
|
+
return {
|
|
240
|
+
status: "target-unresolved",
|
|
241
|
+
state: "target-unresolved",
|
|
242
|
+
reason: "no-signal-target"
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
const targetScale = target
|
|
246
|
+
.requiredExposureScaleToTarget;
|
|
247
|
+
const targetStops = target
|
|
248
|
+
.exposureOffsetStopsToTarget;
|
|
249
|
+
const achievedStops = Math.log2(achievedScale);
|
|
250
|
+
const residualStops = Math.log2(targetScale /
|
|
251
|
+
achievedScale);
|
|
252
|
+
if (!Number.isFinite(achievedStops) ||
|
|
253
|
+
!Number.isFinite(residualStops)) {
|
|
254
|
+
throw new InvalidScientificInputError("Exposure target residual must remain finite.");
|
|
255
|
+
}
|
|
256
|
+
const toleranceStops = 1e-12;
|
|
257
|
+
const state = Math.abs(residualStops) <=
|
|
258
|
+
toleranceStops
|
|
259
|
+
? "matched"
|
|
260
|
+
: residualStops > 0
|
|
261
|
+
? "under-target"
|
|
262
|
+
: "over-target";
|
|
263
|
+
return {
|
|
264
|
+
status: "resolved",
|
|
265
|
+
state,
|
|
266
|
+
targetExposureStops: targetStops,
|
|
267
|
+
achievedExposureStops: achievedStops,
|
|
268
|
+
residualStops,
|
|
269
|
+
limitingConstraint
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
function nearestDiscreteIso(idealIso, values) {
|
|
273
|
+
const first = values[0];
|
|
274
|
+
const last = values[values.length - 1];
|
|
275
|
+
if (idealIso < first) {
|
|
276
|
+
return {
|
|
277
|
+
iso: first,
|
|
278
|
+
quantized: true,
|
|
279
|
+
clamped: "minimum",
|
|
280
|
+
limitingConstraint: "iso-minimum"
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
if (idealIso > last) {
|
|
284
|
+
return {
|
|
285
|
+
iso: last,
|
|
286
|
+
quantized: true,
|
|
287
|
+
clamped: "maximum",
|
|
288
|
+
limitingConstraint: "iso-maximum"
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
let selected = first;
|
|
292
|
+
let selectedDistance = Math.abs(Math.log2(idealIso / first));
|
|
293
|
+
for (let index = 1; index < values.length; index += 1) {
|
|
294
|
+
const candidate = values[index];
|
|
295
|
+
const distance = Math.abs(Math.log2(idealIso /
|
|
296
|
+
candidate));
|
|
297
|
+
if (distance <
|
|
298
|
+
selectedDistance -
|
|
299
|
+
1e-15 ||
|
|
300
|
+
(Math.abs(distance -
|
|
301
|
+
selectedDistance) <= 1e-15 &&
|
|
302
|
+
candidate < selected)) {
|
|
303
|
+
selected = candidate;
|
|
304
|
+
selectedDistance =
|
|
305
|
+
distance;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
const quantized = !settingEquals(selected, idealIso);
|
|
309
|
+
return {
|
|
310
|
+
iso: selected,
|
|
311
|
+
quantized,
|
|
312
|
+
clamped: false,
|
|
313
|
+
limitingConstraint: quantized
|
|
314
|
+
? "iso-grid-quantization"
|
|
315
|
+
: "none"
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
function autoIsoBounds(capabilities) {
|
|
319
|
+
return {
|
|
320
|
+
minimum: capabilities.iso
|
|
321
|
+
.autoIsoMinimum ??
|
|
322
|
+
capabilities.iso.minimum,
|
|
323
|
+
maximum: capabilities.iso
|
|
324
|
+
.autoIsoMaximum ??
|
|
325
|
+
capabilities.iso.maximum
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
function resolveAutomaticIso(idealIso, capabilities) {
|
|
329
|
+
const grid = capabilities.iso.settingGrid;
|
|
330
|
+
const bounds = autoIsoBounds(capabilities);
|
|
331
|
+
if (grid.kind ===
|
|
332
|
+
"discrete-values") {
|
|
333
|
+
const autoValues = grid.values.filter((value) => value >= bounds.minimum &&
|
|
334
|
+
value <= bounds.maximum);
|
|
335
|
+
if (autoValues.length === 0) {
|
|
336
|
+
throw new InvalidScientificInputError("Auto ISO bounds contain no selectable values from the resolved ISO grid.");
|
|
337
|
+
}
|
|
338
|
+
const discrete = nearestDiscreteIso(idealIso, autoValues);
|
|
339
|
+
return {
|
|
340
|
+
...discrete,
|
|
341
|
+
kind: "discrete"
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
if (idealIso <
|
|
345
|
+
bounds.minimum) {
|
|
346
|
+
return {
|
|
347
|
+
iso: bounds.minimum,
|
|
348
|
+
kind: "continuous",
|
|
349
|
+
quantized: false,
|
|
350
|
+
clamped: "minimum",
|
|
351
|
+
limitingConstraint: "iso-minimum"
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
if (idealIso >
|
|
355
|
+
bounds.maximum) {
|
|
356
|
+
return {
|
|
357
|
+
iso: bounds.maximum,
|
|
358
|
+
kind: "continuous",
|
|
359
|
+
quantized: false,
|
|
360
|
+
clamped: "maximum",
|
|
361
|
+
limitingConstraint: "iso-maximum"
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
return {
|
|
365
|
+
iso: idealIso,
|
|
366
|
+
kind: "continuous",
|
|
367
|
+
quantized: false,
|
|
368
|
+
clamped: false,
|
|
369
|
+
limitingConstraint: "none"
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
function nearestDiscreteShutter(idealShutterSeconds, values) {
|
|
373
|
+
const first = values[0];
|
|
374
|
+
const last = values[values.length - 1];
|
|
375
|
+
if (idealShutterSeconds < first) {
|
|
376
|
+
return {
|
|
377
|
+
shutterSeconds: first,
|
|
378
|
+
quantized: true,
|
|
379
|
+
clamped: "minimum",
|
|
380
|
+
limitingConstraint: "shutter-minimum"
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
if (idealShutterSeconds > last) {
|
|
384
|
+
return {
|
|
385
|
+
shutterSeconds: last,
|
|
386
|
+
quantized: true,
|
|
387
|
+
clamped: "maximum",
|
|
388
|
+
limitingConstraint: "shutter-maximum"
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
let selected = first;
|
|
392
|
+
let selectedDistance = Math.abs(Math.log2(idealShutterSeconds /
|
|
393
|
+
first));
|
|
394
|
+
for (let index = 1; index < values.length; index += 1) {
|
|
395
|
+
const candidate = values[index];
|
|
396
|
+
const distance = Math.abs(Math.log2(idealShutterSeconds /
|
|
397
|
+
candidate));
|
|
398
|
+
if (distance <
|
|
399
|
+
selectedDistance -
|
|
400
|
+
1e-15 ||
|
|
401
|
+
(Math.abs(distance -
|
|
402
|
+
selectedDistance) <= 1e-15 &&
|
|
403
|
+
candidate < selected)) {
|
|
404
|
+
selected = candidate;
|
|
405
|
+
selectedDistance =
|
|
406
|
+
distance;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
const quantized = !settingEquals(selected, idealShutterSeconds);
|
|
410
|
+
return {
|
|
411
|
+
shutterSeconds: selected,
|
|
412
|
+
quantized,
|
|
413
|
+
clamped: false,
|
|
414
|
+
limitingConstraint: quantized
|
|
415
|
+
? "shutter-grid-quantization"
|
|
416
|
+
: "none"
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
function resolveAutomaticShutter(idealShutterSeconds, capabilities) {
|
|
420
|
+
const grid = capabilities.shutter.settingGrid;
|
|
421
|
+
if (grid.kind ===
|
|
422
|
+
"discrete-values") {
|
|
423
|
+
const discrete = nearestDiscreteShutter(idealShutterSeconds, grid.values);
|
|
424
|
+
return {
|
|
425
|
+
...discrete,
|
|
426
|
+
kind: "discrete"
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
if (idealShutterSeconds <
|
|
430
|
+
capabilities.shutter
|
|
431
|
+
.minimumSeconds) {
|
|
432
|
+
return {
|
|
433
|
+
shutterSeconds: capabilities.shutter
|
|
434
|
+
.minimumSeconds,
|
|
435
|
+
kind: "continuous",
|
|
436
|
+
quantized: false,
|
|
437
|
+
clamped: "minimum",
|
|
438
|
+
limitingConstraint: "shutter-minimum"
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
if (idealShutterSeconds >
|
|
442
|
+
capabilities.shutter
|
|
443
|
+
.maximumSeconds) {
|
|
444
|
+
return {
|
|
445
|
+
shutterSeconds: capabilities.shutter
|
|
446
|
+
.maximumSeconds,
|
|
447
|
+
kind: "continuous",
|
|
448
|
+
quantized: false,
|
|
449
|
+
clamped: "maximum",
|
|
450
|
+
limitingConstraint: "shutter-maximum"
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
return {
|
|
454
|
+
shutterSeconds: idealShutterSeconds,
|
|
455
|
+
kind: "continuous",
|
|
456
|
+
quantized: false,
|
|
457
|
+
clamped: false,
|
|
458
|
+
limitingConstraint: "none"
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
function minimumSelectableIso(capabilities) {
|
|
462
|
+
const grid = capabilities.iso.settingGrid;
|
|
463
|
+
const bounds = autoIsoBounds(capabilities);
|
|
464
|
+
if (grid.kind ===
|
|
465
|
+
"continuous-within-range") {
|
|
466
|
+
return bounds.minimum;
|
|
467
|
+
}
|
|
468
|
+
const first = grid.values.find((value) => value >= bounds.minimum &&
|
|
469
|
+
value <= bounds.maximum);
|
|
470
|
+
if (first === undefined) {
|
|
471
|
+
throw new InvalidScientificInputError("Auto ISO bounds contain no selectable values from the resolved ISO grid.");
|
|
472
|
+
}
|
|
473
|
+
return first;
|
|
474
|
+
}
|
|
475
|
+
function selectShutterNotLongerThanTarget(targetSeconds, capabilities) {
|
|
476
|
+
const minimum = capabilities.shutter.minimumSeconds;
|
|
477
|
+
const maximum = capabilities.shutter.maximumSeconds;
|
|
478
|
+
const grid = capabilities.shutter.settingGrid;
|
|
479
|
+
if (grid.kind === "continuous-within-range") {
|
|
480
|
+
if (targetSeconds < minimum) {
|
|
481
|
+
return {
|
|
482
|
+
shutterSeconds: minimum,
|
|
483
|
+
kind: "continuous",
|
|
484
|
+
quantized: false,
|
|
485
|
+
clamped: "minimum",
|
|
486
|
+
limitingConstraint: "shutter-minimum"
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
if (targetSeconds > maximum) {
|
|
490
|
+
return {
|
|
491
|
+
shutterSeconds: maximum,
|
|
492
|
+
kind: "continuous",
|
|
493
|
+
quantized: false,
|
|
494
|
+
clamped: "maximum",
|
|
495
|
+
limitingConstraint: "shutter-maximum"
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
return {
|
|
499
|
+
shutterSeconds: targetSeconds,
|
|
500
|
+
kind: "continuous",
|
|
501
|
+
quantized: false,
|
|
502
|
+
clamped: false,
|
|
503
|
+
limitingConstraint: "none"
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
const values = grid.values;
|
|
507
|
+
const first = values[0];
|
|
508
|
+
const last = values[values.length - 1];
|
|
509
|
+
if (targetSeconds < first) {
|
|
510
|
+
return {
|
|
511
|
+
shutterSeconds: first,
|
|
512
|
+
kind: "discrete",
|
|
513
|
+
quantized: true,
|
|
514
|
+
clamped: "minimum",
|
|
515
|
+
limitingConstraint: "shutter-minimum"
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
if (targetSeconds >= last) {
|
|
519
|
+
return {
|
|
520
|
+
shutterSeconds: targetSeconds > last
|
|
521
|
+
? last
|
|
522
|
+
: targetSeconds,
|
|
523
|
+
kind: "discrete",
|
|
524
|
+
quantized: !settingEquals(targetSeconds, last),
|
|
525
|
+
clamped: targetSeconds > last
|
|
526
|
+
? "maximum"
|
|
527
|
+
: false,
|
|
528
|
+
limitingConstraint: targetSeconds > last
|
|
529
|
+
? "shutter-maximum"
|
|
530
|
+
: "none"
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
let selected = first;
|
|
534
|
+
for (const candidate of values) {
|
|
535
|
+
if (candidate > targetSeconds) {
|
|
536
|
+
break;
|
|
537
|
+
}
|
|
538
|
+
selected = candidate;
|
|
539
|
+
}
|
|
540
|
+
const quantized = !settingEquals(selected, targetSeconds);
|
|
541
|
+
return {
|
|
542
|
+
shutterSeconds: selected,
|
|
543
|
+
kind: "discrete",
|
|
544
|
+
quantized,
|
|
545
|
+
clamped: false,
|
|
546
|
+
limitingConstraint: quantized
|
|
547
|
+
? "shutter-grid-quantization"
|
|
548
|
+
: "none"
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
function nearestDiscreteAperture(idealFNumber, values) {
|
|
552
|
+
const first = values[0];
|
|
553
|
+
const last = values[values.length - 1];
|
|
554
|
+
if (idealFNumber < first) {
|
|
555
|
+
return {
|
|
556
|
+
aperture: first,
|
|
557
|
+
quantized: true,
|
|
558
|
+
clamped: "widest",
|
|
559
|
+
limitingConstraint: "aperture-widest"
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
if (idealFNumber > last) {
|
|
563
|
+
return {
|
|
564
|
+
aperture: last,
|
|
565
|
+
quantized: true,
|
|
566
|
+
clamped: "narrowest",
|
|
567
|
+
limitingConstraint: "aperture-narrowest"
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
let selected = first;
|
|
571
|
+
let selectedDistance = Math.abs(Math.log2(idealFNumber /
|
|
572
|
+
first));
|
|
573
|
+
for (let index = 1; index < values.length; index += 1) {
|
|
574
|
+
const candidate = values[index];
|
|
575
|
+
const distance = Math.abs(Math.log2(idealFNumber /
|
|
576
|
+
candidate));
|
|
577
|
+
if (distance <
|
|
578
|
+
selectedDistance -
|
|
579
|
+
1e-15 ||
|
|
580
|
+
(Math.abs(distance -
|
|
581
|
+
selectedDistance) <= 1e-15 &&
|
|
582
|
+
candidate > selected)) {
|
|
583
|
+
selected = candidate;
|
|
584
|
+
selectedDistance =
|
|
585
|
+
distance;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
const quantized = !settingEquals(selected, idealFNumber);
|
|
589
|
+
return {
|
|
590
|
+
aperture: selected,
|
|
591
|
+
quantized,
|
|
592
|
+
clamped: false,
|
|
593
|
+
limitingConstraint: quantized
|
|
594
|
+
? "aperture-grid-quantization"
|
|
595
|
+
: "none"
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
function resolveAutomaticAperture(idealFNumber, capabilities) {
|
|
599
|
+
const minimum = capabilities.aperture
|
|
600
|
+
.widestAvailableFNumber;
|
|
601
|
+
const maximum = capabilities.aperture
|
|
602
|
+
.narrowestAvailableFNumber;
|
|
603
|
+
const grid = capabilities.aperture.settingGrid;
|
|
604
|
+
if (grid.kind === "discrete-values") {
|
|
605
|
+
return {
|
|
606
|
+
...nearestDiscreteAperture(idealFNumber, grid.values),
|
|
607
|
+
kind: "discrete"
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
if (idealFNumber < minimum) {
|
|
611
|
+
return {
|
|
612
|
+
aperture: minimum,
|
|
613
|
+
kind: "continuous",
|
|
614
|
+
quantized: false,
|
|
615
|
+
clamped: "widest",
|
|
616
|
+
limitingConstraint: "aperture-widest"
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
if (idealFNumber > maximum) {
|
|
620
|
+
return {
|
|
621
|
+
aperture: maximum,
|
|
622
|
+
kind: "continuous",
|
|
623
|
+
quantized: false,
|
|
624
|
+
clamped: "narrowest",
|
|
625
|
+
limitingConstraint: "aperture-narrowest"
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
return {
|
|
629
|
+
aperture: idealFNumber,
|
|
630
|
+
kind: "continuous",
|
|
631
|
+
quantized: false,
|
|
632
|
+
clamped: false,
|
|
633
|
+
limitingConstraint: "none"
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
function selectApertureNotWiderThanTarget(idealFNumber, capabilities) {
|
|
637
|
+
const minimum = capabilities.aperture
|
|
638
|
+
.widestAvailableFNumber;
|
|
639
|
+
const maximum = capabilities.aperture
|
|
640
|
+
.narrowestAvailableFNumber;
|
|
641
|
+
const grid = capabilities.aperture.settingGrid;
|
|
642
|
+
if (grid.kind === "continuous-within-range") {
|
|
643
|
+
if (idealFNumber < minimum) {
|
|
644
|
+
return {
|
|
645
|
+
aperture: minimum,
|
|
646
|
+
kind: "continuous",
|
|
647
|
+
quantized: false,
|
|
648
|
+
clamped: "widest",
|
|
649
|
+
limitingConstraint: "aperture-widest"
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
if (idealFNumber > maximum) {
|
|
653
|
+
return {
|
|
654
|
+
aperture: maximum,
|
|
655
|
+
kind: "continuous",
|
|
656
|
+
quantized: false,
|
|
657
|
+
clamped: "narrowest",
|
|
658
|
+
limitingConstraint: "aperture-narrowest"
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
return {
|
|
662
|
+
aperture: idealFNumber,
|
|
663
|
+
kind: "continuous",
|
|
664
|
+
quantized: false,
|
|
665
|
+
clamped: false,
|
|
666
|
+
limitingConstraint: "none"
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
const values = grid.values;
|
|
670
|
+
const first = values[0];
|
|
671
|
+
const last = values[values.length - 1];
|
|
672
|
+
if (idealFNumber <= first) {
|
|
673
|
+
return {
|
|
674
|
+
aperture: first,
|
|
675
|
+
kind: "discrete",
|
|
676
|
+
quantized: !settingEquals(idealFNumber, first),
|
|
677
|
+
clamped: idealFNumber < first
|
|
678
|
+
? "widest"
|
|
679
|
+
: false,
|
|
680
|
+
limitingConstraint: idealFNumber < first
|
|
681
|
+
? "aperture-widest"
|
|
682
|
+
: "none"
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
if (idealFNumber > last) {
|
|
686
|
+
return {
|
|
687
|
+
aperture: last,
|
|
688
|
+
kind: "discrete",
|
|
689
|
+
quantized: true,
|
|
690
|
+
clamped: "narrowest",
|
|
691
|
+
limitingConstraint: "aperture-narrowest"
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
const selected = values.find((candidate) => candidate >= idealFNumber) ?? last;
|
|
695
|
+
const quantized = !settingEquals(selected, idealFNumber);
|
|
696
|
+
return {
|
|
697
|
+
aperture: selected,
|
|
698
|
+
kind: "discrete",
|
|
699
|
+
quantized,
|
|
700
|
+
clamped: false,
|
|
701
|
+
limitingConstraint: quantized
|
|
702
|
+
? "aperture-grid-quantization"
|
|
703
|
+
: "none"
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
function idealApertureForTarget(targetScale, shutterSeconds, iso, reference) {
|
|
707
|
+
const numerator = reference.aperture *
|
|
708
|
+
reference.aperture *
|
|
709
|
+
(shutterSeconds /
|
|
710
|
+
reference.shutterSeconds) *
|
|
711
|
+
(iso /
|
|
712
|
+
reference.iso);
|
|
713
|
+
const squared = numerator / targetScale;
|
|
714
|
+
const aperture = Math.sqrt(squared);
|
|
715
|
+
if (!Number.isFinite(aperture) ||
|
|
716
|
+
aperture <= 0) {
|
|
717
|
+
throw new InvalidScientificInputError("Ideal automatic aperture must remain finite and greater than zero.");
|
|
718
|
+
}
|
|
719
|
+
return aperture;
|
|
720
|
+
}
|
|
721
|
+
function validateProgramLineForResolution(value, referenceExposure, capabilities) {
|
|
722
|
+
const profile = parseExposureProgramLineProfile(value);
|
|
723
|
+
const toleranceStops = 1e-9;
|
|
724
|
+
for (const node of profile.nodes) {
|
|
725
|
+
if (node.aperture <
|
|
726
|
+
capabilities.aperture
|
|
727
|
+
.widestAvailableFNumber ||
|
|
728
|
+
node.aperture >
|
|
729
|
+
capabilities.aperture
|
|
730
|
+
.narrowestAvailableFNumber) {
|
|
731
|
+
throw new InvalidScientificInputError("Program-line aperture lies outside the resolved equipment capability range.");
|
|
732
|
+
}
|
|
733
|
+
if (node.shutterSeconds <
|
|
734
|
+
capabilities.shutter
|
|
735
|
+
.minimumSeconds ||
|
|
736
|
+
node.shutterSeconds >
|
|
737
|
+
capabilities.shutter
|
|
738
|
+
.maximumSeconds) {
|
|
739
|
+
throw new InvalidScientificInputError("Program-line shutter duration lies outside the resolved equipment capability range.");
|
|
740
|
+
}
|
|
741
|
+
const computedStops = Math.log2(opticalExposureFactor(node.aperture, node.shutterSeconds, referenceExposure));
|
|
742
|
+
if (!Number.isFinite(computedStops) ||
|
|
743
|
+
Math.abs(computedStops -
|
|
744
|
+
node
|
|
745
|
+
.opticalExposureStopsFromReference) > toleranceStops) {
|
|
746
|
+
throw new InvalidScientificInputError("Program-line node opticalExposureStopsFromReference is inconsistent with its aperture/shutter pair and the selected reference exposure.");
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
return profile;
|
|
750
|
+
}
|
|
751
|
+
function selectProgramLineSettings(requestedOpticalStops, profile, capabilities) {
|
|
752
|
+
if (!Number.isFinite(requestedOpticalStops)) {
|
|
753
|
+
throw new InvalidScientificInputError("Requested program-line optical exposure stops must be finite.");
|
|
754
|
+
}
|
|
755
|
+
const first = profile.nodes[0];
|
|
756
|
+
const last = profile.nodes[profile.nodes.length - 1];
|
|
757
|
+
let position;
|
|
758
|
+
let selectedStops;
|
|
759
|
+
let interpolationPhase;
|
|
760
|
+
let idealAperture;
|
|
761
|
+
let idealShutterSeconds;
|
|
762
|
+
if (requestedOpticalStops <=
|
|
763
|
+
first
|
|
764
|
+
.opticalExposureStopsFromReference) {
|
|
765
|
+
position =
|
|
766
|
+
requestedOpticalStops <
|
|
767
|
+
first
|
|
768
|
+
.opticalExposureStopsFromReference
|
|
769
|
+
? "below-line"
|
|
770
|
+
: "within-line";
|
|
771
|
+
selectedStops =
|
|
772
|
+
first
|
|
773
|
+
.opticalExposureStopsFromReference;
|
|
774
|
+
interpolationPhase = null;
|
|
775
|
+
idealAperture = first.aperture;
|
|
776
|
+
idealShutterSeconds =
|
|
777
|
+
first.shutterSeconds;
|
|
778
|
+
}
|
|
779
|
+
else if (requestedOpticalStops >=
|
|
780
|
+
last
|
|
781
|
+
.opticalExposureStopsFromReference) {
|
|
782
|
+
position =
|
|
783
|
+
requestedOpticalStops >
|
|
784
|
+
last
|
|
785
|
+
.opticalExposureStopsFromReference
|
|
786
|
+
? "above-line"
|
|
787
|
+
: "within-line";
|
|
788
|
+
selectedStops =
|
|
789
|
+
last
|
|
790
|
+
.opticalExposureStopsFromReference;
|
|
791
|
+
interpolationPhase = null;
|
|
792
|
+
idealAperture = last.aperture;
|
|
793
|
+
idealShutterSeconds =
|
|
794
|
+
last.shutterSeconds;
|
|
795
|
+
}
|
|
796
|
+
else {
|
|
797
|
+
position = "within-line";
|
|
798
|
+
let left = first;
|
|
799
|
+
let right = profile.nodes[1];
|
|
800
|
+
for (let index = 1; index < profile.nodes.length; index += 1) {
|
|
801
|
+
const candidate = profile.nodes[index];
|
|
802
|
+
if (requestedOpticalStops <=
|
|
803
|
+
candidate
|
|
804
|
+
.opticalExposureStopsFromReference) {
|
|
805
|
+
left =
|
|
806
|
+
profile.nodes[index - 1];
|
|
807
|
+
right = candidate;
|
|
808
|
+
break;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
const span = right
|
|
812
|
+
.opticalExposureStopsFromReference -
|
|
813
|
+
left
|
|
814
|
+
.opticalExposureStopsFromReference;
|
|
815
|
+
const phase = (requestedOpticalStops -
|
|
816
|
+
left
|
|
817
|
+
.opticalExposureStopsFromReference) /
|
|
818
|
+
span;
|
|
819
|
+
interpolationPhase = phase;
|
|
820
|
+
selectedStops =
|
|
821
|
+
requestedOpticalStops;
|
|
822
|
+
const logAperture = Math.log2(left.aperture) +
|
|
823
|
+
phase *
|
|
824
|
+
(Math.log2(right.aperture) -
|
|
825
|
+
Math.log2(left.aperture));
|
|
826
|
+
const logShutter = Math.log2(left.shutterSeconds) +
|
|
827
|
+
phase *
|
|
828
|
+
(Math.log2(right.shutterSeconds) -
|
|
829
|
+
Math.log2(left.shutterSeconds));
|
|
830
|
+
idealAperture =
|
|
831
|
+
Math.pow(2, logAperture);
|
|
832
|
+
idealShutterSeconds =
|
|
833
|
+
Math.pow(2, logShutter);
|
|
834
|
+
}
|
|
835
|
+
const aperture = resolveAutomaticAperture(idealAperture, capabilities);
|
|
836
|
+
const shutter = resolveAutomaticShutter(idealShutterSeconds, capabilities);
|
|
837
|
+
return {
|
|
838
|
+
profileId: profile.profileId,
|
|
839
|
+
profileVersion: profile.profileVersion,
|
|
840
|
+
requestedOpticalExposureStopsFromReference: requestedOpticalStops,
|
|
841
|
+
selectedOpticalExposureStopsFromReference: selectedStops,
|
|
842
|
+
position,
|
|
843
|
+
interpolationPhase,
|
|
844
|
+
idealAperture,
|
|
845
|
+
idealShutterSeconds,
|
|
846
|
+
resolvedAperture: aperture.aperture,
|
|
847
|
+
resolvedShutterSeconds: shutter.shutterSeconds,
|
|
848
|
+
apertureQuantized: aperture.quantized,
|
|
849
|
+
shutterQuantized: shutter.quantized,
|
|
850
|
+
apertureClamped: aperture.clamped,
|
|
851
|
+
shutterClamped: shutter.clamped
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
function programLineResidualConstraint(selection) {
|
|
855
|
+
if (selection.apertureClamped ===
|
|
856
|
+
"widest") {
|
|
857
|
+
return "aperture-widest";
|
|
858
|
+
}
|
|
859
|
+
if (selection.apertureClamped ===
|
|
860
|
+
"narrowest") {
|
|
861
|
+
return "aperture-narrowest";
|
|
862
|
+
}
|
|
863
|
+
if (selection.shutterClamped ===
|
|
864
|
+
"minimum") {
|
|
865
|
+
return "shutter-minimum";
|
|
866
|
+
}
|
|
867
|
+
if (selection.shutterClamped ===
|
|
868
|
+
"maximum") {
|
|
869
|
+
return "shutter-maximum";
|
|
870
|
+
}
|
|
871
|
+
if (selection.apertureQuantized) {
|
|
872
|
+
return "aperture-grid-quantization";
|
|
873
|
+
}
|
|
874
|
+
if (selection.shutterQuantized) {
|
|
875
|
+
return "shutter-grid-quantization";
|
|
876
|
+
}
|
|
877
|
+
return "none";
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* Resolves Manual exposure with either manual ISO or Auto ISO.
|
|
881
|
+
*
|
|
882
|
+
* Aperture and shutter are always caller-owned and are never changed.
|
|
883
|
+
* The relative meter target is normalized by an explicit reference exposure
|
|
884
|
+
* anchor; no ISO-100 or other hidden absolute calibration is assumed.
|
|
885
|
+
*/
|
|
886
|
+
export function resolveManualExposureMode(input) {
|
|
887
|
+
const target = validateTarget(input.target);
|
|
888
|
+
validateCapabilities(input.capabilities);
|
|
889
|
+
const referenceExposure = validateReferenceExposure(input.referenceExposure, input.capabilities);
|
|
890
|
+
const manualAperture = requirePositiveFinite(input.manualAperture, "manualAperture");
|
|
891
|
+
const manualShutterSeconds = requirePositiveFinite(input.manualShutterSeconds, "manualShutterSeconds");
|
|
892
|
+
validateSettingAgainstGrid(manualAperture, input.capabilities.aperture
|
|
893
|
+
.widestAvailableFNumber, input.capabilities.aperture
|
|
894
|
+
.narrowestAvailableFNumber, input.capabilities.aperture
|
|
895
|
+
.settingGrid, "manualAperture");
|
|
896
|
+
validateSettingAgainstGrid(manualShutterSeconds, input.capabilities.shutter
|
|
897
|
+
.minimumSeconds, input.capabilities.shutter
|
|
898
|
+
.maximumSeconds, input.capabilities.shutter
|
|
899
|
+
.settingGrid, "manualShutterSeconds");
|
|
900
|
+
const base = {
|
|
901
|
+
resolverVersion: EXPOSURE_MODE_RESOLVER_VERSION,
|
|
902
|
+
mode: "manual",
|
|
903
|
+
axisOwnership: {
|
|
904
|
+
aperture: "manual",
|
|
905
|
+
shutter: "manual",
|
|
906
|
+
iso: input.isoControl.kind ===
|
|
907
|
+
"manual"
|
|
908
|
+
? "manual"
|
|
909
|
+
: "automatic"
|
|
910
|
+
},
|
|
911
|
+
targetId: target.targetId,
|
|
912
|
+
targetSourceMeterSnapshot: target.sourceMeterSnapshot,
|
|
913
|
+
capabilityProfiles: {
|
|
914
|
+
bodyProfileId: input.capabilities
|
|
915
|
+
.bodyProfile.profileId,
|
|
916
|
+
bodyProfileVersion: input.capabilities
|
|
917
|
+
.bodyProfile.profileVersion,
|
|
918
|
+
lensProfileId: input.capabilities
|
|
919
|
+
.lensProfile.profileId,
|
|
920
|
+
lensProfileVersion: input.capabilities
|
|
921
|
+
.lensProfile.profileVersion
|
|
922
|
+
},
|
|
923
|
+
selectedFocalLengthMm: input.capabilities
|
|
924
|
+
.selectedFocalLengthMm,
|
|
925
|
+
referenceExposure,
|
|
926
|
+
manualAperture,
|
|
927
|
+
manualShutterSeconds,
|
|
928
|
+
apertureMutatedByResolver: false,
|
|
929
|
+
shutterMutatedByResolver: false,
|
|
930
|
+
exposureCompensationAppliedByResolver: false,
|
|
931
|
+
meterRecomputedByResolver: false,
|
|
932
|
+
isoNoiseOrGainTopologyInferred: false,
|
|
933
|
+
flashPolicyApplied: false,
|
|
934
|
+
safetyShiftApplied: false
|
|
935
|
+
};
|
|
936
|
+
const opticalFactor = opticalExposureFactor(manualAperture, manualShutterSeconds, referenceExposure);
|
|
937
|
+
if (input.isoControl.kind !==
|
|
938
|
+
"manual" &&
|
|
939
|
+
input.isoControl.kind !==
|
|
940
|
+
"automatic") {
|
|
941
|
+
throw new InvalidScientificInputError("isoControl.kind is invalid.");
|
|
942
|
+
}
|
|
943
|
+
if (input.isoControl.kind ===
|
|
944
|
+
"automatic" &&
|
|
945
|
+
input.isoControl
|
|
946
|
+
.quantizationPolicy !==
|
|
947
|
+
"nearest-log2-lower-on-tie") {
|
|
948
|
+
throw new InvalidScientificInputError('isoControl.quantizationPolicy must be "nearest-log2-lower-on-tie".');
|
|
949
|
+
}
|
|
950
|
+
if (input.isoControl.kind ===
|
|
951
|
+
"manual") {
|
|
952
|
+
const manualIso = requirePositiveFinite(input.isoControl.iso, "isoControl.iso");
|
|
953
|
+
validateSettingAgainstGrid(manualIso, input.capabilities.iso.minimum, input.capabilities.iso.maximum, input.capabilities.iso.settingGrid, "isoControl.iso");
|
|
954
|
+
const achievedScale = opticalFactor *
|
|
955
|
+
(manualIso /
|
|
956
|
+
referenceExposure.iso);
|
|
957
|
+
return {
|
|
958
|
+
...base,
|
|
959
|
+
status: "resolved",
|
|
960
|
+
isoControl: "manual",
|
|
961
|
+
resolvedSettings: {
|
|
962
|
+
aperture: manualAperture,
|
|
963
|
+
shutterSeconds: manualShutterSeconds,
|
|
964
|
+
iso: manualIso
|
|
965
|
+
},
|
|
966
|
+
idealIsoBeforeConstraints: null,
|
|
967
|
+
isoResolution: {
|
|
968
|
+
kind: "manual-preserved",
|
|
969
|
+
quantized: false,
|
|
970
|
+
clamped: false
|
|
971
|
+
},
|
|
972
|
+
targetResidual: targetResidual(target, achievedScale, "none")
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
const availability = input.capabilities.iso
|
|
976
|
+
.autoIsoAvailability;
|
|
977
|
+
if (availability !== "supported") {
|
|
978
|
+
return {
|
|
979
|
+
...base,
|
|
980
|
+
status: "blocked",
|
|
981
|
+
isoControl: "automatic",
|
|
982
|
+
resolvedSettings: {
|
|
983
|
+
aperture: manualAperture,
|
|
984
|
+
shutterSeconds: manualShutterSeconds
|
|
985
|
+
},
|
|
986
|
+
idealIsoBeforeConstraints: null,
|
|
987
|
+
blocker: availability ===
|
|
988
|
+
"unknown"
|
|
989
|
+
? "auto-iso-unknown"
|
|
990
|
+
: "auto-iso-unsupported",
|
|
991
|
+
targetResidual: {
|
|
992
|
+
status: "target-unresolved",
|
|
993
|
+
state: "target-unresolved",
|
|
994
|
+
reason: "auto-iso-unavailable"
|
|
995
|
+
}
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
if (target.status === "no-signal") {
|
|
999
|
+
return {
|
|
1000
|
+
...base,
|
|
1001
|
+
status: "blocked",
|
|
1002
|
+
isoControl: "automatic",
|
|
1003
|
+
resolvedSettings: {
|
|
1004
|
+
aperture: manualAperture,
|
|
1005
|
+
shutterSeconds: manualShutterSeconds
|
|
1006
|
+
},
|
|
1007
|
+
idealIsoBeforeConstraints: null,
|
|
1008
|
+
blocker: "target-no-signal",
|
|
1009
|
+
targetResidual: {
|
|
1010
|
+
status: "target-unresolved",
|
|
1011
|
+
state: "target-unresolved",
|
|
1012
|
+
reason: "no-signal-target"
|
|
1013
|
+
}
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
const idealIso = referenceExposure.iso *
|
|
1017
|
+
(target
|
|
1018
|
+
.requiredExposureScaleToTarget /
|
|
1019
|
+
opticalFactor);
|
|
1020
|
+
if (!Number.isFinite(idealIso) ||
|
|
1021
|
+
idealIso <= 0) {
|
|
1022
|
+
throw new InvalidScientificInputError("Ideal Auto ISO must remain finite and greater than zero.");
|
|
1023
|
+
}
|
|
1024
|
+
const automatic = resolveAutomaticIso(idealIso, input.capabilities);
|
|
1025
|
+
const achievedScale = opticalFactor *
|
|
1026
|
+
(automatic.iso /
|
|
1027
|
+
referenceExposure.iso);
|
|
1028
|
+
const residual = targetResidual(target, achievedScale, automatic
|
|
1029
|
+
.limitingConstraint);
|
|
1030
|
+
return {
|
|
1031
|
+
...base,
|
|
1032
|
+
status: "resolved",
|
|
1033
|
+
isoControl: "automatic",
|
|
1034
|
+
resolvedSettings: {
|
|
1035
|
+
aperture: manualAperture,
|
|
1036
|
+
shutterSeconds: manualShutterSeconds,
|
|
1037
|
+
iso: automatic.iso
|
|
1038
|
+
},
|
|
1039
|
+
idealIsoBeforeConstraints: idealIso,
|
|
1040
|
+
isoResolution: {
|
|
1041
|
+
kind: automatic.kind,
|
|
1042
|
+
quantizationPolicy: input.isoControl
|
|
1043
|
+
.quantizationPolicy,
|
|
1044
|
+
quantized: automatic.quantized,
|
|
1045
|
+
clamped: automatic.clamped
|
|
1046
|
+
},
|
|
1047
|
+
targetResidual: residual
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Resolves Aperture Priority with manual ISO.
|
|
1052
|
+
*
|
|
1053
|
+
* Aperture and ISO remain caller-owned. Shutter is the only automatic axis.
|
|
1054
|
+
* The target/reference relationship is the same relative exposure model used
|
|
1055
|
+
* by Manual + Auto ISO; no new mode-specific exposure equation is introduced.
|
|
1056
|
+
*/
|
|
1057
|
+
export function resolveAperturePriorityExposureMode(input) {
|
|
1058
|
+
const target = validateTarget(input.target);
|
|
1059
|
+
validateCapabilities(input.capabilities);
|
|
1060
|
+
const referenceExposure = validateReferenceExposure(input.referenceExposure, input.capabilities);
|
|
1061
|
+
const manualAperture = requirePositiveFinite(input.manualAperture, "manualAperture");
|
|
1062
|
+
const manualIso = requirePositiveFinite(input.manualIso, "manualIso");
|
|
1063
|
+
validateSettingAgainstGrid(manualAperture, input.capabilities.aperture
|
|
1064
|
+
.widestAvailableFNumber, input.capabilities.aperture
|
|
1065
|
+
.narrowestAvailableFNumber, input.capabilities.aperture
|
|
1066
|
+
.settingGrid, "manualAperture");
|
|
1067
|
+
validateSettingAgainstGrid(manualIso, input.capabilities.iso.minimum, input.capabilities.iso.maximum, input.capabilities.iso.settingGrid, "manualIso");
|
|
1068
|
+
if (input.shutterQuantizationPolicy !==
|
|
1069
|
+
"nearest-log2-shorter-on-tie") {
|
|
1070
|
+
throw new InvalidScientificInputError('shutterQuantizationPolicy must be "nearest-log2-shorter-on-tie".');
|
|
1071
|
+
}
|
|
1072
|
+
const base = {
|
|
1073
|
+
resolverVersion: EXPOSURE_MODE_RESOLVER_VERSION,
|
|
1074
|
+
mode: "aperture-priority",
|
|
1075
|
+
axisOwnership: {
|
|
1076
|
+
aperture: "manual",
|
|
1077
|
+
shutter: "automatic",
|
|
1078
|
+
iso: "manual"
|
|
1079
|
+
},
|
|
1080
|
+
targetId: target.targetId,
|
|
1081
|
+
targetSourceMeterSnapshot: target.sourceMeterSnapshot,
|
|
1082
|
+
capabilityProfiles: {
|
|
1083
|
+
bodyProfileId: input.capabilities
|
|
1084
|
+
.bodyProfile.profileId,
|
|
1085
|
+
bodyProfileVersion: input.capabilities
|
|
1086
|
+
.bodyProfile.profileVersion,
|
|
1087
|
+
lensProfileId: input.capabilities
|
|
1088
|
+
.lensProfile.profileId,
|
|
1089
|
+
lensProfileVersion: input.capabilities
|
|
1090
|
+
.lensProfile.profileVersion
|
|
1091
|
+
},
|
|
1092
|
+
selectedFocalLengthMm: input.capabilities
|
|
1093
|
+
.selectedFocalLengthMm,
|
|
1094
|
+
referenceExposure,
|
|
1095
|
+
manualAperture,
|
|
1096
|
+
manualIso,
|
|
1097
|
+
apertureMutatedByResolver: false,
|
|
1098
|
+
isoMutatedByResolver: false,
|
|
1099
|
+
shutterResolvedByResolver: true,
|
|
1100
|
+
exposureCompensationAppliedByResolver: false,
|
|
1101
|
+
meterRecomputedByResolver: false,
|
|
1102
|
+
isoNoiseOrGainTopologyInferred: false,
|
|
1103
|
+
flashPolicyApplied: false,
|
|
1104
|
+
safetyShiftApplied: false
|
|
1105
|
+
};
|
|
1106
|
+
if (target.status === "no-signal") {
|
|
1107
|
+
return {
|
|
1108
|
+
...base,
|
|
1109
|
+
status: "blocked",
|
|
1110
|
+
resolvedSettings: {
|
|
1111
|
+
aperture: manualAperture,
|
|
1112
|
+
iso: manualIso
|
|
1113
|
+
},
|
|
1114
|
+
idealShutterSecondsBeforeConstraints: null,
|
|
1115
|
+
blocker: "target-no-signal",
|
|
1116
|
+
targetResidual: {
|
|
1117
|
+
status: "target-unresolved",
|
|
1118
|
+
state: "target-unresolved",
|
|
1119
|
+
reason: "no-signal-target"
|
|
1120
|
+
}
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
1123
|
+
const fixedAxisFactorAtReferenceShutter = opticalExposureFactor(manualAperture, referenceExposure
|
|
1124
|
+
.shutterSeconds, referenceExposure) *
|
|
1125
|
+
(manualIso /
|
|
1126
|
+
referenceExposure.iso);
|
|
1127
|
+
if (!Number.isFinite(fixedAxisFactorAtReferenceShutter) ||
|
|
1128
|
+
fixedAxisFactorAtReferenceShutter <= 0) {
|
|
1129
|
+
throw new InvalidScientificInputError("Aperture Priority fixed-axis exposure factor must remain finite and greater than zero.");
|
|
1130
|
+
}
|
|
1131
|
+
const idealShutterSeconds = referenceExposure
|
|
1132
|
+
.shutterSeconds *
|
|
1133
|
+
(target
|
|
1134
|
+
.requiredExposureScaleToTarget /
|
|
1135
|
+
fixedAxisFactorAtReferenceShutter);
|
|
1136
|
+
if (!Number.isFinite(idealShutterSeconds) ||
|
|
1137
|
+
idealShutterSeconds <= 0) {
|
|
1138
|
+
throw new InvalidScientificInputError("Ideal automatic shutter duration must remain finite and greater than zero.");
|
|
1139
|
+
}
|
|
1140
|
+
const automatic = resolveAutomaticShutter(idealShutterSeconds, input.capabilities);
|
|
1141
|
+
const achievedScale = opticalExposureFactor(manualAperture, automatic.shutterSeconds, referenceExposure) *
|
|
1142
|
+
(manualIso /
|
|
1143
|
+
referenceExposure.iso);
|
|
1144
|
+
const residual = targetResidual(target, achievedScale, automatic
|
|
1145
|
+
.limitingConstraint);
|
|
1146
|
+
return {
|
|
1147
|
+
...base,
|
|
1148
|
+
status: "resolved",
|
|
1149
|
+
resolvedSettings: {
|
|
1150
|
+
aperture: manualAperture,
|
|
1151
|
+
shutterSeconds: automatic.shutterSeconds,
|
|
1152
|
+
iso: manualIso
|
|
1153
|
+
},
|
|
1154
|
+
idealShutterSecondsBeforeConstraints: idealShutterSeconds,
|
|
1155
|
+
shutterResolution: {
|
|
1156
|
+
kind: automatic.kind,
|
|
1157
|
+
quantizationPolicy: input
|
|
1158
|
+
.shutterQuantizationPolicy,
|
|
1159
|
+
quantized: automatic.quantized,
|
|
1160
|
+
clamped: automatic.clamped
|
|
1161
|
+
},
|
|
1162
|
+
targetResidual: residual
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
function validateAutoIsoAvailability(capabilities) {
|
|
1166
|
+
return capabilities.iso.autoIsoAvailability;
|
|
1167
|
+
}
|
|
1168
|
+
function buildPriorityAutoIsoBlocked(mode, target, referenceExposure, reason, resolvedSettings) {
|
|
1169
|
+
return {
|
|
1170
|
+
resolverVersion: EXPOSURE_MODE_RESOLVER_VERSION,
|
|
1171
|
+
mode,
|
|
1172
|
+
status: "blocked",
|
|
1173
|
+
targetId: target.targetId,
|
|
1174
|
+
targetSourceMeterSnapshot: target.sourceMeterSnapshot,
|
|
1175
|
+
referenceExposure,
|
|
1176
|
+
resolvedSettings,
|
|
1177
|
+
blocker: reason,
|
|
1178
|
+
targetResidual: {
|
|
1179
|
+
status: "target-unresolved",
|
|
1180
|
+
state: "target-unresolved",
|
|
1181
|
+
reason: reason === "target-no-signal"
|
|
1182
|
+
? "no-signal-target"
|
|
1183
|
+
: "auto-iso-unavailable"
|
|
1184
|
+
}
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
/**
|
|
1188
|
+
* Hold aperture fixed, follow the declared minimum-shutter policy and use bounded Auto ISO to absorb
|
|
1189
|
+
* remaining target residual.
|
|
1190
|
+
*
|
|
1191
|
+
* Exposure modes apply control policy to one shared relative exposure equation and an explicit
|
|
1192
|
+
* reference anchor. Compensation is already included in the immutable meter target. Manual axes remain
|
|
1193
|
+
* unchanged; automatic axes use declared setting grids and expose signed residual stops and limit
|
|
1194
|
+
* diagnostics. A zero-signal target cannot authorize an invented exposure.
|
|
1195
|
+
* @param input - ResolveAperturePriorityAutoIsoExposureModeInput. See the linked contract for coordinate, unit and profile binding semantics.
|
|
1196
|
+
* @returns AperturePriorityAutoIsoExposureModeResolution. Return shape and scientific status are explicit; no calibration is inferred from successful execution.
|
|
1197
|
+
*
|
|
1198
|
+
* @see docs/API_REFERENCE.md for the root export and exact type graph.
|
|
1199
|
+
*/
|
|
1200
|
+
export function resolveAperturePriorityAutoIsoExposureMode(input) {
|
|
1201
|
+
const target = validateTarget(input.target);
|
|
1202
|
+
validateCapabilities(input.capabilities);
|
|
1203
|
+
const referenceExposure = validateReferenceExposure(input.referenceExposure, input.capabilities);
|
|
1204
|
+
const manualAperture = requirePositiveFinite(input.manualAperture, "manualAperture");
|
|
1205
|
+
validateSettingAgainstGrid(manualAperture, input.capabilities.aperture
|
|
1206
|
+
.widestAvailableFNumber, input.capabilities.aperture
|
|
1207
|
+
.narrowestAvailableFNumber, input.capabilities.aperture
|
|
1208
|
+
.settingGrid, "manualAperture");
|
|
1209
|
+
const policy = input.policy;
|
|
1210
|
+
if (policy.kind !==
|
|
1211
|
+
"minimum-iso-until-slowest-preferred-shutter" ||
|
|
1212
|
+
policy.isoBaseline !==
|
|
1213
|
+
"minimum-selectable" ||
|
|
1214
|
+
policy.isoQuantizationPolicy !==
|
|
1215
|
+
"nearest-log2-lower-on-tie" ||
|
|
1216
|
+
policy.shutterSelectionPolicy !==
|
|
1217
|
+
"not-longer-than-target" ||
|
|
1218
|
+
(policy.afterMaximumIso !==
|
|
1219
|
+
"allow-slower-shutter" &&
|
|
1220
|
+
policy.afterMaximumIso !==
|
|
1221
|
+
"hold-preferred-shutter")) {
|
|
1222
|
+
throw new InvalidScientificInputError("Aperture Priority Auto ISO policy is invalid.");
|
|
1223
|
+
}
|
|
1224
|
+
const slowestPreferred = requirePositiveFinite(policy.slowestPreferredShutterSeconds, "policy.slowestPreferredShutterSeconds");
|
|
1225
|
+
if (slowestPreferred <
|
|
1226
|
+
input.capabilities.shutter
|
|
1227
|
+
.minimumSeconds ||
|
|
1228
|
+
slowestPreferred >
|
|
1229
|
+
input.capabilities.shutter
|
|
1230
|
+
.maximumSeconds) {
|
|
1231
|
+
throw new InvalidScientificInputError("policy.slowestPreferredShutterSeconds lies outside the resolved shutter capability range.");
|
|
1232
|
+
}
|
|
1233
|
+
const availability = validateAutoIsoAvailability(input.capabilities);
|
|
1234
|
+
const commonBlocked = (blocker) => {
|
|
1235
|
+
const raw = buildPriorityAutoIsoBlocked("aperture-priority", target, referenceExposure, blocker, { aperture: manualAperture });
|
|
1236
|
+
return {
|
|
1237
|
+
...raw,
|
|
1238
|
+
axisOwnership: {
|
|
1239
|
+
aperture: "manual",
|
|
1240
|
+
shutter: "automatic",
|
|
1241
|
+
iso: "automatic"
|
|
1242
|
+
},
|
|
1243
|
+
manualAperture,
|
|
1244
|
+
policy,
|
|
1245
|
+
apertureMutatedByResolver: false,
|
|
1246
|
+
exposureCompensationAppliedByResolver: false,
|
|
1247
|
+
meterRecomputedByResolver: false,
|
|
1248
|
+
flashPolicyApplied: false,
|
|
1249
|
+
safetyShiftApplied: false
|
|
1250
|
+
};
|
|
1251
|
+
};
|
|
1252
|
+
if (availability !== "supported") {
|
|
1253
|
+
return commonBlocked(availability === "unknown"
|
|
1254
|
+
? "auto-iso-unknown"
|
|
1255
|
+
: "auto-iso-unsupported");
|
|
1256
|
+
}
|
|
1257
|
+
if (target.status === "no-signal") {
|
|
1258
|
+
return commonBlocked("target-no-signal");
|
|
1259
|
+
}
|
|
1260
|
+
const baselineIso = minimumSelectableIso(input.capabilities);
|
|
1261
|
+
const factorAtReferenceShutter = opticalExposureFactor(manualAperture, referenceExposure
|
|
1262
|
+
.shutterSeconds, referenceExposure) *
|
|
1263
|
+
(baselineIso /
|
|
1264
|
+
referenceExposure.iso);
|
|
1265
|
+
const idealAtBaseline = referenceExposure
|
|
1266
|
+
.shutterSeconds *
|
|
1267
|
+
(target
|
|
1268
|
+
.requiredExposureScaleToTarget /
|
|
1269
|
+
factorAtReferenceShutter);
|
|
1270
|
+
if (!Number.isFinite(idealAtBaseline) ||
|
|
1271
|
+
idealAtBaseline <= 0) {
|
|
1272
|
+
throw new InvalidScientificInputError("Ideal Aperture Priority shutter duration at baseline ISO must remain finite and greater than zero.");
|
|
1273
|
+
}
|
|
1274
|
+
const desiredPreferred = Math.min(idealAtBaseline, slowestPreferred);
|
|
1275
|
+
let shutter = selectShutterNotLongerThanTarget(desiredPreferred, input.capabilities);
|
|
1276
|
+
const isoPass = resolveManualExposureMode({
|
|
1277
|
+
target,
|
|
1278
|
+
capabilities: input.capabilities,
|
|
1279
|
+
referenceExposure,
|
|
1280
|
+
manualAperture,
|
|
1281
|
+
manualShutterSeconds: shutter.shutterSeconds,
|
|
1282
|
+
isoControl: {
|
|
1283
|
+
kind: "automatic",
|
|
1284
|
+
quantizationPolicy: policy.isoQuantizationPolicy
|
|
1285
|
+
}
|
|
1286
|
+
});
|
|
1287
|
+
if (isoPass.status !== "resolved" ||
|
|
1288
|
+
isoPass.isoControl !== "automatic") {
|
|
1289
|
+
throw new InvalidScientificInputError("Aperture Priority Auto ISO could not resolve its ISO pass.");
|
|
1290
|
+
}
|
|
1291
|
+
let fallbackUsed = false;
|
|
1292
|
+
let shutterSelectionPolicy = "not-longer-than-target";
|
|
1293
|
+
if (isoPass.isoResolution.clamped ===
|
|
1294
|
+
"maximum" &&
|
|
1295
|
+
isoPass.targetResidual.state ===
|
|
1296
|
+
"under-target" &&
|
|
1297
|
+
policy.afterMaximumIso ===
|
|
1298
|
+
"allow-slower-shutter") {
|
|
1299
|
+
const maxIso = isoPass.resolvedSettings.iso;
|
|
1300
|
+
const fixedAtReference = opticalExposureFactor(manualAperture, referenceExposure
|
|
1301
|
+
.shutterSeconds, referenceExposure) *
|
|
1302
|
+
(maxIso /
|
|
1303
|
+
referenceExposure.iso);
|
|
1304
|
+
const fallbackIdeal = referenceExposure
|
|
1305
|
+
.shutterSeconds *
|
|
1306
|
+
(target
|
|
1307
|
+
.requiredExposureScaleToTarget /
|
|
1308
|
+
fixedAtReference);
|
|
1309
|
+
shutter =
|
|
1310
|
+
resolveAutomaticShutter(fallbackIdeal, input.capabilities);
|
|
1311
|
+
fallbackUsed = true;
|
|
1312
|
+
shutterSelectionPolicy =
|
|
1313
|
+
"nearest-log2-shorter-on-tie";
|
|
1314
|
+
const achievedScale = opticalExposureFactor(manualAperture, shutter.shutterSeconds, referenceExposure) *
|
|
1315
|
+
(maxIso /
|
|
1316
|
+
referenceExposure.iso);
|
|
1317
|
+
const residual = targetResidual(target, achievedScale, shutter.limitingConstraint);
|
|
1318
|
+
return {
|
|
1319
|
+
resolverVersion: EXPOSURE_MODE_RESOLVER_VERSION,
|
|
1320
|
+
mode: "aperture-priority",
|
|
1321
|
+
status: "resolved",
|
|
1322
|
+
axisOwnership: {
|
|
1323
|
+
aperture: "manual",
|
|
1324
|
+
shutter: "automatic",
|
|
1325
|
+
iso: "automatic"
|
|
1326
|
+
},
|
|
1327
|
+
targetId: target.targetId,
|
|
1328
|
+
targetSourceMeterSnapshot: target.sourceMeterSnapshot,
|
|
1329
|
+
referenceExposure,
|
|
1330
|
+
manualAperture,
|
|
1331
|
+
policy,
|
|
1332
|
+
baselineIso,
|
|
1333
|
+
idealShutterAtBaselineIsoSeconds: idealAtBaseline,
|
|
1334
|
+
preferredShutterSeconds: desiredPreferred,
|
|
1335
|
+
afterMaximumIsoFallbackUsed: fallbackUsed,
|
|
1336
|
+
resolvedSettings: {
|
|
1337
|
+
aperture: manualAperture,
|
|
1338
|
+
shutterSeconds: shutter.shutterSeconds,
|
|
1339
|
+
iso: maxIso
|
|
1340
|
+
},
|
|
1341
|
+
shutterResolution: {
|
|
1342
|
+
kind: shutter.kind,
|
|
1343
|
+
quantized: shutter.quantized,
|
|
1344
|
+
clamped: shutter.clamped,
|
|
1345
|
+
selectionPolicy: shutterSelectionPolicy
|
|
1346
|
+
},
|
|
1347
|
+
idealIsoBeforeConstraints: isoPass
|
|
1348
|
+
.idealIsoBeforeConstraints,
|
|
1349
|
+
isoResolution: isoPass.isoResolution,
|
|
1350
|
+
targetResidual: residual,
|
|
1351
|
+
apertureMutatedByResolver: false,
|
|
1352
|
+
shutterResolvedByResolver: true,
|
|
1353
|
+
isoResolvedByResolver: true,
|
|
1354
|
+
exposureCompensationAppliedByResolver: false,
|
|
1355
|
+
meterRecomputedByResolver: false,
|
|
1356
|
+
flashPolicyApplied: false,
|
|
1357
|
+
safetyShiftApplied: false
|
|
1358
|
+
};
|
|
1359
|
+
}
|
|
1360
|
+
return {
|
|
1361
|
+
resolverVersion: EXPOSURE_MODE_RESOLVER_VERSION,
|
|
1362
|
+
mode: "aperture-priority",
|
|
1363
|
+
status: "resolved",
|
|
1364
|
+
axisOwnership: {
|
|
1365
|
+
aperture: "manual",
|
|
1366
|
+
shutter: "automatic",
|
|
1367
|
+
iso: "automatic"
|
|
1368
|
+
},
|
|
1369
|
+
targetId: target.targetId,
|
|
1370
|
+
targetSourceMeterSnapshot: target.sourceMeterSnapshot,
|
|
1371
|
+
referenceExposure,
|
|
1372
|
+
manualAperture,
|
|
1373
|
+
policy,
|
|
1374
|
+
baselineIso,
|
|
1375
|
+
idealShutterAtBaselineIsoSeconds: idealAtBaseline,
|
|
1376
|
+
preferredShutterSeconds: desiredPreferred,
|
|
1377
|
+
afterMaximumIsoFallbackUsed: fallbackUsed,
|
|
1378
|
+
resolvedSettings: isoPass.resolvedSettings,
|
|
1379
|
+
shutterResolution: {
|
|
1380
|
+
kind: shutter.kind,
|
|
1381
|
+
quantized: shutter.quantized,
|
|
1382
|
+
clamped: shutter.clamped,
|
|
1383
|
+
selectionPolicy: shutterSelectionPolicy
|
|
1384
|
+
},
|
|
1385
|
+
idealIsoBeforeConstraints: isoPass
|
|
1386
|
+
.idealIsoBeforeConstraints,
|
|
1387
|
+
isoResolution: isoPass.isoResolution,
|
|
1388
|
+
targetResidual: isoPass.targetResidual,
|
|
1389
|
+
apertureMutatedByResolver: false,
|
|
1390
|
+
shutterResolvedByResolver: true,
|
|
1391
|
+
isoResolvedByResolver: true,
|
|
1392
|
+
exposureCompensationAppliedByResolver: false,
|
|
1393
|
+
meterRecomputedByResolver: false,
|
|
1394
|
+
flashPolicyApplied: false,
|
|
1395
|
+
safetyShiftApplied: false
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1398
|
+
/**
|
|
1399
|
+
* Hold shutter fixed and resolve aperture plus optional Auto ISO against the exact capability
|
|
1400
|
+
* envelope.
|
|
1401
|
+
*
|
|
1402
|
+
* Exposure modes apply control policy to one shared relative exposure equation and an explicit
|
|
1403
|
+
* reference anchor. Compensation is already included in the immutable meter target. Manual axes remain
|
|
1404
|
+
* unchanged; automatic axes use declared setting grids and expose signed residual stops and limit
|
|
1405
|
+
* diagnostics. A zero-signal target cannot authorize an invented exposure.
|
|
1406
|
+
* @param input - ResolveShutterPriorityExposureModeInput. See the linked contract for coordinate, unit and profile binding semantics.
|
|
1407
|
+
* @returns ShutterPriorityExposureModeResolution. Return shape and scientific status are explicit; no calibration is inferred from successful execution.
|
|
1408
|
+
*
|
|
1409
|
+
* @see docs/API_REFERENCE.md for the root export and exact type graph.
|
|
1410
|
+
*/
|
|
1411
|
+
export function resolveShutterPriorityExposureMode(input) {
|
|
1412
|
+
const target = validateTarget(input.target);
|
|
1413
|
+
validateCapabilities(input.capabilities);
|
|
1414
|
+
const referenceExposure = validateReferenceExposure(input.referenceExposure, input.capabilities);
|
|
1415
|
+
const manualShutterSeconds = requirePositiveFinite(input.manualShutterSeconds, "manualShutterSeconds");
|
|
1416
|
+
validateSettingAgainstGrid(manualShutterSeconds, input.capabilities.shutter
|
|
1417
|
+
.minimumSeconds, input.capabilities.shutter
|
|
1418
|
+
.maximumSeconds, input.capabilities.shutter
|
|
1419
|
+
.settingGrid, "manualShutterSeconds");
|
|
1420
|
+
const base = {
|
|
1421
|
+
resolverVersion: EXPOSURE_MODE_RESOLVER_VERSION,
|
|
1422
|
+
mode: "shutter-priority",
|
|
1423
|
+
targetId: target.targetId,
|
|
1424
|
+
targetSourceMeterSnapshot: target.sourceMeterSnapshot,
|
|
1425
|
+
referenceExposure,
|
|
1426
|
+
manualShutterSeconds,
|
|
1427
|
+
shutterMutatedByResolver: false,
|
|
1428
|
+
apertureResolvedByResolver: true,
|
|
1429
|
+
exposureCompensationAppliedByResolver: false,
|
|
1430
|
+
meterRecomputedByResolver: false,
|
|
1431
|
+
flashPolicyApplied: false,
|
|
1432
|
+
safetyShiftApplied: false
|
|
1433
|
+
};
|
|
1434
|
+
if (input.isoControl.kind === "manual") {
|
|
1435
|
+
const manualIso = requirePositiveFinite(input.isoControl.iso, "isoControl.iso");
|
|
1436
|
+
validateSettingAgainstGrid(manualIso, input.capabilities.iso.minimum, input.capabilities.iso.maximum, input.capabilities.iso.settingGrid, "isoControl.iso");
|
|
1437
|
+
if (input.isoControl
|
|
1438
|
+
.apertureQuantizationPolicy !==
|
|
1439
|
+
"nearest-log2-narrower-on-tie") {
|
|
1440
|
+
throw new InvalidScientificInputError('isoControl.apertureQuantizationPolicy must be "nearest-log2-narrower-on-tie".');
|
|
1441
|
+
}
|
|
1442
|
+
if (target.status === "no-signal") {
|
|
1443
|
+
return {
|
|
1444
|
+
...base,
|
|
1445
|
+
status: "blocked",
|
|
1446
|
+
axisOwnership: {
|
|
1447
|
+
aperture: "automatic",
|
|
1448
|
+
shutter: "manual",
|
|
1449
|
+
iso: "manual"
|
|
1450
|
+
},
|
|
1451
|
+
isoControl: "manual",
|
|
1452
|
+
resolvedSettings: {
|
|
1453
|
+
shutterSeconds: manualShutterSeconds
|
|
1454
|
+
},
|
|
1455
|
+
blocker: "target-no-signal",
|
|
1456
|
+
targetResidual: {
|
|
1457
|
+
status: "target-unresolved",
|
|
1458
|
+
state: "target-unresolved",
|
|
1459
|
+
reason: "no-signal-target"
|
|
1460
|
+
}
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
const idealAperture = idealApertureForTarget(target
|
|
1464
|
+
.requiredExposureScaleToTarget, manualShutterSeconds, manualIso, referenceExposure);
|
|
1465
|
+
const aperture = resolveAutomaticAperture(idealAperture, input.capabilities);
|
|
1466
|
+
const achievedScale = opticalExposureFactor(aperture.aperture, manualShutterSeconds, referenceExposure) *
|
|
1467
|
+
(manualIso /
|
|
1468
|
+
referenceExposure.iso);
|
|
1469
|
+
return {
|
|
1470
|
+
...base,
|
|
1471
|
+
status: "resolved",
|
|
1472
|
+
axisOwnership: {
|
|
1473
|
+
aperture: "automatic",
|
|
1474
|
+
shutter: "manual",
|
|
1475
|
+
iso: "manual"
|
|
1476
|
+
},
|
|
1477
|
+
isoControl: "manual",
|
|
1478
|
+
manualIso,
|
|
1479
|
+
idealApertureBeforeConstraints: idealAperture,
|
|
1480
|
+
resolvedSettings: {
|
|
1481
|
+
aperture: aperture.aperture,
|
|
1482
|
+
shutterSeconds: manualShutterSeconds,
|
|
1483
|
+
iso: manualIso
|
|
1484
|
+
},
|
|
1485
|
+
apertureResolution: {
|
|
1486
|
+
kind: aperture.kind,
|
|
1487
|
+
quantizationPolicy: input.isoControl
|
|
1488
|
+
.apertureQuantizationPolicy,
|
|
1489
|
+
quantized: aperture.quantized,
|
|
1490
|
+
clamped: aperture.clamped
|
|
1491
|
+
},
|
|
1492
|
+
targetResidual: targetResidual(target, achievedScale, aperture
|
|
1493
|
+
.limitingConstraint)
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
if (input.isoControl.kind !==
|
|
1497
|
+
"automatic") {
|
|
1498
|
+
throw new InvalidScientificInputError("isoControl.kind is invalid for Shutter Priority.");
|
|
1499
|
+
}
|
|
1500
|
+
const policy = input.isoControl.policy;
|
|
1501
|
+
if (policy.kind !==
|
|
1502
|
+
"minimum-iso-aperture-first" ||
|
|
1503
|
+
policy.isoBaseline !==
|
|
1504
|
+
"minimum-selectable" ||
|
|
1505
|
+
policy.isoQuantizationPolicy !==
|
|
1506
|
+
"nearest-log2-lower-on-tie" ||
|
|
1507
|
+
policy.apertureSelectionPolicy !==
|
|
1508
|
+
"not-wider-than-target") {
|
|
1509
|
+
throw new InvalidScientificInputError("Shutter Priority Auto ISO policy is invalid.");
|
|
1510
|
+
}
|
|
1511
|
+
const availability = validateAutoIsoAvailability(input.capabilities);
|
|
1512
|
+
if (availability !== "supported") {
|
|
1513
|
+
const raw = buildPriorityAutoIsoBlocked("shutter-priority", target, referenceExposure, availability === "unknown"
|
|
1514
|
+
? "auto-iso-unknown"
|
|
1515
|
+
: "auto-iso-unsupported", {
|
|
1516
|
+
shutterSeconds: manualShutterSeconds
|
|
1517
|
+
});
|
|
1518
|
+
return {
|
|
1519
|
+
...base,
|
|
1520
|
+
...raw,
|
|
1521
|
+
axisOwnership: {
|
|
1522
|
+
aperture: "automatic",
|
|
1523
|
+
shutter: "manual",
|
|
1524
|
+
iso: "automatic"
|
|
1525
|
+
},
|
|
1526
|
+
isoControl: "automatic"
|
|
1527
|
+
};
|
|
1528
|
+
}
|
|
1529
|
+
if (target.status === "no-signal") {
|
|
1530
|
+
return {
|
|
1531
|
+
...base,
|
|
1532
|
+
status: "blocked",
|
|
1533
|
+
axisOwnership: {
|
|
1534
|
+
aperture: "automatic",
|
|
1535
|
+
shutter: "manual",
|
|
1536
|
+
iso: "automatic"
|
|
1537
|
+
},
|
|
1538
|
+
isoControl: "automatic",
|
|
1539
|
+
resolvedSettings: {
|
|
1540
|
+
shutterSeconds: manualShutterSeconds
|
|
1541
|
+
},
|
|
1542
|
+
blocker: "target-no-signal",
|
|
1543
|
+
targetResidual: {
|
|
1544
|
+
status: "target-unresolved",
|
|
1545
|
+
state: "target-unresolved",
|
|
1546
|
+
reason: "no-signal-target"
|
|
1547
|
+
}
|
|
1548
|
+
};
|
|
1549
|
+
}
|
|
1550
|
+
const baselineIso = minimumSelectableIso(input.capabilities);
|
|
1551
|
+
const idealAtBaseline = idealApertureForTarget(target
|
|
1552
|
+
.requiredExposureScaleToTarget, manualShutterSeconds, baselineIso, referenceExposure);
|
|
1553
|
+
const aperture = selectApertureNotWiderThanTarget(idealAtBaseline, input.capabilities);
|
|
1554
|
+
const isoPass = resolveManualExposureMode({
|
|
1555
|
+
target,
|
|
1556
|
+
capabilities: input.capabilities,
|
|
1557
|
+
referenceExposure,
|
|
1558
|
+
manualAperture: aperture.aperture,
|
|
1559
|
+
manualShutterSeconds,
|
|
1560
|
+
isoControl: {
|
|
1561
|
+
kind: "automatic",
|
|
1562
|
+
quantizationPolicy: policy.isoQuantizationPolicy
|
|
1563
|
+
}
|
|
1564
|
+
});
|
|
1565
|
+
if (isoPass.status !== "resolved" ||
|
|
1566
|
+
isoPass.isoControl !== "automatic") {
|
|
1567
|
+
throw new InvalidScientificInputError("Shutter Priority Auto ISO could not resolve its ISO pass.");
|
|
1568
|
+
}
|
|
1569
|
+
return {
|
|
1570
|
+
...base,
|
|
1571
|
+
status: "resolved",
|
|
1572
|
+
axisOwnership: {
|
|
1573
|
+
aperture: "automatic",
|
|
1574
|
+
shutter: "manual",
|
|
1575
|
+
iso: "automatic"
|
|
1576
|
+
},
|
|
1577
|
+
isoControl: "automatic",
|
|
1578
|
+
policy,
|
|
1579
|
+
baselineIso,
|
|
1580
|
+
idealApertureAtBaselineIso: idealAtBaseline,
|
|
1581
|
+
resolvedSettings: isoPass.resolvedSettings,
|
|
1582
|
+
apertureResolution: {
|
|
1583
|
+
kind: aperture.kind,
|
|
1584
|
+
quantized: aperture.quantized,
|
|
1585
|
+
clamped: aperture.clamped,
|
|
1586
|
+
selectionPolicy: policy.apertureSelectionPolicy
|
|
1587
|
+
},
|
|
1588
|
+
idealIsoBeforeConstraints: isoPass
|
|
1589
|
+
.idealIsoBeforeConstraints,
|
|
1590
|
+
isoResolution: isoPass.isoResolution,
|
|
1591
|
+
targetResidual: isoPass.targetResidual
|
|
1592
|
+
};
|
|
1593
|
+
}
|
|
1594
|
+
/**
|
|
1595
|
+
* Evaluate the declared generic program line in log2 exposure space, quantize aperture/shutter and
|
|
1596
|
+
* optionally use Auto ISO for residual exposure.
|
|
1597
|
+
*
|
|
1598
|
+
* Exposure modes apply control policy to one shared relative exposure equation and an explicit
|
|
1599
|
+
* reference anchor. Compensation is already included in the immutable meter target. Manual axes remain
|
|
1600
|
+
* unchanged; automatic axes use declared setting grids and expose signed residual stops and limit
|
|
1601
|
+
* diagnostics. A zero-signal target cannot authorize an invented exposure.
|
|
1602
|
+
* @param input - ResolveProgramAutoExposureModeInput. See the linked contract for coordinate, unit and profile binding semantics.
|
|
1603
|
+
* @returns ProgramAutoExposureModeResolution. Return shape and scientific status are explicit; no calibration is inferred from successful execution.
|
|
1604
|
+
*
|
|
1605
|
+
* @see docs/API_REFERENCE.md for the root export and exact type graph.
|
|
1606
|
+
*/
|
|
1607
|
+
export function resolveProgramAutoExposureMode(input) {
|
|
1608
|
+
const target = validateTarget(input.target);
|
|
1609
|
+
validateCapabilities(input.capabilities);
|
|
1610
|
+
const referenceExposure = validateReferenceExposure(input.referenceExposure, input.capabilities);
|
|
1611
|
+
const programLine = validateProgramLineForResolution(input.programLine, referenceExposure, input.capabilities);
|
|
1612
|
+
if (input.isoControl.kind !==
|
|
1613
|
+
"manual" &&
|
|
1614
|
+
input.isoControl.kind !==
|
|
1615
|
+
"automatic") {
|
|
1616
|
+
throw new InvalidScientificInputError("isoControl.kind is invalid for Program Auto.");
|
|
1617
|
+
}
|
|
1618
|
+
if (target.status === "no-signal") {
|
|
1619
|
+
return {
|
|
1620
|
+
resolverVersion: EXPOSURE_MODE_RESOLVER_VERSION,
|
|
1621
|
+
mode: "program-auto",
|
|
1622
|
+
status: "blocked",
|
|
1623
|
+
axisOwnership: {
|
|
1624
|
+
aperture: "automatic",
|
|
1625
|
+
shutter: "automatic",
|
|
1626
|
+
iso: input.isoControl.kind ===
|
|
1627
|
+
"manual"
|
|
1628
|
+
? "manual"
|
|
1629
|
+
: "automatic"
|
|
1630
|
+
},
|
|
1631
|
+
isoControl: input.isoControl.kind,
|
|
1632
|
+
targetId: target.targetId,
|
|
1633
|
+
targetSourceMeterSnapshot: target.sourceMeterSnapshot,
|
|
1634
|
+
referenceExposure,
|
|
1635
|
+
blocker: "target-no-signal",
|
|
1636
|
+
targetResidual: {
|
|
1637
|
+
status: "target-unresolved",
|
|
1638
|
+
state: "target-unresolved",
|
|
1639
|
+
reason: "no-signal-target"
|
|
1640
|
+
},
|
|
1641
|
+
exposureCompensationAppliedByResolver: false,
|
|
1642
|
+
meterRecomputedByResolver: false,
|
|
1643
|
+
flashPolicyApplied: false,
|
|
1644
|
+
safetyShiftApplied: false
|
|
1645
|
+
};
|
|
1646
|
+
}
|
|
1647
|
+
if (input.isoControl.kind ===
|
|
1648
|
+
"manual") {
|
|
1649
|
+
const manualIso = requirePositiveFinite(input.isoControl.iso, "isoControl.iso");
|
|
1650
|
+
validateSettingAgainstGrid(manualIso, input.capabilities.iso.minimum, input.capabilities.iso.maximum, input.capabilities.iso.settingGrid, "isoControl.iso");
|
|
1651
|
+
const requestedOpticalStops = target
|
|
1652
|
+
.exposureOffsetStopsToTarget -
|
|
1653
|
+
Math.log2(manualIso /
|
|
1654
|
+
referenceExposure.iso);
|
|
1655
|
+
const selection = selectProgramLineSettings(requestedOpticalStops, programLine, input.capabilities);
|
|
1656
|
+
const achievedScale = opticalExposureFactor(selection.resolvedAperture, selection
|
|
1657
|
+
.resolvedShutterSeconds, referenceExposure) *
|
|
1658
|
+
(manualIso /
|
|
1659
|
+
referenceExposure.iso);
|
|
1660
|
+
return {
|
|
1661
|
+
resolverVersion: EXPOSURE_MODE_RESOLVER_VERSION,
|
|
1662
|
+
mode: "program-auto",
|
|
1663
|
+
status: "resolved",
|
|
1664
|
+
axisOwnership: {
|
|
1665
|
+
aperture: "automatic",
|
|
1666
|
+
shutter: "automatic",
|
|
1667
|
+
iso: "manual"
|
|
1668
|
+
},
|
|
1669
|
+
isoControl: "manual",
|
|
1670
|
+
targetId: target.targetId,
|
|
1671
|
+
targetSourceMeterSnapshot: target.sourceMeterSnapshot,
|
|
1672
|
+
referenceExposure,
|
|
1673
|
+
programLineSelection: selection,
|
|
1674
|
+
apertureResolvedByResolver: true,
|
|
1675
|
+
shutterResolvedByResolver: true,
|
|
1676
|
+
exposureCompensationAppliedByResolver: false,
|
|
1677
|
+
meterRecomputedByResolver: false,
|
|
1678
|
+
flashPolicyApplied: false,
|
|
1679
|
+
safetyShiftApplied: false,
|
|
1680
|
+
manualIso,
|
|
1681
|
+
resolvedSettings: {
|
|
1682
|
+
aperture: selection
|
|
1683
|
+
.resolvedAperture,
|
|
1684
|
+
shutterSeconds: selection
|
|
1685
|
+
.resolvedShutterSeconds,
|
|
1686
|
+
iso: manualIso
|
|
1687
|
+
},
|
|
1688
|
+
targetResidual: targetResidual(target, achievedScale, programLineResidualConstraint(selection))
|
|
1689
|
+
};
|
|
1690
|
+
}
|
|
1691
|
+
if (input.isoControl
|
|
1692
|
+
.isoBaseline !==
|
|
1693
|
+
"minimum-selectable" ||
|
|
1694
|
+
input.isoControl
|
|
1695
|
+
.isoQuantizationPolicy !==
|
|
1696
|
+
"nearest-log2-lower-on-tie") {
|
|
1697
|
+
throw new InvalidScientificInputError("Program Auto ISO policy is invalid.");
|
|
1698
|
+
}
|
|
1699
|
+
const availability = validateAutoIsoAvailability(input.capabilities);
|
|
1700
|
+
if (availability !== "supported") {
|
|
1701
|
+
return {
|
|
1702
|
+
resolverVersion: EXPOSURE_MODE_RESOLVER_VERSION,
|
|
1703
|
+
mode: "program-auto",
|
|
1704
|
+
status: "blocked",
|
|
1705
|
+
axisOwnership: {
|
|
1706
|
+
aperture: "automatic",
|
|
1707
|
+
shutter: "automatic",
|
|
1708
|
+
iso: "automatic"
|
|
1709
|
+
},
|
|
1710
|
+
isoControl: "automatic",
|
|
1711
|
+
targetId: target.targetId,
|
|
1712
|
+
targetSourceMeterSnapshot: target.sourceMeterSnapshot,
|
|
1713
|
+
referenceExposure,
|
|
1714
|
+
blocker: availability === "unknown"
|
|
1715
|
+
? "auto-iso-unknown"
|
|
1716
|
+
: "auto-iso-unsupported",
|
|
1717
|
+
targetResidual: {
|
|
1718
|
+
status: "target-unresolved",
|
|
1719
|
+
state: "target-unresolved",
|
|
1720
|
+
reason: "auto-iso-unavailable"
|
|
1721
|
+
},
|
|
1722
|
+
exposureCompensationAppliedByResolver: false,
|
|
1723
|
+
meterRecomputedByResolver: false,
|
|
1724
|
+
flashPolicyApplied: false,
|
|
1725
|
+
safetyShiftApplied: false
|
|
1726
|
+
};
|
|
1727
|
+
}
|
|
1728
|
+
const baselineIso = minimumSelectableIso(input.capabilities);
|
|
1729
|
+
const requestedOpticalStops = target
|
|
1730
|
+
.exposureOffsetStopsToTarget -
|
|
1731
|
+
Math.log2(baselineIso /
|
|
1732
|
+
referenceExposure.iso);
|
|
1733
|
+
const selection = selectProgramLineSettings(requestedOpticalStops, programLine, input.capabilities);
|
|
1734
|
+
const isoPass = resolveManualExposureMode({
|
|
1735
|
+
target,
|
|
1736
|
+
capabilities: input.capabilities,
|
|
1737
|
+
referenceExposure,
|
|
1738
|
+
manualAperture: selection.resolvedAperture,
|
|
1739
|
+
manualShutterSeconds: selection
|
|
1740
|
+
.resolvedShutterSeconds,
|
|
1741
|
+
isoControl: {
|
|
1742
|
+
kind: "automatic",
|
|
1743
|
+
quantizationPolicy: input.isoControl
|
|
1744
|
+
.isoQuantizationPolicy
|
|
1745
|
+
}
|
|
1746
|
+
});
|
|
1747
|
+
if (isoPass.status !== "resolved" ||
|
|
1748
|
+
isoPass.isoControl !== "automatic") {
|
|
1749
|
+
throw new InvalidScientificInputError("Program Auto could not resolve its Auto ISO pass.");
|
|
1750
|
+
}
|
|
1751
|
+
return {
|
|
1752
|
+
resolverVersion: EXPOSURE_MODE_RESOLVER_VERSION,
|
|
1753
|
+
mode: "program-auto",
|
|
1754
|
+
status: "resolved",
|
|
1755
|
+
axisOwnership: {
|
|
1756
|
+
aperture: "automatic",
|
|
1757
|
+
shutter: "automatic",
|
|
1758
|
+
iso: "automatic"
|
|
1759
|
+
},
|
|
1760
|
+
isoControl: "automatic",
|
|
1761
|
+
targetId: target.targetId,
|
|
1762
|
+
targetSourceMeterSnapshot: target.sourceMeterSnapshot,
|
|
1763
|
+
referenceExposure,
|
|
1764
|
+
programLineSelection: selection,
|
|
1765
|
+
apertureResolvedByResolver: true,
|
|
1766
|
+
shutterResolvedByResolver: true,
|
|
1767
|
+
exposureCompensationAppliedByResolver: false,
|
|
1768
|
+
meterRecomputedByResolver: false,
|
|
1769
|
+
flashPolicyApplied: false,
|
|
1770
|
+
safetyShiftApplied: false,
|
|
1771
|
+
baselineIso,
|
|
1772
|
+
idealIsoBeforeConstraints: isoPass
|
|
1773
|
+
.idealIsoBeforeConstraints,
|
|
1774
|
+
isoResolution: isoPass.isoResolution,
|
|
1775
|
+
resolvedSettings: isoPass.resolvedSettings,
|
|
1776
|
+
targetResidual: isoPass.targetResidual
|
|
1777
|
+
};
|
|
1778
|
+
}
|
|
1779
|
+
/**
|
|
1780
|
+
* Resolve aperture, shutter and ISO under an explicit generic Full Auto exposure policy. Autofocus,
|
|
1781
|
+
* WB, flash, drive and scene recognition remain unresolved.
|
|
1782
|
+
*
|
|
1783
|
+
* Exposure modes apply control policy to one shared relative exposure equation and an explicit
|
|
1784
|
+
* reference anchor. Compensation is already included in the immutable meter target. Manual axes remain
|
|
1785
|
+
* unchanged; automatic axes use declared setting grids and expose signed residual stops and limit
|
|
1786
|
+
* diagnostics. A zero-signal target cannot authorize an invented exposure.
|
|
1787
|
+
* @param input - ResolveFullAutoExposureModeInput. See the linked contract for coordinate, unit and profile binding semantics.
|
|
1788
|
+
* @returns FullAutoExposureModeResolution. Return shape and scientific status are explicit; no calibration is inferred from successful execution.
|
|
1789
|
+
*
|
|
1790
|
+
* @see docs/API_REFERENCE.md for the root export and exact type graph.
|
|
1791
|
+
*/
|
|
1792
|
+
export function resolveFullAutoExposureMode(input) {
|
|
1793
|
+
const target = validateTarget(input.target);
|
|
1794
|
+
validateCapabilities(input.capabilities);
|
|
1795
|
+
const referenceExposure = validateReferenceExposure(input.referenceExposure, input.capabilities);
|
|
1796
|
+
if (input.policy.kind !==
|
|
1797
|
+
"generic-program-line-minimum-iso" ||
|
|
1798
|
+
input.policy.isoBaseline !==
|
|
1799
|
+
"minimum-selectable" ||
|
|
1800
|
+
input.policy
|
|
1801
|
+
.isoQuantizationPolicy !==
|
|
1802
|
+
"nearest-log2-lower-on-tie") {
|
|
1803
|
+
throw new InvalidScientificInputError("Full Auto exposure policy is invalid.");
|
|
1804
|
+
}
|
|
1805
|
+
const programLine = validateProgramLineForResolution(input.policy.programLine, referenceExposure, input.capabilities);
|
|
1806
|
+
const blocked = (blocker) => ({
|
|
1807
|
+
resolverVersion: EXPOSURE_MODE_RESOLVER_VERSION,
|
|
1808
|
+
mode: "full-auto-exposure",
|
|
1809
|
+
status: "blocked",
|
|
1810
|
+
axisOwnership: {
|
|
1811
|
+
aperture: "automatic",
|
|
1812
|
+
shutter: "automatic",
|
|
1813
|
+
iso: "automatic"
|
|
1814
|
+
},
|
|
1815
|
+
policy: input.policy,
|
|
1816
|
+
targetId: target.targetId,
|
|
1817
|
+
targetSourceMeterSnapshot: target.sourceMeterSnapshot,
|
|
1818
|
+
referenceExposure,
|
|
1819
|
+
blocker,
|
|
1820
|
+
targetResidual: {
|
|
1821
|
+
status: "target-unresolved",
|
|
1822
|
+
state: "target-unresolved",
|
|
1823
|
+
reason: blocker ===
|
|
1824
|
+
"target-no-signal"
|
|
1825
|
+
? "no-signal-target"
|
|
1826
|
+
: "auto-iso-unavailable"
|
|
1827
|
+
},
|
|
1828
|
+
exposureCompensationAppliedByResolver: false,
|
|
1829
|
+
meterRecomputedByResolver: false,
|
|
1830
|
+
flashPolicyApplied: false,
|
|
1831
|
+
safetyShiftApplied: false,
|
|
1832
|
+
autofocusResolved: false,
|
|
1833
|
+
whiteBalanceResolved: false,
|
|
1834
|
+
flashResolved: false,
|
|
1835
|
+
driveResolved: false,
|
|
1836
|
+
sceneRecognitionResolved: false,
|
|
1837
|
+
stabilizationPolicyResolved: false
|
|
1838
|
+
});
|
|
1839
|
+
if (target.status === "no-signal") {
|
|
1840
|
+
return blocked("target-no-signal");
|
|
1841
|
+
}
|
|
1842
|
+
const availability = validateAutoIsoAvailability(input.capabilities);
|
|
1843
|
+
if (availability !== "supported") {
|
|
1844
|
+
return blocked(availability === "unknown"
|
|
1845
|
+
? "auto-iso-unknown"
|
|
1846
|
+
: "auto-iso-unsupported");
|
|
1847
|
+
}
|
|
1848
|
+
const baselineIso = minimumSelectableIso(input.capabilities);
|
|
1849
|
+
const requestedOpticalStops = target
|
|
1850
|
+
.exposureOffsetStopsToTarget -
|
|
1851
|
+
Math.log2(baselineIso /
|
|
1852
|
+
referenceExposure.iso);
|
|
1853
|
+
const selection = selectProgramLineSettings(requestedOpticalStops, programLine, input.capabilities);
|
|
1854
|
+
const isoPass = resolveManualExposureMode({
|
|
1855
|
+
target,
|
|
1856
|
+
capabilities: input.capabilities,
|
|
1857
|
+
referenceExposure,
|
|
1858
|
+
manualAperture: selection.resolvedAperture,
|
|
1859
|
+
manualShutterSeconds: selection
|
|
1860
|
+
.resolvedShutterSeconds,
|
|
1861
|
+
isoControl: {
|
|
1862
|
+
kind: "automatic",
|
|
1863
|
+
quantizationPolicy: input.policy
|
|
1864
|
+
.isoQuantizationPolicy
|
|
1865
|
+
}
|
|
1866
|
+
});
|
|
1867
|
+
if (isoPass.status !== "resolved" ||
|
|
1868
|
+
isoPass.isoControl !== "automatic") {
|
|
1869
|
+
throw new InvalidScientificInputError("Full Auto exposure could not resolve its Auto ISO pass.");
|
|
1870
|
+
}
|
|
1871
|
+
return {
|
|
1872
|
+
resolverVersion: EXPOSURE_MODE_RESOLVER_VERSION,
|
|
1873
|
+
mode: "full-auto-exposure",
|
|
1874
|
+
status: "resolved",
|
|
1875
|
+
axisOwnership: {
|
|
1876
|
+
aperture: "automatic",
|
|
1877
|
+
shutter: "automatic",
|
|
1878
|
+
iso: "automatic"
|
|
1879
|
+
},
|
|
1880
|
+
policy: input.policy,
|
|
1881
|
+
targetId: target.targetId,
|
|
1882
|
+
targetSourceMeterSnapshot: target.sourceMeterSnapshot,
|
|
1883
|
+
referenceExposure,
|
|
1884
|
+
programLineSelection: selection,
|
|
1885
|
+
apertureResolvedByResolver: true,
|
|
1886
|
+
shutterResolvedByResolver: true,
|
|
1887
|
+
exposureCompensationAppliedByResolver: false,
|
|
1888
|
+
meterRecomputedByResolver: false,
|
|
1889
|
+
flashPolicyApplied: false,
|
|
1890
|
+
safetyShiftApplied: false,
|
|
1891
|
+
baselineIso,
|
|
1892
|
+
idealIsoBeforeConstraints: isoPass
|
|
1893
|
+
.idealIsoBeforeConstraints,
|
|
1894
|
+
isoResolution: isoPass.isoResolution,
|
|
1895
|
+
resolvedSettings: isoPass.resolvedSettings,
|
|
1896
|
+
targetResidual: isoPass.targetResidual,
|
|
1897
|
+
autofocusResolved: false,
|
|
1898
|
+
whiteBalanceResolved: false,
|
|
1899
|
+
flashResolved: false,
|
|
1900
|
+
driveResolved: false,
|
|
1901
|
+
sceneRecognitionResolved: false,
|
|
1902
|
+
stabilizationPolicyResolved: false
|
|
1903
|
+
};
|
|
1904
|
+
}
|
|
1905
|
+
//# sourceMappingURL=exposure-mode-resolver.js.map
|