@photivra/engine 2.17.0 → 2.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. package/CHANGELOG.md +116 -1
  2. package/CITATION.cff +1 -1
  3. package/README.md +6 -6
  4. package/SECURITY.md +1 -1
  5. package/dist/camera/sensor-equivalence.d.ts +156 -0
  6. package/dist/camera/sensor-equivalence.js +208 -0
  7. package/dist/camera/sensor-equivalence.js.map +1 -0
  8. package/dist/capture/focus-control.js +2 -75
  9. package/dist/capture/focus-control.js.map +1 -1
  10. package/dist/capture/native-environment-lean.d.ts +1 -0
  11. package/dist/capture/native-environment-lean.js +1 -1
  12. package/dist/capture/native-environment-lean.js.map +1 -1
  13. package/dist/capture/native-environment-optical-stage.d.ts +1 -0
  14. package/dist/capture/native-environment-optical-stage.js +2 -5
  15. package/dist/capture/native-environment-optical-stage.js.map +1 -1
  16. package/dist/capture/native-environment-raw.js +1 -5
  17. package/dist/capture/native-environment-raw.js.map +1 -1
  18. package/dist/capture/native-environment-tabulated.d.ts +5 -0
  19. package/dist/capture/native-environment-tabulated.js +1 -1
  20. package/dist/capture/native-environment-tabulated.js.map +1 -1
  21. package/dist/capture/release-sequence.js +2 -47
  22. package/dist/capture/release-sequence.js.map +1 -1
  23. package/dist/color/white-balance.d.ts +2 -0
  24. package/dist/color/white-balance.js +3 -56
  25. package/dist/color/white-balance.js.map +1 -1
  26. package/dist/composition/image-formation-plan.d.ts +1 -0
  27. package/dist/composition/image-formation-plan.js +3 -49
  28. package/dist/composition/image-formation-plan.js.map +1 -1
  29. package/dist/core/evidence-provenance.js +1 -16
  30. package/dist/core/evidence-provenance.js.map +1 -1
  31. package/dist/core/record-validation.d.ts +54 -0
  32. package/dist/core/record-validation.js +176 -0
  33. package/dist/core/record-validation.js.map +1 -1
  34. package/dist/core/scientific-assurance.js +1 -18
  35. package/dist/core/scientific-assurance.js.map +1 -1
  36. package/dist/core/shared-helpers.d.ts +33 -0
  37. package/dist/core/shared-helpers.js +34 -0
  38. package/dist/core/shared-helpers.js.map +1 -0
  39. package/dist/core/spectral-composition.js +1 -36
  40. package/dist/core/spectral-composition.js.map +1 -1
  41. package/dist/core/spectral.js +1 -19
  42. package/dist/core/spectral.js.map +1 -1
  43. package/dist/core/version.d.ts +1 -1
  44. package/dist/core/version.js +1 -1
  45. package/dist/core/version.js.map +1 -1
  46. package/dist/equipment/exposure-capabilities.d.ts +1 -0
  47. package/dist/equipment/exposure-capabilities.js +2 -36
  48. package/dist/equipment/exposure-capabilities.js.map +1 -1
  49. package/dist/equipment/ideal-lens.d.ts +25 -0
  50. package/dist/equipment/ideal-lens.js +21 -0
  51. package/dist/equipment/ideal-lens.js.map +1 -0
  52. package/dist/equipment/iso-capabilities.js +2 -49
  53. package/dist/equipment/iso-capabilities.js.map +1 -1
  54. package/dist/equipment/metering-capabilities.js +1 -26
  55. package/dist/equipment/metering-capabilities.js.map +1 -1
  56. package/dist/equipment/release-capabilities.js +1 -53
  57. package/dist/equipment/release-capabilities.js.map +1 -1
  58. package/dist/exposure/auto-iso-slowest-shutter.js +1 -7
  59. package/dist/exposure/auto-iso-slowest-shutter.js.map +1 -1
  60. package/dist/exposure/exposure-duration-control.js +1 -16
  61. package/dist/exposure/exposure-duration-control.js.map +1 -1
  62. package/dist/exposure/exposure-mode-resolver.js +1 -17
  63. package/dist/exposure/exposure-mode-resolver.js.map +1 -1
  64. package/dist/exposure/filtered-exposure-time.d.ts +93 -0
  65. package/dist/exposure/filtered-exposure-time.js +129 -0
  66. package/dist/exposure/filtered-exposure-time.js.map +1 -0
  67. package/dist/exposure/flash-ttl.d.ts +1 -0
  68. package/dist/exposure/flash-ttl.js +1 -1
  69. package/dist/exposure/flash-ttl.js.map +1 -1
  70. package/dist/exposure/flash.js +1 -49
  71. package/dist/exposure/flash.js.map +1 -1
  72. package/dist/exposure/metering-scene-radiance.js +1 -25
  73. package/dist/exposure/metering-scene-radiance.js.map +1 -1
  74. package/dist/exposure/metering-target.js +1 -30
  75. package/dist/exposure/metering-target.js.map +1 -1
  76. package/dist/exposure/metering-temporal.js +1 -8
  77. package/dist/exposure/metering-temporal.js.map +1 -1
  78. package/dist/exposure/metering.js +1 -72
  79. package/dist/exposure/metering.js.map +1 -1
  80. package/dist/exposure/program-line.js +1 -45
  81. package/dist/exposure/program-line.js.map +1 -1
  82. package/dist/facade/catalogue-lens-signal.d.ts +94 -0
  83. package/dist/facade/catalogue-lens-signal.js +362 -0
  84. package/dist/facade/catalogue-lens-signal.js.map +1 -0
  85. package/dist/facade/defocus-image-space-provider.js +5 -2
  86. package/dist/facade/defocus-image-space-provider.js.map +1 -1
  87. package/dist/facade/develop-joined-parts.d.ts +29 -0
  88. package/dist/facade/develop-joined-parts.js +33 -0
  89. package/dist/facade/develop-joined-parts.js.map +1 -0
  90. package/dist/facade/display-view-lens-signal.d.ts +32 -1
  91. package/dist/facade/display-view-lens-signal.js +85 -10
  92. package/dist/facade/display-view-lens-signal.js.map +1 -1
  93. package/dist/facade/expected-signal-setup.d.ts +8 -0
  94. package/dist/facade/expected-signal-setup.js +7 -0
  95. package/dist/facade/expected-signal-setup.js.map +1 -1
  96. package/dist/facade/expected-signal.d.ts +5 -0
  97. package/dist/facade/expected-signal.js +5 -1
  98. package/dist/facade/expected-signal.js.map +1 -1
  99. package/dist/facade/generic-lens-capture.d.ts +5 -2
  100. package/dist/facade/generic-lens-capture.js +5 -2
  101. package/dist/facade/generic-lens-capture.js.map +1 -1
  102. package/dist/facade/simulate-capture.d.ts +67 -8
  103. package/dist/facade/simulate-capture.js +262 -126
  104. package/dist/facade/simulate-capture.js.map +1 -1
  105. package/dist/facade/simulate-display-view.js +1 -1
  106. package/dist/facade/simulate-display-view.js.map +1 -1
  107. package/dist/gpu/chart-composite-gpu.js +1 -3
  108. package/dist/gpu/chart-composite-gpu.js.map +1 -1
  109. package/dist/gpu/chart-radiance-gpu.js +1 -5
  110. package/dist/gpu/chart-radiance-gpu.js.map +1 -1
  111. package/dist/gpu/display-view-quality-gpu.d.ts +13 -1
  112. package/dist/gpu/display-view-quality-gpu.js +4 -8
  113. package/dist/gpu/display-view-quality-gpu.js.map +1 -1
  114. package/dist/gpu/display-view-speed-gpu.js +3 -25
  115. package/dist/gpu/display-view-speed-gpu.js.map +1 -1
  116. package/dist/gpu/sensor-stage-gpu.js +1 -5
  117. package/dist/gpu/sensor-stage-gpu.js.map +1 -1
  118. package/dist/gpu/webgpu-runtime.d.ts +2 -0
  119. package/dist/gpu/webgpu-runtime.js +3 -0
  120. package/dist/gpu/webgpu-runtime.js.map +1 -1
  121. package/dist/index.d.ts +6 -1
  122. package/dist/index.js +5 -0
  123. package/dist/index.js.map +1 -1
  124. package/dist/motion/camera-rotation.js +1 -6
  125. package/dist/motion/camera-rotation.js.map +1 -1
  126. package/dist/motion/capture-rotation-inverse-mapping.d.ts +12 -0
  127. package/dist/motion/capture-rotation-inverse-mapping.js +2 -2
  128. package/dist/motion/capture-rotation-inverse-mapping.js.map +1 -1
  129. package/dist/motion/capture-rotation-temporal-quadrature.d.ts +1 -0
  130. package/dist/motion/capture-rotation-temporal-quadrature.js +1 -1
  131. package/dist/motion/capture-rotation-temporal-quadrature.js.map +1 -1
  132. package/dist/motion/capture-rotation-trajectory.js +2 -17
  133. package/dist/motion/capture-rotation-trajectory.js.map +1 -1
  134. package/dist/motion/capture-translation-parallax-temporal-quadrature.d.ts +1 -0
  135. package/dist/motion/capture-translation-parallax-temporal-quadrature.js +3 -14
  136. package/dist/motion/capture-translation-parallax-temporal-quadrature.js.map +1 -1
  137. package/dist/motion/extended-object-projection.js +5 -40
  138. package/dist/motion/extended-object-projection.js.map +1 -1
  139. package/dist/optics/atmospheric-haze.js +1 -7
  140. package/dist/optics/atmospheric-haze.js.map +1 -1
  141. package/dist/optics/brown-conrady-distortion.js +1 -7
  142. package/dist/optics/brown-conrady-distortion.js.map +1 -1
  143. package/dist/optics/complex-pupil-psf.d.ts +1 -0
  144. package/dist/optics/complex-pupil-psf.js +2 -84
  145. package/dist/optics/complex-pupil-psf.js.map +1 -1
  146. package/dist/optics/focus-state.js +1 -11
  147. package/dist/optics/focus-state.js.map +1 -1
  148. package/dist/optics/front-of-lens-filter.d.ts +5 -0
  149. package/dist/optics/front-of-lens-filter.js +3 -65
  150. package/dist/optics/front-of-lens-filter.js.map +1 -1
  151. package/dist/optics/generic-nd-filter-presets.d.ts +12 -0
  152. package/dist/optics/generic-nd-filter-presets.js +48 -0
  153. package/dist/optics/generic-nd-filter-presets.js.map +1 -0
  154. package/dist/optics/illumination-vignetting.js +1 -6
  155. package/dist/optics/illumination-vignetting.js.map +1 -1
  156. package/dist/optics/lens-psf-profile.d.ts +4 -1
  157. package/dist/optics/lens-psf-profile.js +12 -111
  158. package/dist/optics/lens-psf-profile.js.map +1 -1
  159. package/dist/optics/longitudinal-chromatic-aberration.js +1 -7
  160. package/dist/optics/longitudinal-chromatic-aberration.js.map +1 -1
  161. package/dist/optics/pupil-vignetting.js +1 -7
  162. package/dist/optics/pupil-vignetting.js.map +1 -1
  163. package/dist/optics/radial-distortion.js +1 -6
  164. package/dist/optics/radial-distortion.js.map +1 -1
  165. package/dist/optics/scene-to-sensor-irradiance.js +4 -96
  166. package/dist/optics/scene-to-sensor-irradiance.js.map +1 -1
  167. package/dist/optics/stray-light.d.ts +1 -0
  168. package/dist/optics/stray-light.js +1 -1
  169. package/dist/optics/stray-light.js.map +1 -1
  170. package/dist/optics/zernike-wavefront.js +1 -7
  171. package/dist/optics/zernike-wavefront.js.map +1 -1
  172. package/dist/output/capture-geometry.js +1 -8
  173. package/dist/output/capture-geometry.js.map +1 -1
  174. package/dist/output/developed-lens-corrections.d.ts +62 -0
  175. package/dist/output/developed-lens-corrections.js +135 -0
  176. package/dist/output/developed-lens-corrections.js.map +1 -0
  177. package/dist/output/generic-lens-corrections.d.ts +2 -6
  178. package/dist/output/generic-lens-corrections.js +3 -6
  179. package/dist/output/generic-lens-corrections.js.map +1 -1
  180. package/dist/output/geometric-transforms.js +1 -4
  181. package/dist/output/geometric-transforms.js.map +1 -1
  182. package/dist/output/native-capture-sdr.d.ts +2 -2
  183. package/dist/output/native-capture-sdr.js +3 -2
  184. package/dist/output/native-capture-sdr.js.map +1 -1
  185. package/dist/processing/development-detail.d.ts +29 -0
  186. package/dist/processing/development-detail.js +128 -107
  187. package/dist/processing/development-detail.js.map +1 -1
  188. package/dist/schema/illumination-temporal.js +3 -72
  189. package/dist/schema/illumination-temporal.js.map +1 -1
  190. package/dist/schema/illumination.d.ts +1 -0
  191. package/dist/schema/illumination.js +2 -65
  192. package/dist/schema/illumination.js.map +1 -1
  193. package/dist/schema/scene-radiance-temporal.js +2 -14
  194. package/dist/schema/scene-radiance-temporal.js.map +1 -1
  195. package/dist/schema/scene-radiance.js +2 -60
  196. package/dist/schema/scene-radiance.js.map +1 -1
  197. package/dist/schema/validation.d.ts +2 -0
  198. package/dist/schema/validation.js +2 -17
  199. package/dist/schema/validation.js.map +1 -1
  200. package/dist/sensor/accumulated-charge.js +2 -59
  201. package/dist/sensor/accumulated-charge.js.map +1 -1
  202. package/dist/sensor/architecture.js +1 -16
  203. package/dist/sensor/architecture.js.map +1 -1
  204. package/dist/sensor/camera-saturation-capacity.d.ts +1 -0
  205. package/dist/sensor/camera-saturation-capacity.js +3 -55
  206. package/dist/sensor/camera-saturation-capacity.js.map +1 -1
  207. package/dist/sensor/capture-color-sampling-binding.js +1 -26
  208. package/dist/sensor/capture-color-sampling-binding.js.map +1 -1
  209. package/dist/sensor/capture-mode-timing.js +1 -39
  210. package/dist/sensor/capture-mode-timing.js.map +1 -1
  211. package/dist/sensor/capture-mode.js +1 -23
  212. package/dist/sensor/capture-mode.js.map +1 -1
  213. package/dist/sensor/color-sampling.js +1 -26
  214. package/dist/sensor/color-sampling.js.map +1 -1
  215. package/dist/sensor/constant-rate-temporal-integration.js +2 -46
  216. package/dist/sensor/constant-rate-temporal-integration.js.map +1 -1
  217. package/dist/sensor/dark-current.js +2 -45
  218. package/dist/sensor/dark-current.js.map +1 -1
  219. package/dist/sensor/eqe-electron-rate.js +2 -35
  220. package/dist/sensor/eqe-electron-rate.js.map +1 -1
  221. package/dist/sensor/exposure-window.d.ts +5 -0
  222. package/dist/sensor/exposure-window.js +7 -22
  223. package/dist/sensor/exposure-window.js.map +1 -1
  224. package/dist/sensor/iso-gain.js +1 -7
  225. package/dist/sensor/iso-gain.js.map +1 -1
  226. package/dist/sensor/iso-signal-chain.js +1 -43
  227. package/dist/sensor/iso-signal-chain.js.map +1 -1
  228. package/dist/sensor/optical-stack.js +1 -25
  229. package/dist/sensor/optical-stack.js.map +1 -1
  230. package/dist/sensor/photon-energy.js +1 -26
  231. package/dist/sensor/photon-energy.js.map +1 -1
  232. package/dist/sensor/physical-charge-capacity.js +3 -77
  233. package/dist/sensor/physical-charge-capacity.js.map +1 -1
  234. package/dist/sensor/radiometry-readiness.js +2 -23
  235. package/dist/sensor/radiometry-readiness.js.map +1 -1
  236. package/dist/sensor/raw-readout.d.ts +3 -0
  237. package/dist/sensor/raw-readout.js +4 -66
  238. package/dist/sensor/raw-readout.js.map +1 -1
  239. package/dist/sensor/raw-reconstruction.js +2 -64
  240. package/dist/sensor/raw-reconstruction.js.map +1 -1
  241. package/dist/sensor/readout-exposure-linkage.js +1 -9
  242. package/dist/sensor/readout-exposure-linkage.js.map +1 -1
  243. package/dist/sensor/readout-timing.js +3 -57
  244. package/dist/sensor/readout-timing.js.map +1 -1
  245. package/dist/sensor/response-application-compatibility.d.ts +2 -0
  246. package/dist/sensor/response-application-compatibility.js +3 -29
  247. package/dist/sensor/response-application-compatibility.js.map +1 -1
  248. package/dist/sensor/response-operating-range.d.ts +1 -0
  249. package/dist/sensor/response-operating-range.js +3 -79
  250. package/dist/sensor/response-operating-range.js.map +1 -1
  251. package/dist/sensor/responsivity-photocurrent.js +4 -108
  252. package/dist/sensor/responsivity-photocurrent.js.map +1 -1
  253. package/dist/sensor/sampling-aperture.js +1 -25
  254. package/dist/sensor/sampling-aperture.js.map +1 -1
  255. package/dist/sensor/spatial-sample-reduction.js +2 -19
  256. package/dist/sensor/spatial-sample-reduction.js.map +1 -1
  257. package/dist/sensor/spatial-sampling-quadrature.js +1 -3
  258. package/dist/sensor/spatial-sampling-quadrature.js.map +1 -1
  259. package/dist/sensor/spatio-spectral-reduction.js +1 -18
  260. package/dist/sensor/spatio-spectral-reduction.js.map +1 -1
  261. package/dist/sensor/spectral-coverage.d.ts +10 -1
  262. package/dist/sensor/spectral-coverage.js +1 -1
  263. package/dist/sensor/spectral-coverage.js.map +1 -1
  264. package/dist/sensor/spectral-quadrature.js +2 -35
  265. package/dist/sensor/spectral-quadrature.js.map +1 -1
  266. package/dist/sensor/spectral-response.js +1 -47
  267. package/dist/sensor/spectral-response.js.map +1 -1
  268. package/dist/stabilization/camera-shake.js +1 -6
  269. package/dist/stabilization/camera-shake.js.map +1 -1
  270. package/dist/stabilization/system.js +2 -80
  271. package/dist/stabilization/system.js.map +1 -1
  272. package/docs/AGENT_GUIDE.md +4 -0
  273. package/docs/API_REFERENCE.md +69 -2
  274. package/docs/QUICK_START.md +136 -6
  275. package/docs/USAGE.md +89 -3
  276. package/docs/api/camera-sensor-equivalence.md +209 -0
  277. package/docs/api/core-version.md +1 -1
  278. package/docs/api/equipment-ideal-lens.md +26 -0
  279. package/docs/api/exports.json +118 -3
  280. package/docs/api/exposure-filtered-exposure-time.md +105 -0
  281. package/docs/api/facade-develop-joined-parts.md +32 -0
  282. package/docs/api/facade-simulate-capture.md +62 -8
  283. package/docs/api/optics-generic-nd-filter-presets.md +34 -0
  284. package/docs/api/optics-lens-psf-profile.md +3 -1
  285. package/docs/api/output-developed-lens-corrections.md +12 -0
  286. package/docs/api/output-generic-lens-corrections.md +0 -9
  287. package/llms.txt +3 -0
  288. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"metering-target.js","sourceRoot":"","sources":["../../src/exposure/metering-target.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Meter targets: a frozen metering result that camera-control logic uses as its exposure target.\n *\n * The target copies the meter snapshot by value. Later changes to the scene or light do not change\n * it. This is what the engine provides for AE lock (auto-exposure lock).\n *\n * Exposure compensation is set as an absolute value. It is always worked out again from the base\n * target without compensation, so repeated UI updates cannot build up floating-point or control\n * drift. Positive compensation asks for more exposure.\n * @see docs/USAGE.md for the equations, units, coordinate conventions, known blockers and support\n * limits.\n */\n\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport type {\n ExposureMeteringResult\n} from \"./metering.js\";\nimport type {\n SceneRadianceTemporalExposureMeteringResult\n} from \"./metering-temporal.js\";\nimport { requireInputObject, requireInputKeys } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\n\nexport const EXPOSURE_METER_TARGET_SCHEMA_VERSION =\n \"0.1.0\" as const;\n\nexport type ExposureMeterTargetSourceResult =\n | ExposureMeteringResult\n | SceneRadianceTemporalExposureMeteringResult;\n\nexport type ExposureMeterTargetSourceKind =\n | \"spatial-metering-result\"\n | \"temporal-metering-result\";\n\nexport interface ExposureMeterSnapshotIdentity {\n sourceKind:\n ExposureMeterTargetSourceKind;\n measurementId: string;\n sceneStateId: string;\n meteringProfileId: string;\n}\n\ninterface ExposureMeterTargetBase {\n schemaVersion:\n typeof EXPOSURE_METER_TARGET_SCHEMA_VERSION;\n targetId: string;\n sourceMeterSnapshot:\n ExposureMeterSnapshotIdentity;\n scientificStatus: \"approximation\";\n inputDomain:\n \"relative-pre-exposure-linear-signal\";\n baseTargetRelativeSignal: number;\n meteredRelativeSignal: number;\n exposureCompensationStops: number;\n exposureCompensationApplied: boolean;\n meterMeasurementMutated: false;\n sourceMeterSnapshotPreserved: true;\n resultReusableForAeLock: true;\n automaticExposureResolved: false;\n intendedConsumer:\n \"exposure-mode-resolver\";\n}\n\nexport type ExposureMeterTarget =\n | (ExposureMeterTargetBase & {\n status: \"resolved\";\n baseRequiredExposureScaleToTarget:\n number;\n baseExposureOffsetStopsToTarget:\n number;\n requiredExposureScaleToTarget:\n number;\n exposureOffsetStopsToTarget:\n number;\n })\n | (ExposureMeterTargetBase & {\n status: \"no-signal\";\n baseRequiredExposureScaleResolved:\n false;\n baseExposureOffsetStopsResolved:\n false;\n requiredExposureScaleResolved:\n false;\n exposureOffsetStopsResolved:\n false;\n });\n\nexport interface CreateExposureMeterTargetInput {\n targetId: string;\n meterResult:\n ExposureMeterTargetSourceResult;\n}\n\nexport interface SetExposureCompensationOnMeterTargetInput {\n targetId: string;\n baseTarget: ExposureMeterTarget;\n exposureCompensationStops: number;\n}\n\nfunction requireNonEmptyString(\n value: unknown,\n path: string\n): string {\n if (\n typeof value !== \"string\" ||\n value.trim().length === 0\n ) {\n throw new InvalidConfigurationError(\n path + \" must be a non-empty string.\"\n );\n }\n return value.trim();\n}\n\nfunction requireFinite(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isFinite(value)\n ) {\n throw new InvalidScientificInputError(\n path + \" must be finite.\"\n );\n }\n return value;\n}\n\nfunction requirePositiveFinite(\n value: unknown,\n path: string\n): number {\n const parsed = requireFinite(\n value,\n path\n );\n if (parsed <= 0) {\n throw new InvalidScientificInputError(\n path + \" must be greater than zero.\"\n );\n }\n return parsed;\n}\n\nfunction requireNonNegativeFinite(\n value: unknown,\n path: string\n): number {\n const parsed = requireFinite(\n value,\n path\n );\n if (parsed < 0) {\n throw new InvalidScientificInputError(\n path +\n \" must be greater than or equal to zero.\"\n );\n }\n return parsed;\n}\n\nfunction meterSnapshotIdentity(\n result:\n ExposureMeterTargetSourceResult\n): ExposureMeterSnapshotIdentity {\n const hasSpatialId =\n \"measurementId\" in result;\n const hasTemporalId =\n \"temporalMeasurementId\" in\n result;\n\n if (\n hasSpatialId === hasTemporalId\n ) {\n throw new InvalidScientificInputError(\n \"meterResult must identify exactly one spatial or temporal metering result.\"\n );\n }\n\n return {\n sourceKind: hasSpatialId\n ? \"spatial-metering-result\"\n : \"temporal-metering-result\",\n measurementId:\n requireNonEmptyString(\n hasSpatialId\n ? result.measurementId\n : result\n .temporalMeasurementId,\n hasSpatialId\n ? \"meterResult.measurementId\"\n : \"meterResult.temporalMeasurementId\"\n ),\n sceneStateId:\n requireNonEmptyString(\n result.sceneStateId,\n \"meterResult.sceneStateId\"\n ),\n meteringProfileId:\n requireNonEmptyString(\n result.profileId,\n \"meterResult.profileId\"\n )\n };\n}\n\nfunction validateCommonMeterResult(\n result:\n ExposureMeterTargetSourceResult\n): void {\n requireInputObject(result, \"result input\");\n if (\n result.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n 'meterResult.scientificStatus must be \"approximation\" for schema 0.1.0.'\n );\n }\n if (\n result.inputDomain !==\n \"relative-pre-exposure-linear-signal\"\n ) {\n throw new InvalidConfigurationError(\n 'meterResult.inputDomain must be \"relative-pre-exposure-linear-signal\".'\n );\n }\n if (\n result.exposureCompensationApplied !==\n false\n ) {\n throw new InvalidScientificInputError(\n \"meterResult must be the uncompensated base metering result.\"\n );\n }\n if (\n result.automaticExposureResolved !==\n false\n ) {\n throw new InvalidScientificInputError(\n \"meterResult must precede automatic exposure resolution.\"\n );\n }\n if (\n result.resultReusableForAeLock !==\n true\n ) {\n throw new InvalidScientificInputError(\n \"meterResult must preserve reusable meter-result identity.\"\n );\n }\n\n requirePositiveFinite(\n result.targetRelativeSignal,\n \"meterResult.targetRelativeSignal\"\n );\n requireNonNegativeFinite(\n result.meteredRelativeSignal,\n \"meterResult.meteredRelativeSignal\"\n );\n}\n\nfunction createBaseTargetFields(\n targetId: string,\n meterResult:\n ExposureMeterTargetSourceResult\n): ExposureMeterTargetBase {\n validateCommonMeterResult(\n meterResult\n );\n\n return {\n schemaVersion:\n EXPOSURE_METER_TARGET_SCHEMA_VERSION,\n targetId:\n requireNonEmptyString(\n targetId,\n \"targetId\"\n ),\n sourceMeterSnapshot:\n meterSnapshotIdentity(\n meterResult\n ),\n scientificStatus: \"approximation\",\n inputDomain:\n \"relative-pre-exposure-linear-signal\",\n baseTargetRelativeSignal:\n meterResult.targetRelativeSignal,\n meteredRelativeSignal:\n meterResult.meteredRelativeSignal,\n exposureCompensationStops: 0,\n exposureCompensationApplied:\n false,\n meterMeasurementMutated: false,\n sourceMeterSnapshotPreserved:\n true,\n resultReusableForAeLock: true,\n automaticExposureResolved: false,\n intendedConsumer:\n \"exposure-mode-resolver\"\n };\n}\n\n/**\n * Freezes one metering result into a stable relative exposure target, for\n * camera-control logic to use.\n *\n * The target copies the meter snapshot by value. Later changes to the scene or\n * light do not change it. This is what the engine provides for AE lock\n * (auto-exposure lock).\n */\nexport function createExposureMeterTargetFromMeteringResult(\n input:\n CreateExposureMeterTargetInput\n): ExposureMeterTarget {\n requireInputKeys(input, \"createExposureMeterTargetFromMeteringResult\", [\"targetId\", \"meterResult\"]);\n const base =\n createBaseTargetFields(\n input.targetId,\n input.meterResult\n );\n\n if (\n input.meterResult.status ===\n \"no-signal\"\n ) {\n if (\n input.meterResult\n .meteredRelativeSignal !== 0\n ) {\n throw new InvalidScientificInputError(\n \"A no-signal meter result must have meteredRelativeSignal equal to zero.\"\n );\n }\n return {\n ...base,\n status: \"no-signal\",\n baseRequiredExposureScaleResolved:\n false,\n baseExposureOffsetStopsResolved:\n false,\n requiredExposureScaleResolved:\n false,\n exposureOffsetStopsResolved:\n false\n };\n }\n\n const meteredRelativeSignal =\n requirePositiveFinite(\n input.meterResult\n .meteredRelativeSignal,\n \"meterResult.meteredRelativeSignal\"\n );\n const baseScale =\n base.baseTargetRelativeSignal /\n meteredRelativeSignal;\n if (\n !Number.isFinite(baseScale) ||\n baseScale <= 0\n ) {\n throw new InvalidScientificInputError(\n \"Base required exposure scale must remain finite and greater than zero.\"\n );\n }\n const baseStops =\n Math.log2(baseScale);\n if (!Number.isFinite(baseStops)) {\n throw new InvalidScientificInputError(\n \"Base exposure offset must remain finite.\"\n );\n }\n\n return {\n ...base,\n status: \"resolved\",\n baseRequiredExposureScaleToTarget:\n baseScale,\n baseExposureOffsetStopsToTarget:\n baseStops,\n requiredExposureScaleToTarget:\n baseScale,\n exposureOffsetStopsToTarget:\n baseStops\n };\n}\n\nfunction validateBaseTarget(\n target: ExposureMeterTarget\n): void {\n requireInputObject(target, \"target input\");\n if (\n target.schemaVersion !==\n EXPOSURE_METER_TARGET_SCHEMA_VERSION\n ) {\n throw new InvalidConfigurationError(\n 'baseTarget.schemaVersion must be \"' +\n EXPOSURE_METER_TARGET_SCHEMA_VERSION +\n '\".'\n );\n }\n requireNonEmptyString(\n target.targetId,\n \"baseTarget.targetId\"\n );\n requireInputObject(target.sourceMeterSnapshot, \"baseTarget.sourceMeterSnapshot\");\n requireNonEmptyString(\n target\n .sourceMeterSnapshot\n .measurementId,\n \"baseTarget.sourceMeterSnapshot.measurementId\"\n );\n requireNonEmptyString(\n target\n .sourceMeterSnapshot\n .sceneStateId,\n \"baseTarget.sourceMeterSnapshot.sceneStateId\"\n );\n requireNonEmptyString(\n target\n .sourceMeterSnapshot\n .meteringProfileId,\n \"baseTarget.sourceMeterSnapshot.meteringProfileId\"\n );\n if (\n target\n .sourceMeterSnapshot\n .sourceKind !==\n \"spatial-metering-result\" &&\n target\n .sourceMeterSnapshot\n .sourceKind !==\n \"temporal-metering-result\"\n ) {\n throw new InvalidConfigurationError(\n \"baseTarget.sourceMeterSnapshot.sourceKind is invalid.\"\n );\n }\n if (\n target.scientificStatus !==\n \"approximation\" ||\n target.inputDomain !==\n \"relative-pre-exposure-linear-signal\" ||\n target.meterMeasurementMutated !==\n false ||\n target.sourceMeterSnapshotPreserved !==\n true ||\n target.resultReusableForAeLock !==\n true ||\n target.automaticExposureResolved !==\n false ||\n target.intendedConsumer !==\n \"exposure-mode-resolver\"\n ) {\n throw new InvalidScientificInputError(\n \"baseTarget does not satisfy the exposure-meter target boundary.\"\n );\n }\n requirePositiveFinite(\n target.baseTargetRelativeSignal,\n \"baseTarget.baseTargetRelativeSignal\"\n );\n requireNonNegativeFinite(\n target.meteredRelativeSignal,\n \"baseTarget.meteredRelativeSignal\"\n );\n\n if (target.status === \"resolved\") {\n requirePositiveFinite(\n target\n .baseRequiredExposureScaleToTarget,\n \"baseTarget.baseRequiredExposureScaleToTarget\"\n );\n requireFinite(\n target\n .baseExposureOffsetStopsToTarget,\n \"baseTarget.baseExposureOffsetStopsToTarget\"\n );\n } else if (\n target.meteredRelativeSignal !== 0\n ) {\n throw new InvalidScientificInputError(\n \"A no-signal baseTarget must preserve zero meteredRelativeSignal.\"\n );\n }\n}\n\n/**\n * Sets an absolute exposure-compensation value on a frozen meter target.\n *\n * Compensation is always worked out again from the base target without\n * compensation, so repeated UI updates cannot build up floating-point or\n * control drift. Positive compensation asks for more exposure.\n */\nexport function setExposureCompensationOnMeterTarget(\n input:\n SetExposureCompensationOnMeterTargetInput\n): ExposureMeterTarget {\n requireInputKeys(input, \"setExposureCompensationOnMeterTarget\", [\"targetId\", \"baseTarget\", \"exposureCompensationStops\"]);\n validateBaseTarget(\n input.baseTarget\n );\n const targetId =\n requireNonEmptyString(\n input.targetId,\n \"targetId\"\n );\n if (\n targetId ===\n input.baseTarget.targetId\n ) {\n throw new InvalidScientificInputError(\n \"A compensated target must use a new targetId so changed control state cannot hide behind the same identity.\"\n );\n }\n\n const compensation =\n requireFinite(\n input.exposureCompensationStops,\n \"exposureCompensationStops\"\n );\n const common = {\n ...input.baseTarget,\n targetId,\n exposureCompensationStops:\n compensation,\n exposureCompensationApplied:\n compensation !== 0,\n meterMeasurementMutated:\n false as const,\n sourceMeterSnapshotPreserved:\n true as const,\n resultReusableForAeLock:\n true as const,\n automaticExposureResolved:\n false as const\n };\n\n if (\n input.baseTarget.status ===\n \"no-signal\"\n ) {\n return {\n ...common,\n status: \"no-signal\",\n baseRequiredExposureScaleResolved:\n false,\n baseExposureOffsetStopsResolved:\n false,\n requiredExposureScaleResolved:\n false,\n exposureOffsetStopsResolved:\n false\n };\n }\n\n const compensatedStops =\n input.baseTarget\n .baseExposureOffsetStopsToTarget +\n compensation;\n const compensatedScale =\n input.baseTarget\n .baseRequiredExposureScaleToTarget *\n Math.pow(2, compensation);\n\n if (\n !Number.isFinite(\n compensatedStops\n ) ||\n !Number.isFinite(\n compensatedScale\n ) ||\n compensatedScale <= 0\n ) {\n throw new InvalidScientificInputError(\n \"Compensated exposure target must remain finite and greater than zero.\"\n );\n }\n\n return {\n ...common,\n status: \"resolved\",\n baseRequiredExposureScaleToTarget:\n input.baseTarget\n .baseRequiredExposureScaleToTarget,\n baseExposureOffsetStopsToTarget:\n input.baseTarget\n .baseExposureOffsetStopsToTarget,\n requiredExposureScaleToTarget:\n compensatedScale,\n exposureOffsetStopsToTarget:\n compensatedStops\n };\n}\n"]}
1
+ {"version":3,"file":"metering-target.js","sourceRoot":"","sources":["../../src/exposure/metering-target.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Meter targets: a frozen metering result that camera-control logic uses as its exposure target.\n *\n * The target copies the meter snapshot by value. Later changes to the scene or light do not change\n * it. This is what the engine provides for AE lock (auto-exposure lock).\n *\n * Exposure compensation is set as an absolute value. It is always worked out again from the base\n * target without compensation, so repeated UI updates cannot build up floating-point or control\n * drift. Positive compensation asks for more exposure.\n * @see docs/USAGE.md for the equations, units, coordinate conventions, known blockers and support\n * limits.\n */\n\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport type {\n ExposureMeteringResult\n} from \"./metering.js\";\nimport type {\n SceneRadianceTemporalExposureMeteringResult\n} from \"./metering-temporal.js\";\nimport { requireInputObject, requireInputKeys, requireTrimmedConfigString as requireNonEmptyString, requireInputFinite as requireFinite, requireInputFiniteAboveZero as requirePositiveFinite, requireInputFiniteAtLeastZero as requireNonNegativeFinite } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\n\nexport const EXPOSURE_METER_TARGET_SCHEMA_VERSION =\n \"0.1.0\" as const;\n\nexport type ExposureMeterTargetSourceResult =\n | ExposureMeteringResult\n | SceneRadianceTemporalExposureMeteringResult;\n\nexport type ExposureMeterTargetSourceKind =\n | \"spatial-metering-result\"\n | \"temporal-metering-result\";\n\nexport interface ExposureMeterSnapshotIdentity {\n sourceKind:\n ExposureMeterTargetSourceKind;\n measurementId: string;\n sceneStateId: string;\n meteringProfileId: string;\n}\n\ninterface ExposureMeterTargetBase {\n schemaVersion:\n typeof EXPOSURE_METER_TARGET_SCHEMA_VERSION;\n targetId: string;\n sourceMeterSnapshot:\n ExposureMeterSnapshotIdentity;\n scientificStatus: \"approximation\";\n inputDomain:\n \"relative-pre-exposure-linear-signal\";\n baseTargetRelativeSignal: number;\n meteredRelativeSignal: number;\n exposureCompensationStops: number;\n exposureCompensationApplied: boolean;\n meterMeasurementMutated: false;\n sourceMeterSnapshotPreserved: true;\n resultReusableForAeLock: true;\n automaticExposureResolved: false;\n intendedConsumer:\n \"exposure-mode-resolver\";\n}\n\nexport type ExposureMeterTarget =\n | (ExposureMeterTargetBase & {\n status: \"resolved\";\n baseRequiredExposureScaleToTarget:\n number;\n baseExposureOffsetStopsToTarget:\n number;\n requiredExposureScaleToTarget:\n number;\n exposureOffsetStopsToTarget:\n number;\n })\n | (ExposureMeterTargetBase & {\n status: \"no-signal\";\n baseRequiredExposureScaleResolved:\n false;\n baseExposureOffsetStopsResolved:\n false;\n requiredExposureScaleResolved:\n false;\n exposureOffsetStopsResolved:\n false;\n });\n\nexport interface CreateExposureMeterTargetInput {\n targetId: string;\n meterResult:\n ExposureMeterTargetSourceResult;\n}\n\nexport interface SetExposureCompensationOnMeterTargetInput {\n targetId: string;\n baseTarget: ExposureMeterTarget;\n exposureCompensationStops: number;\n}\n\nfunction meterSnapshotIdentity(\n result:\n ExposureMeterTargetSourceResult\n): ExposureMeterSnapshotIdentity {\n const hasSpatialId =\n \"measurementId\" in result;\n const hasTemporalId =\n \"temporalMeasurementId\" in\n result;\n\n if (\n hasSpatialId === hasTemporalId\n ) {\n throw new InvalidScientificInputError(\n \"meterResult must identify exactly one spatial or temporal metering result.\"\n );\n }\n\n return {\n sourceKind: hasSpatialId\n ? \"spatial-metering-result\"\n : \"temporal-metering-result\",\n measurementId:\n requireNonEmptyString(\n hasSpatialId\n ? result.measurementId\n : result\n .temporalMeasurementId,\n hasSpatialId\n ? \"meterResult.measurementId\"\n : \"meterResult.temporalMeasurementId\"\n ),\n sceneStateId:\n requireNonEmptyString(\n result.sceneStateId,\n \"meterResult.sceneStateId\"\n ),\n meteringProfileId:\n requireNonEmptyString(\n result.profileId,\n \"meterResult.profileId\"\n )\n };\n}\n\nfunction validateCommonMeterResult(\n result:\n ExposureMeterTargetSourceResult\n): void {\n requireInputObject(result, \"result input\");\n if (\n result.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n 'meterResult.scientificStatus must be \"approximation\" for schema 0.1.0.'\n );\n }\n if (\n result.inputDomain !==\n \"relative-pre-exposure-linear-signal\"\n ) {\n throw new InvalidConfigurationError(\n 'meterResult.inputDomain must be \"relative-pre-exposure-linear-signal\".'\n );\n }\n if (\n result.exposureCompensationApplied !==\n false\n ) {\n throw new InvalidScientificInputError(\n \"meterResult must be the uncompensated base metering result.\"\n );\n }\n if (\n result.automaticExposureResolved !==\n false\n ) {\n throw new InvalidScientificInputError(\n \"meterResult must precede automatic exposure resolution.\"\n );\n }\n if (\n result.resultReusableForAeLock !==\n true\n ) {\n throw new InvalidScientificInputError(\n \"meterResult must preserve reusable meter-result identity.\"\n );\n }\n\n requirePositiveFinite(\n result.targetRelativeSignal,\n \"meterResult.targetRelativeSignal\"\n );\n requireNonNegativeFinite(\n result.meteredRelativeSignal,\n \"meterResult.meteredRelativeSignal\"\n );\n}\n\nfunction createBaseTargetFields(\n targetId: string,\n meterResult:\n ExposureMeterTargetSourceResult\n): ExposureMeterTargetBase {\n validateCommonMeterResult(\n meterResult\n );\n\n return {\n schemaVersion:\n EXPOSURE_METER_TARGET_SCHEMA_VERSION,\n targetId:\n requireNonEmptyString(\n targetId,\n \"targetId\"\n ),\n sourceMeterSnapshot:\n meterSnapshotIdentity(\n meterResult\n ),\n scientificStatus: \"approximation\",\n inputDomain:\n \"relative-pre-exposure-linear-signal\",\n baseTargetRelativeSignal:\n meterResult.targetRelativeSignal,\n meteredRelativeSignal:\n meterResult.meteredRelativeSignal,\n exposureCompensationStops: 0,\n exposureCompensationApplied:\n false,\n meterMeasurementMutated: false,\n sourceMeterSnapshotPreserved:\n true,\n resultReusableForAeLock: true,\n automaticExposureResolved: false,\n intendedConsumer:\n \"exposure-mode-resolver\"\n };\n}\n\n/**\n * Freezes one metering result into a stable relative exposure target, for\n * camera-control logic to use.\n *\n * The target copies the meter snapshot by value. Later changes to the scene or\n * light do not change it. This is what the engine provides for AE lock\n * (auto-exposure lock).\n */\nexport function createExposureMeterTargetFromMeteringResult(\n input:\n CreateExposureMeterTargetInput\n): ExposureMeterTarget {\n requireInputKeys(input, \"createExposureMeterTargetFromMeteringResult\", [\"targetId\", \"meterResult\"]);\n const base =\n createBaseTargetFields(\n input.targetId,\n input.meterResult\n );\n\n if (\n input.meterResult.status ===\n \"no-signal\"\n ) {\n if (\n input.meterResult\n .meteredRelativeSignal !== 0\n ) {\n throw new InvalidScientificInputError(\n \"A no-signal meter result must have meteredRelativeSignal equal to zero.\"\n );\n }\n return {\n ...base,\n status: \"no-signal\",\n baseRequiredExposureScaleResolved:\n false,\n baseExposureOffsetStopsResolved:\n false,\n requiredExposureScaleResolved:\n false,\n exposureOffsetStopsResolved:\n false\n };\n }\n\n const meteredRelativeSignal =\n requirePositiveFinite(\n input.meterResult\n .meteredRelativeSignal,\n \"meterResult.meteredRelativeSignal\"\n );\n const baseScale =\n base.baseTargetRelativeSignal /\n meteredRelativeSignal;\n if (\n !Number.isFinite(baseScale) ||\n baseScale <= 0\n ) {\n throw new InvalidScientificInputError(\n \"Base required exposure scale must remain finite and greater than zero.\"\n );\n }\n const baseStops =\n Math.log2(baseScale);\n if (!Number.isFinite(baseStops)) {\n throw new InvalidScientificInputError(\n \"Base exposure offset must remain finite.\"\n );\n }\n\n return {\n ...base,\n status: \"resolved\",\n baseRequiredExposureScaleToTarget:\n baseScale,\n baseExposureOffsetStopsToTarget:\n baseStops,\n requiredExposureScaleToTarget:\n baseScale,\n exposureOffsetStopsToTarget:\n baseStops\n };\n}\n\nfunction validateBaseTarget(\n target: ExposureMeterTarget\n): void {\n requireInputObject(target, \"target input\");\n if (\n target.schemaVersion !==\n EXPOSURE_METER_TARGET_SCHEMA_VERSION\n ) {\n throw new InvalidConfigurationError(\n 'baseTarget.schemaVersion must be \"' +\n EXPOSURE_METER_TARGET_SCHEMA_VERSION +\n '\".'\n );\n }\n requireNonEmptyString(\n target.targetId,\n \"baseTarget.targetId\"\n );\n requireInputObject(target.sourceMeterSnapshot, \"baseTarget.sourceMeterSnapshot\");\n requireNonEmptyString(\n target\n .sourceMeterSnapshot\n .measurementId,\n \"baseTarget.sourceMeterSnapshot.measurementId\"\n );\n requireNonEmptyString(\n target\n .sourceMeterSnapshot\n .sceneStateId,\n \"baseTarget.sourceMeterSnapshot.sceneStateId\"\n );\n requireNonEmptyString(\n target\n .sourceMeterSnapshot\n .meteringProfileId,\n \"baseTarget.sourceMeterSnapshot.meteringProfileId\"\n );\n if (\n target\n .sourceMeterSnapshot\n .sourceKind !==\n \"spatial-metering-result\" &&\n target\n .sourceMeterSnapshot\n .sourceKind !==\n \"temporal-metering-result\"\n ) {\n throw new InvalidConfigurationError(\n \"baseTarget.sourceMeterSnapshot.sourceKind is invalid.\"\n );\n }\n if (\n target.scientificStatus !==\n \"approximation\" ||\n target.inputDomain !==\n \"relative-pre-exposure-linear-signal\" ||\n target.meterMeasurementMutated !==\n false ||\n target.sourceMeterSnapshotPreserved !==\n true ||\n target.resultReusableForAeLock !==\n true ||\n target.automaticExposureResolved !==\n false ||\n target.intendedConsumer !==\n \"exposure-mode-resolver\"\n ) {\n throw new InvalidScientificInputError(\n \"baseTarget does not satisfy the exposure-meter target boundary.\"\n );\n }\n requirePositiveFinite(\n target.baseTargetRelativeSignal,\n \"baseTarget.baseTargetRelativeSignal\"\n );\n requireNonNegativeFinite(\n target.meteredRelativeSignal,\n \"baseTarget.meteredRelativeSignal\"\n );\n\n if (target.status === \"resolved\") {\n requirePositiveFinite(\n target\n .baseRequiredExposureScaleToTarget,\n \"baseTarget.baseRequiredExposureScaleToTarget\"\n );\n requireFinite(\n target\n .baseExposureOffsetStopsToTarget,\n \"baseTarget.baseExposureOffsetStopsToTarget\"\n );\n } else if (\n target.meteredRelativeSignal !== 0\n ) {\n throw new InvalidScientificInputError(\n \"A no-signal baseTarget must preserve zero meteredRelativeSignal.\"\n );\n }\n}\n\n/**\n * Sets an absolute exposure-compensation value on a frozen meter target.\n *\n * Compensation is always worked out again from the base target without\n * compensation, so repeated UI updates cannot build up floating-point or\n * control drift. Positive compensation asks for more exposure.\n */\nexport function setExposureCompensationOnMeterTarget(\n input:\n SetExposureCompensationOnMeterTargetInput\n): ExposureMeterTarget {\n requireInputKeys(input, \"setExposureCompensationOnMeterTarget\", [\"targetId\", \"baseTarget\", \"exposureCompensationStops\"]);\n validateBaseTarget(\n input.baseTarget\n );\n const targetId =\n requireNonEmptyString(\n input.targetId,\n \"targetId\"\n );\n if (\n targetId ===\n input.baseTarget.targetId\n ) {\n throw new InvalidScientificInputError(\n \"A compensated target must use a new targetId so changed control state cannot hide behind the same identity.\"\n );\n }\n\n const compensation =\n requireFinite(\n input.exposureCompensationStops,\n \"exposureCompensationStops\"\n );\n const common = {\n ...input.baseTarget,\n targetId,\n exposureCompensationStops:\n compensation,\n exposureCompensationApplied:\n compensation !== 0,\n meterMeasurementMutated:\n false as const,\n sourceMeterSnapshotPreserved:\n true as const,\n resultReusableForAeLock:\n true as const,\n automaticExposureResolved:\n false as const\n };\n\n if (\n input.baseTarget.status ===\n \"no-signal\"\n ) {\n return {\n ...common,\n status: \"no-signal\",\n baseRequiredExposureScaleResolved:\n false,\n baseExposureOffsetStopsResolved:\n false,\n requiredExposureScaleResolved:\n false,\n exposureOffsetStopsResolved:\n false\n };\n }\n\n const compensatedStops =\n input.baseTarget\n .baseExposureOffsetStopsToTarget +\n compensation;\n const compensatedScale =\n input.baseTarget\n .baseRequiredExposureScaleToTarget *\n Math.pow(2, compensation);\n\n if (\n !Number.isFinite(\n compensatedStops\n ) ||\n !Number.isFinite(\n compensatedScale\n ) ||\n compensatedScale <= 0\n ) {\n throw new InvalidScientificInputError(\n \"Compensated exposure target must remain finite and greater than zero.\"\n );\n }\n\n return {\n ...common,\n status: \"resolved\",\n baseRequiredExposureScaleToTarget:\n input.baseTarget\n .baseRequiredExposureScaleToTarget,\n baseExposureOffsetStopsToTarget:\n input.baseTarget\n .baseExposureOffsetStopsToTarget,\n requiredExposureScaleToTarget:\n compensatedScale,\n exposureOffsetStopsToTarget:\n compensatedStops\n };\n}\n"]}
@@ -13,16 +13,9 @@
13
13
  import { approximationResult } from "../core/calculation-result.js";
14
14
  import { InvalidScientificInputError } from "../core/validation.js";
15
15
  import { meterRelativeExposure, parseExposureMeteringProfile } from "./metering.js";
16
- import { requireInputKeys, requireInputObject } from "../core/record-validation.js";
16
+ import { requireInputKeys, requireInputObject, requireTrimmedConfigString as requireNonEmptyString } from "../core/record-validation.js";
17
17
  import { InvalidConfigurationError } from "../core/configuration-error.js";
18
18
  const NORMALIZED_WEIGHT_TOLERANCE = 1e-9;
19
- function requireNonEmptyString(value, path) {
20
- if (typeof value !== "string" ||
21
- value.trim().length === 0) {
22
- throw new InvalidConfigurationError(path + " must be a non-empty string.");
23
- }
24
- return value.trim();
25
- }
26
19
  function requireNormalizedWeight(value, path) {
27
20
  if (typeof value !== "number" ||
28
21
  !Number.isFinite(value) ||
@@ -1 +1 @@
1
- {"version":3,"file":"metering-temporal.js","sourceRoot":"","sources":["../../src/exposure/metering-temporal.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Metering over time: a declared weighted time average of relative meter samples taken before the\n * exposure (pre-exposure) and derived from scene radiance. Useful for flickering light.\n *\n * Each time sample must already be bound to one temporal illumination profile and one capture time.\n * Times are measured from the first opening-boundary phase (when the shutter starts to open). Each\n * time sample is metered spatially first, with the same declared profile. The resulting single\n * values are then combined linearly using normalized time weights that the caller declares.\n * @see docs/USAGE.md for the equations, units, coordinate conventions, known blockers and support\n * limits.\n */\n\nimport {\n approximationResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport {\n meterRelativeExposure,\n parseExposureMeteringProfile,\n type ExposureMeteringPolicy,\n type ExposureMeteringProfile\n} from \"./metering.js\";\nimport type {\n SceneRadianceDerivedExposureMeteringSampleSet\n} from \"./metering-scene-radiance.js\";\nimport { requireInputKeys, requireInputObject } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\n\nconst NORMALIZED_WEIGHT_TOLERANCE =\n 1e-9;\n\nexport interface SceneRadianceTemporalMeteringSample {\n normalizedTimeWeight: number;\n sampleSet:\n SceneRadianceDerivedExposureMeteringSampleSet;\n}\n\nexport interface MeterSceneRadianceTemporalExposureInput {\n temporalMeasurementId: string;\n profile: ExposureMeteringProfile;\n policy: {\n kind: \"weighted-time-average\";\n timeReference:\n \"first-opening-boundary-phase\";\n };\n temporalSamples:\n readonly SceneRadianceTemporalMeteringSample[];\n}\n\ninterface SceneRadianceTemporalExposureMeteringResultBase {\n temporalMeasurementId: string;\n sceneStateId: string;\n profileId: string;\n scientificStatus: \"approximation\";\n inputDomain:\n \"relative-pre-exposure-linear-signal\";\n captureRegion:\n \"oriented-active-capture\";\n spatialPolicyKind:\n ExposureMeteringPolicy[\"kind\"];\n temporalPolicyKind:\n \"weighted-time-average\";\n timeReference:\n \"first-opening-boundary-phase\";\n temporalSampleCount: number;\n normalizedTimeWeightSum: number;\n captureTimeRangeSecondsFromReference: {\n minimum: number;\n maximum: number;\n };\n targetRelativeSignal: number;\n meteredRelativeSignal: number;\n outputCropUsedForMetering: false;\n exposureCompensationApplied: false;\n finalToneMappingUsed: false;\n displayGammaUsed: false;\n calibratedLuminanceClaimAuthorized:\n false;\n calibratedSceneRadianceClaimAuthorized:\n false;\n automaticExposureResolved: false;\n resultReusableForAeLock: true;\n temporalAveragingExplicit: true;\n flashTtlMeteringPerformed: false;\n}\n\nexport type SceneRadianceTemporalExposureMeteringResult =\n | (SceneRadianceTemporalExposureMeteringResultBase & {\n status: \"resolved\";\n requiredExposureScaleToTarget:\n number;\n exposureOffsetStopsToTarget:\n number;\n })\n | (SceneRadianceTemporalExposureMeteringResultBase & {\n status: \"no-signal\";\n requiredExposureScaleResolved:\n false;\n exposureOffsetStopsResolved:\n false;\n });\n\nfunction requireNonEmptyString(\n value: unknown,\n path: string\n): string {\n if (\n typeof value !== \"string\" ||\n value.trim().length === 0\n ) {\n throw new InvalidConfigurationError(\n path + \" must be a non-empty string.\"\n );\n }\n return value.trim();\n}\n\nfunction requireNormalizedWeight(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isFinite(value) ||\n value <= 0 ||\n value > 1\n ) {\n throw new InvalidScientificInputError(\n path +\n \" must be finite and in (0, 1].\"\n );\n }\n return value;\n}\n\nfunction contextKey(\n sampleSet:\n SceneRadianceDerivedExposureMeteringSampleSet\n): string {\n const context =\n sampleSet.sourceContext;\n const geometry =\n sampleSet.captureGeometry;\n return JSON.stringify({\n sceneId: context.sceneId,\n sceneStateId:\n context.sceneStateId,\n providerProfileId:\n context.providerProfileId,\n illuminationProfileId:\n context.illuminationProfileId,\n materialResponseProfileId:\n context.materialResponseProfileId,\n derivationProfileId:\n context.derivationProfileId,\n materialResponseFidelity:\n context.materialResponseFidelity,\n orientation:\n geometry.orientedCapture\n .orientation,\n activeNativeRect:\n geometry.activeCapture.nativeRect,\n activeImagingArea:\n geometry.activeCapture.imagingArea,\n orientedImagingArea:\n geometry.orientedCapture\n .imagingArea\n });\n}\n\n/**\n * Meters a declared weighted time average of relative pre-exposure samples\n * derived from scene radiance.\n *\n * Each time sample must already be bound to one temporal illumination profile\n * and one capture time, measured from the first opening-boundary phase. Each\n * time sample is metered spatially first, with the same declared profile. The\n * resulting single values are then combined linearly using normalized time\n * weights that the caller declares.\n */\nexport function meterSceneRadianceTemporalExposure(\n input:\n MeterSceneRadianceTemporalExposureInput\n): CalculationResult<\n SceneRadianceTemporalExposureMeteringResult\n> {\n requireInputKeys(input, \"meterSceneRadianceTemporalExposure\", [\"temporalMeasurementId\", \"profile\", \"policy\", \"temporalSamples\"]);\n const temporalMeasurementId =\n requireNonEmptyString(\n input.temporalMeasurementId,\n \"temporalMeasurementId\"\n );\n\n const profile =\n parseExposureMeteringProfile(\n input.profile\n );\n\n if (\n profile.policy.kind ===\n \"highlight-priority\"\n ) {\n // A time average of per-snapshot quantiles is not the quantile of the time-averaged signal.\n throw new InvalidScientificInputError(\n \"highlight-priority is a spatial quantile, not a linear signal mean; temporal averaging of snapshot quantiles is not defined by this model.\"\n );\n }\n requireInputObject(input.policy, \"policy\");\n if (\n input.policy.kind !==\n \"weighted-time-average\"\n ) {\n throw new InvalidConfigurationError(\n 'policy.kind must be \"weighted-time-average\".'\n );\n }\n if (\n input.policy.timeReference !==\n \"first-opening-boundary-phase\"\n ) {\n throw new InvalidConfigurationError(\n 'policy.timeReference must be \"first-opening-boundary-phase\".'\n );\n }\n if (\n !Array.isArray(\n input.temporalSamples\n ) ||\n input.temporalSamples.length === 0\n ) {\n throw new InvalidConfigurationError(\n \"temporalSamples must be a non-empty array.\"\n );\n }\n\n input.temporalSamples.forEach((entry, index) => {\n const path = \"temporalSamples[\" + index + \"]\";\n requireInputObject(entry, path);\n const sampleSet = entry.sampleSet;\n requireInputObject(sampleSet, path + \".sampleSet\");\n requireInputObject(sampleSet.sourceContext, path + \".sampleSet.sourceContext\");\n requireInputObject(sampleSet.sourceContext.temporal, path + \".sampleSet.sourceContext.temporal\");\n requireInputObject(sampleSet.captureGeometry, path + \".sampleSet.captureGeometry\");\n requireInputObject(sampleSet.captureGeometry.orientedCapture, path + \".sampleSet.captureGeometry.orientedCapture\");\n requireInputObject(sampleSet.captureGeometry.activeCapture, path + \".sampleSet.captureGeometry.activeCapture\");\n });\n const firstSampleSet =\n input.temporalSamples[0]!\n .sampleSet;\n const firstContext =\n firstSampleSet.sourceContext;\n if (\n firstContext.temporal.kind !==\n \"registered-time-varying\"\n ) {\n throw new InvalidScientificInputError(\n \"Temporal metering requires scene-radiance sample sets with registered-time-varying source context.\"\n );\n }\n\n const expectedContextKey =\n contextKey(firstSampleSet);\n const sceneStateId =\n firstSampleSet.sceneStateId;\n const temporalProfileId =\n firstContext.temporal\n .illuminationTemporalProfileId;\n\n let weightSum = 0;\n let meteredRelativeSignal = 0;\n let previousTime =\n Number.NEGATIVE_INFINITY;\n let minimumTime =\n Number.POSITIVE_INFINITY;\n let maximumTime =\n Number.NEGATIVE_INFINITY;\n\n for (\n let index = 0;\n index <\n input.temporalSamples.length;\n index += 1\n ) {\n const entry =\n input.temporalSamples[index]!;\n const path =\n \"temporalSamples[\" +\n index +\n \"]\";\n const weight =\n requireNormalizedWeight(\n entry.normalizedTimeWeight,\n path +\n \".normalizedTimeWeight\"\n );\n const sampleSet =\n entry.sampleSet;\n const context =\n sampleSet.sourceContext;\n\n if (\n context.temporal.kind !==\n \"registered-time-varying\"\n ) {\n throw new InvalidScientificInputError(\n path +\n \".sampleSet must use registered-time-varying source context.\"\n );\n }\n if (\n context.temporal\n .illuminationTemporalProfileId !==\n temporalProfileId\n ) {\n throw new InvalidScientificInputError(\n \"All temporal metering samples must bind the same illuminationTemporalProfileId.\"\n );\n }\n if (\n sampleSet.sceneStateId !==\n sceneStateId ||\n context.sceneStateId !==\n sceneStateId\n ) {\n throw new InvalidScientificInputError(\n \"All temporal metering samples must bind the same sceneStateId.\"\n );\n }\n if (\n contextKey(sampleSet) !==\n expectedContextKey\n ) {\n throw new InvalidScientificInputError(\n \"All temporal metering samples must share the same scene/provider/material/derivation/capture-geometry context.\"\n );\n }\n\n const captureTime =\n context.temporal\n .captureTimeSecondsFromReference;\n if (\n !Number.isFinite(captureTime)\n ) {\n throw new InvalidScientificInputError(\n path +\n \" capture time must be finite.\"\n );\n }\n if (\n captureTime <= previousTime\n ) {\n throw new InvalidScientificInputError(\n \"Temporal metering capture times must be strictly increasing.\"\n );\n }\n previousTime = captureTime;\n minimumTime = Math.min(\n minimumTime,\n captureTime\n );\n maximumTime = Math.max(\n maximumTime,\n captureTime\n );\n\n const spatialMeter =\n meterRelativeExposure({\n profile,\n sampleSet\n });\n meteredRelativeSignal +=\n spatialMeter.value\n .meteredRelativeSignal *\n weight;\n weightSum += weight;\n }\n\n if (\n !Number.isFinite(weightSum) ||\n Math.abs(weightSum - 1) >\n NORMALIZED_WEIGHT_TOLERANCE\n ) {\n throw new InvalidScientificInputError(\n \"temporalSamples normalizedTimeWeight values must sum to 1.\"\n );\n }\n if (\n !Number.isFinite(\n meteredRelativeSignal\n ) ||\n meteredRelativeSignal < 0\n ) {\n throw new InvalidScientificInputError(\n \"Temporal metered relative signal must remain finite and non-negative.\"\n );\n }\n\n const base = {\n temporalMeasurementId,\n sceneStateId,\n profileId:\n profile.profileId,\n scientificStatus:\n \"approximation\" as const,\n inputDomain:\n \"relative-pre-exposure-linear-signal\" as const,\n captureRegion:\n \"oriented-active-capture\" as const,\n spatialPolicyKind:\n profile.policy.kind,\n temporalPolicyKind:\n \"weighted-time-average\" as const,\n timeReference:\n \"first-opening-boundary-phase\" as const,\n temporalSampleCount:\n input.temporalSamples.length,\n normalizedTimeWeightSum: 1,\n captureTimeRangeSecondsFromReference: {\n minimum: minimumTime,\n maximum: maximumTime\n },\n targetRelativeSignal:\n profile.target\n .targetRelativeSignal,\n meteredRelativeSignal,\n outputCropUsedForMetering:\n false as const,\n exposureCompensationApplied:\n false as const,\n finalToneMappingUsed:\n false as const,\n displayGammaUsed:\n false as const,\n calibratedLuminanceClaimAuthorized:\n false as const,\n calibratedSceneRadianceClaimAuthorized:\n false as const,\n automaticExposureResolved:\n false as const,\n resultReusableForAeLock:\n true as const,\n temporalAveragingExplicit:\n true as const,\n flashTtlMeteringPerformed:\n false as const\n };\n\n const result:\n SceneRadianceTemporalExposureMeteringResult =\n meteredRelativeSignal === 0\n ? {\n ...base,\n status: \"no-signal\",\n requiredExposureScaleResolved:\n false,\n exposureOffsetStopsResolved:\n false\n }\n : {\n ...base,\n status: \"resolved\",\n requiredExposureScaleToTarget:\n profile.target\n .targetRelativeSignal /\n meteredRelativeSignal,\n exposureOffsetStopsToTarget:\n Math.log2(\n profile.target\n .targetRelativeSignal /\n meteredRelativeSignal\n )\n };\n\n return approximationResult(\n result,\n \"scene-radiance-relative-temporal-metering\",\n \"1.0.0\",\n [\n \"Each temporal sample is a renderer/provider-supplied relative pre-exposure reduction bound to the same scene-radiance provider context.\",\n \"Spectral radiance is not converted to calibrated luminance or a camera spectral-meter response by this model.\",\n \"Time averaging is explicit and linear in the declared relative pre-exposure signal domain.\",\n \"Temporal sample weights are caller-declared, positive, normalized to one, and are not inferred from source waveform frequency or sensor readout timing.\",\n \"All temporal samples share one committed sceneStateId and one provider/material/derivation/capture-geometry context.\",\n \"Exposure compensation and automatic aperture/shutter/ISO resolution remain downstream.\",\n \"This ambient meter does not implement flash/TTL metering.\"\n ]\n );\n}\n"]}
1
+ {"version":3,"file":"metering-temporal.js","sourceRoot":"","sources":["../../src/exposure/metering-temporal.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Metering over time: a declared weighted time average of relative meter samples taken before the\n * exposure (pre-exposure) and derived from scene radiance. Useful for flickering light.\n *\n * Each time sample must already be bound to one temporal illumination profile and one capture time.\n * Times are measured from the first opening-boundary phase (when the shutter starts to open). Each\n * time sample is metered spatially first, with the same declared profile. The resulting single\n * values are then combined linearly using normalized time weights that the caller declares.\n * @see docs/USAGE.md for the equations, units, coordinate conventions, known blockers and support\n * limits.\n */\n\nimport {\n approximationResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport {\n meterRelativeExposure,\n parseExposureMeteringProfile,\n type ExposureMeteringPolicy,\n type ExposureMeteringProfile\n} from \"./metering.js\";\nimport type {\n SceneRadianceDerivedExposureMeteringSampleSet\n} from \"./metering-scene-radiance.js\";\nimport { requireInputKeys, requireInputObject, requireTrimmedConfigString as requireNonEmptyString } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\n\nconst NORMALIZED_WEIGHT_TOLERANCE =\n 1e-9;\n\nexport interface SceneRadianceTemporalMeteringSample {\n normalizedTimeWeight: number;\n sampleSet:\n SceneRadianceDerivedExposureMeteringSampleSet;\n}\n\nexport interface MeterSceneRadianceTemporalExposureInput {\n temporalMeasurementId: string;\n profile: ExposureMeteringProfile;\n policy: {\n kind: \"weighted-time-average\";\n timeReference:\n \"first-opening-boundary-phase\";\n };\n temporalSamples:\n readonly SceneRadianceTemporalMeteringSample[];\n}\n\ninterface SceneRadianceTemporalExposureMeteringResultBase {\n temporalMeasurementId: string;\n sceneStateId: string;\n profileId: string;\n scientificStatus: \"approximation\";\n inputDomain:\n \"relative-pre-exposure-linear-signal\";\n captureRegion:\n \"oriented-active-capture\";\n spatialPolicyKind:\n ExposureMeteringPolicy[\"kind\"];\n temporalPolicyKind:\n \"weighted-time-average\";\n timeReference:\n \"first-opening-boundary-phase\";\n temporalSampleCount: number;\n normalizedTimeWeightSum: number;\n captureTimeRangeSecondsFromReference: {\n minimum: number;\n maximum: number;\n };\n targetRelativeSignal: number;\n meteredRelativeSignal: number;\n outputCropUsedForMetering: false;\n exposureCompensationApplied: false;\n finalToneMappingUsed: false;\n displayGammaUsed: false;\n calibratedLuminanceClaimAuthorized:\n false;\n calibratedSceneRadianceClaimAuthorized:\n false;\n automaticExposureResolved: false;\n resultReusableForAeLock: true;\n temporalAveragingExplicit: true;\n flashTtlMeteringPerformed: false;\n}\n\nexport type SceneRadianceTemporalExposureMeteringResult =\n | (SceneRadianceTemporalExposureMeteringResultBase & {\n status: \"resolved\";\n requiredExposureScaleToTarget:\n number;\n exposureOffsetStopsToTarget:\n number;\n })\n | (SceneRadianceTemporalExposureMeteringResultBase & {\n status: \"no-signal\";\n requiredExposureScaleResolved:\n false;\n exposureOffsetStopsResolved:\n false;\n });\n\nfunction requireNormalizedWeight(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isFinite(value) ||\n value <= 0 ||\n value > 1\n ) {\n throw new InvalidScientificInputError(\n path +\n \" must be finite and in (0, 1].\"\n );\n }\n return value;\n}\n\nfunction contextKey(\n sampleSet:\n SceneRadianceDerivedExposureMeteringSampleSet\n): string {\n const context =\n sampleSet.sourceContext;\n const geometry =\n sampleSet.captureGeometry;\n return JSON.stringify({\n sceneId: context.sceneId,\n sceneStateId:\n context.sceneStateId,\n providerProfileId:\n context.providerProfileId,\n illuminationProfileId:\n context.illuminationProfileId,\n materialResponseProfileId:\n context.materialResponseProfileId,\n derivationProfileId:\n context.derivationProfileId,\n materialResponseFidelity:\n context.materialResponseFidelity,\n orientation:\n geometry.orientedCapture\n .orientation,\n activeNativeRect:\n geometry.activeCapture.nativeRect,\n activeImagingArea:\n geometry.activeCapture.imagingArea,\n orientedImagingArea:\n geometry.orientedCapture\n .imagingArea\n });\n}\n\n/**\n * Meters a declared weighted time average of relative pre-exposure samples\n * derived from scene radiance.\n *\n * Each time sample must already be bound to one temporal illumination profile\n * and one capture time, measured from the first opening-boundary phase. Each\n * time sample is metered spatially first, with the same declared profile. The\n * resulting single values are then combined linearly using normalized time\n * weights that the caller declares.\n */\nexport function meterSceneRadianceTemporalExposure(\n input:\n MeterSceneRadianceTemporalExposureInput\n): CalculationResult<\n SceneRadianceTemporalExposureMeteringResult\n> {\n requireInputKeys(input, \"meterSceneRadianceTemporalExposure\", [\"temporalMeasurementId\", \"profile\", \"policy\", \"temporalSamples\"]);\n const temporalMeasurementId =\n requireNonEmptyString(\n input.temporalMeasurementId,\n \"temporalMeasurementId\"\n );\n\n const profile =\n parseExposureMeteringProfile(\n input.profile\n );\n\n if (\n profile.policy.kind ===\n \"highlight-priority\"\n ) {\n // A time average of per-snapshot quantiles is not the quantile of the time-averaged signal.\n throw new InvalidScientificInputError(\n \"highlight-priority is a spatial quantile, not a linear signal mean; temporal averaging of snapshot quantiles is not defined by this model.\"\n );\n }\n requireInputObject(input.policy, \"policy\");\n if (\n input.policy.kind !==\n \"weighted-time-average\"\n ) {\n throw new InvalidConfigurationError(\n 'policy.kind must be \"weighted-time-average\".'\n );\n }\n if (\n input.policy.timeReference !==\n \"first-opening-boundary-phase\"\n ) {\n throw new InvalidConfigurationError(\n 'policy.timeReference must be \"first-opening-boundary-phase\".'\n );\n }\n if (\n !Array.isArray(\n input.temporalSamples\n ) ||\n input.temporalSamples.length === 0\n ) {\n throw new InvalidConfigurationError(\n \"temporalSamples must be a non-empty array.\"\n );\n }\n\n input.temporalSamples.forEach((entry, index) => {\n const path = \"temporalSamples[\" + index + \"]\";\n requireInputObject(entry, path);\n const sampleSet = entry.sampleSet;\n requireInputObject(sampleSet, path + \".sampleSet\");\n requireInputObject(sampleSet.sourceContext, path + \".sampleSet.sourceContext\");\n requireInputObject(sampleSet.sourceContext.temporal, path + \".sampleSet.sourceContext.temporal\");\n requireInputObject(sampleSet.captureGeometry, path + \".sampleSet.captureGeometry\");\n requireInputObject(sampleSet.captureGeometry.orientedCapture, path + \".sampleSet.captureGeometry.orientedCapture\");\n requireInputObject(sampleSet.captureGeometry.activeCapture, path + \".sampleSet.captureGeometry.activeCapture\");\n });\n const firstSampleSet =\n input.temporalSamples[0]!\n .sampleSet;\n const firstContext =\n firstSampleSet.sourceContext;\n if (\n firstContext.temporal.kind !==\n \"registered-time-varying\"\n ) {\n throw new InvalidScientificInputError(\n \"Temporal metering requires scene-radiance sample sets with registered-time-varying source context.\"\n );\n }\n\n const expectedContextKey =\n contextKey(firstSampleSet);\n const sceneStateId =\n firstSampleSet.sceneStateId;\n const temporalProfileId =\n firstContext.temporal\n .illuminationTemporalProfileId;\n\n let weightSum = 0;\n let meteredRelativeSignal = 0;\n let previousTime =\n Number.NEGATIVE_INFINITY;\n let minimumTime =\n Number.POSITIVE_INFINITY;\n let maximumTime =\n Number.NEGATIVE_INFINITY;\n\n for (\n let index = 0;\n index <\n input.temporalSamples.length;\n index += 1\n ) {\n const entry =\n input.temporalSamples[index]!;\n const path =\n \"temporalSamples[\" +\n index +\n \"]\";\n const weight =\n requireNormalizedWeight(\n entry.normalizedTimeWeight,\n path +\n \".normalizedTimeWeight\"\n );\n const sampleSet =\n entry.sampleSet;\n const context =\n sampleSet.sourceContext;\n\n if (\n context.temporal.kind !==\n \"registered-time-varying\"\n ) {\n throw new InvalidScientificInputError(\n path +\n \".sampleSet must use registered-time-varying source context.\"\n );\n }\n if (\n context.temporal\n .illuminationTemporalProfileId !==\n temporalProfileId\n ) {\n throw new InvalidScientificInputError(\n \"All temporal metering samples must bind the same illuminationTemporalProfileId.\"\n );\n }\n if (\n sampleSet.sceneStateId !==\n sceneStateId ||\n context.sceneStateId !==\n sceneStateId\n ) {\n throw new InvalidScientificInputError(\n \"All temporal metering samples must bind the same sceneStateId.\"\n );\n }\n if (\n contextKey(sampleSet) !==\n expectedContextKey\n ) {\n throw new InvalidScientificInputError(\n \"All temporal metering samples must share the same scene/provider/material/derivation/capture-geometry context.\"\n );\n }\n\n const captureTime =\n context.temporal\n .captureTimeSecondsFromReference;\n if (\n !Number.isFinite(captureTime)\n ) {\n throw new InvalidScientificInputError(\n path +\n \" capture time must be finite.\"\n );\n }\n if (\n captureTime <= previousTime\n ) {\n throw new InvalidScientificInputError(\n \"Temporal metering capture times must be strictly increasing.\"\n );\n }\n previousTime = captureTime;\n minimumTime = Math.min(\n minimumTime,\n captureTime\n );\n maximumTime = Math.max(\n maximumTime,\n captureTime\n );\n\n const spatialMeter =\n meterRelativeExposure({\n profile,\n sampleSet\n });\n meteredRelativeSignal +=\n spatialMeter.value\n .meteredRelativeSignal *\n weight;\n weightSum += weight;\n }\n\n if (\n !Number.isFinite(weightSum) ||\n Math.abs(weightSum - 1) >\n NORMALIZED_WEIGHT_TOLERANCE\n ) {\n throw new InvalidScientificInputError(\n \"temporalSamples normalizedTimeWeight values must sum to 1.\"\n );\n }\n if (\n !Number.isFinite(\n meteredRelativeSignal\n ) ||\n meteredRelativeSignal < 0\n ) {\n throw new InvalidScientificInputError(\n \"Temporal metered relative signal must remain finite and non-negative.\"\n );\n }\n\n const base = {\n temporalMeasurementId,\n sceneStateId,\n profileId:\n profile.profileId,\n scientificStatus:\n \"approximation\" as const,\n inputDomain:\n \"relative-pre-exposure-linear-signal\" as const,\n captureRegion:\n \"oriented-active-capture\" as const,\n spatialPolicyKind:\n profile.policy.kind,\n temporalPolicyKind:\n \"weighted-time-average\" as const,\n timeReference:\n \"first-opening-boundary-phase\" as const,\n temporalSampleCount:\n input.temporalSamples.length,\n normalizedTimeWeightSum: 1,\n captureTimeRangeSecondsFromReference: {\n minimum: minimumTime,\n maximum: maximumTime\n },\n targetRelativeSignal:\n profile.target\n .targetRelativeSignal,\n meteredRelativeSignal,\n outputCropUsedForMetering:\n false as const,\n exposureCompensationApplied:\n false as const,\n finalToneMappingUsed:\n false as const,\n displayGammaUsed:\n false as const,\n calibratedLuminanceClaimAuthorized:\n false as const,\n calibratedSceneRadianceClaimAuthorized:\n false as const,\n automaticExposureResolved:\n false as const,\n resultReusableForAeLock:\n true as const,\n temporalAveragingExplicit:\n true as const,\n flashTtlMeteringPerformed:\n false as const\n };\n\n const result:\n SceneRadianceTemporalExposureMeteringResult =\n meteredRelativeSignal === 0\n ? {\n ...base,\n status: \"no-signal\",\n requiredExposureScaleResolved:\n false,\n exposureOffsetStopsResolved:\n false\n }\n : {\n ...base,\n status: \"resolved\",\n requiredExposureScaleToTarget:\n profile.target\n .targetRelativeSignal /\n meteredRelativeSignal,\n exposureOffsetStopsToTarget:\n Math.log2(\n profile.target\n .targetRelativeSignal /\n meteredRelativeSignal\n )\n };\n\n return approximationResult(\n result,\n \"scene-radiance-relative-temporal-metering\",\n \"1.0.0\",\n [\n \"Each temporal sample is a renderer/provider-supplied relative pre-exposure reduction bound to the same scene-radiance provider context.\",\n \"Spectral radiance is not converted to calibrated luminance or a camera spectral-meter response by this model.\",\n \"Time averaging is explicit and linear in the declared relative pre-exposure signal domain.\",\n \"Temporal sample weights are caller-declared, positive, normalized to one, and are not inferred from source waveform frequency or sensor readout timing.\",\n \"All temporal samples share one committed sceneStateId and one provider/material/derivation/capture-geometry context.\",\n \"Exposure compensation and automatic aperture/shutter/ISO resolution remain downstream.\",\n \"This ambient meter does not implement flash/TTL metering.\"\n ]\n );\n}\n"]}
@@ -16,39 +16,8 @@ import { approximationResult } from "../core/calculation-result.js";
16
16
  import { InvalidConfigurationError } from "../core/configuration-error.js";
17
17
  import { parseEvidenceList } from "../core/evidence-provenance.js";
18
18
  import { InvalidScientificInputError } from "../core/validation.js";
19
- import { requireInputKeys, requireInputObject, rejectUnknownFields, rejectUnknownVariantFields } from "../core/record-validation.js";
19
+ import { requireInputKeys, requireInputObject, rejectUnknownVariantFields, requireConfigRecord as requireRecord, requireTrimmedConfigString as requireNonEmptyString, requireTrimmedConfigString as requireInputString, requireConfigFinite as requireFinite, requireInputFinite, requireConfigFiniteAboveZero as requirePositiveFinite, parseConfigLimitations as parseLimitations, requireInputFiniteAboveZero as requireInputPositiveFinite, requireInputFiniteAtLeastZero as requireInputNonNegativeFinite } from "../core/record-validation.js";
20
20
  export const EXPOSURE_METERING_PROFILE_SCHEMA_VERSION = "0.1.0";
21
- function requireRecord(value, path, allowedKeys) {
22
- if (typeof value !== "object" ||
23
- value === null ||
24
- Array.isArray(value)) {
25
- throw new InvalidConfigurationError(path + " must be an object.");
26
- }
27
- if (allowedKeys !== null)
28
- rejectUnknownFields(value, allowedKeys, path);
29
- return value;
30
- }
31
- function requireNonEmptyString(value, path) {
32
- if (typeof value !== "string" ||
33
- value.trim().length === 0) {
34
- throw new InvalidConfigurationError(path + " must be a non-empty string.");
35
- }
36
- return value.trim();
37
- }
38
- function requireFinite(value, path) {
39
- if (typeof value !== "number" ||
40
- !Number.isFinite(value)) {
41
- throw new InvalidConfigurationError(path + " must be finite.");
42
- }
43
- return value;
44
- }
45
- function requirePositiveFinite(value, path) {
46
- const parsed = requireFinite(value, path);
47
- if (parsed <= 0) {
48
- throw new InvalidConfigurationError(path + " must be greater than zero.");
49
- }
50
- return parsed;
51
- }
52
21
  function requireFraction(value, path) {
53
22
  const parsed = requireFinite(value, path);
54
23
  if (parsed < 0 || parsed > 1) {
@@ -70,17 +39,6 @@ function parseUv(value, path) {
70
39
  }
71
40
  return { u, v };
72
41
  }
73
- function parseLimitations(value, path) {
74
- if (!Array.isArray(value)) {
75
- throw new InvalidConfigurationError(path + " must be an array.");
76
- }
77
- const limitations = value.map((entry, index) => requireNonEmptyString(entry, path + "[" + index + "]"));
78
- if (new Set(limitations).size !==
79
- limitations.length) {
80
- throw new InvalidConfigurationError(path + " must not contain duplicates.");
81
- }
82
- return limitations;
83
- }
84
42
  function parsePolicy(value, path) {
85
43
  const record = requireRecord(value, path, null);
86
44
  rejectUnknownVariantFields(record, "kind", {
@@ -258,35 +216,6 @@ export function parseExposureMeteringProfile(value) {
258
216
  limitations: parseLimitations(record.limitations, "exposureMeteringProfile.limitations")
259
217
  };
260
218
  }
261
- function requireInputString(value, path) {
262
- if (typeof value !== "string" ||
263
- value.trim().length === 0) {
264
- throw new InvalidConfigurationError(path + " must be a non-empty string.");
265
- }
266
- return value.trim();
267
- }
268
- function requireInputFinite(value, path) {
269
- if (typeof value !== "number" ||
270
- !Number.isFinite(value)) {
271
- throw new InvalidScientificInputError(path + " must be finite.");
272
- }
273
- return value;
274
- }
275
- function requireInputPositiveFinite(value, path) {
276
- const parsed = requireInputFinite(value, path);
277
- if (parsed <= 0) {
278
- throw new InvalidScientificInputError(path + " must be greater than zero.");
279
- }
280
- return parsed;
281
- }
282
- function requireInputNonNegativeFinite(value, path) {
283
- const parsed = requireInputFinite(value, path);
284
- if (parsed < 0) {
285
- throw new InvalidScientificInputError(path +
286
- " must be greater than or equal to zero.");
287
- }
288
- return parsed;
289
- }
290
219
  function validateSampleUv(uv, path) {
291
220
  requireInputObject(uv, path);
292
221
  const u = requireInputFinite(uv.u, path + ".u");
@@ -1 +1 @@
1
- {"version":3,"file":"metering.js","sourceRoot":"","sources":["../../src/exposure/metering.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Generic relative exposure metering.\n *\n * The profile parser reads one metering profile. Schema 0.1.0 is an approximation only. It works on\n * relative linear signal measured before the exposure (pre-exposure). It deliberately does not claim\n * luminance, spectral radiance, sensor-plane irradiance or any manufacturer's meter calibration.\n *\n * The meter works over the actual active capture frame, in its capture orientation. This first\n * contract is an educational, relative approximation. It deliberately ignores the final output crop,\n * display and tone mapping, exposure compensation and the choice of automatic exposure settings.\n * @see docs/USAGE.md for the equations, units, coordinate conventions, known blockers and support\n * limits.\n */\n\nimport {\n approximationResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport {\n parseEvidenceList,\n type EvidenceProvenance\n} from \"../core/evidence-provenance.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport type {\n NormalizedRasterUv,\n ResolvedCaptureGeometry\n} from \"../output/capture-geometry.js\";\nimport { requireInputKeys, requireInputObject, rejectUnknownFields, rejectUnknownVariantFields } from \"../core/record-validation.js\";\n\ntype UnknownRecord = Record<string, unknown>;\n\nexport const EXPOSURE_METERING_PROFILE_SCHEMA_VERSION =\n \"0.1.0\" as const;\n\nexport type ExposureMeteringPolicy =\n | {\n kind: \"multi-zone-uniform\";\n }\n | {\n kind: \"center-weighted-radial\";\n edgeWeight: number;\n exponent: number;\n }\n | {\n kind: \"spot\";\n centerOrientedCaptureUv:\n NormalizedRasterUv;\n radiusFractionOfCaptureDiagonal:\n number;\n }\n | {\n /**\n * Weighted average that emphasises highlights (unchanged since 1.x).\n * Brighter samples get the weight `minimumWeightFraction + (1 -\n * minimumWeightFraction) * (signal / maximum)^exponent`. The weighted mean\n * is then placed at the profile target.\n *\n * This is NOT highlight protection. No sample is placed relative to\n * saturation. In a mostly mid-grey scene with a small bright area, it\n * underexposes the mid-tones compared with uniform metering, yet does not\n * guarantee headroom. Use `highlight-priority` to keep highlights below\n * saturation.\n */\n kind: \"highlight-weighted\";\n minimumWeightFraction: number;\n exponent: number;\n }\n | {\n /**\n * Highlight-priority placement. The area-weighted `highlightPercentile`\n * quantile of the relative pre-exposure signal is placed `headroomStops`\n * below `saturationRelativeSignal`. The profile target must state that\n * placement exactly: `targetRelativeSignal = saturationRelativeSignal *\n * 2^-headroomStops` (see {@link calculateHighlightPriorityPlacementSignal}).\n */\n kind: \"highlight-priority\";\n /** Area-weighted quantile in (0, 1]; 1 places the brightest sample. */\n highlightPercentile: number;\n /** Relative pre-exposure signal at which the declared output saturates. */\n saturationRelativeSignal: number;\n /** Non-negative headroom in stops between the placed highlight and saturation. */\n headroomStops: number;\n };\n\n/** Diagnostics reported only for the highlight-priority policy. */\nexport interface HighlightPriorityMeteringDiagnostics {\n highlightPercentile: number;\n /** Lower area-weighted quantile (inverse empirical CDF) of the samples. */\n percentileRelativeSignal: number;\n saturationRelativeSignal: number;\n headroomStops: number;\n placementRelativeSignal: number;\n quantileDefinition:\n \"area-weighted-lower-quantile-inverse-empirical-cdf\";\n}\n\nexport interface ExposureMeteringProfile {\n schemaVersion:\n typeof EXPOSURE_METERING_PROFILE_SCHEMA_VERSION;\n profileId: string;\n scientificStatus: \"approximation\";\n inputDomain:\n \"relative-pre-exposure-linear-signal\";\n captureRegion:\n \"oriented-active-capture\";\n policy: ExposureMeteringPolicy;\n target: {\n kind: \"relative-signal-reference\";\n targetRelativeSignal: number;\n evidence: readonly EvidenceProvenance[];\n };\n evidence: readonly EvidenceProvenance[];\n limitations: readonly string[];\n}\n\nexport interface ExposureMeteringZoneSample {\n sampleId: string;\n positionOrientedCaptureUv:\n NormalizedRasterUv;\n relativeLinearSignal: number;\n areaWeight: number;\n}\n\nexport interface ExposureMeteringSampleSet {\n measurementId: string;\n sceneStateId: string;\n inputDomain:\n \"relative-pre-exposure-linear-signal\";\n captureRegion:\n \"oriented-active-capture\";\n captureGeometry:\n ResolvedCaptureGeometry;\n processingState: {\n exposureSettingsApplied: false;\n whiteBalanceApplied: false;\n toneMappingApplied: false;\n displayGammaApplied: false;\n sharpeningApplied: false;\n };\n samples:\n readonly ExposureMeteringZoneSample[];\n}\n\ninterface ExposureMeteringResultBase {\n measurementId: string;\n sceneStateId: string;\n profileId: string;\n scientificStatus: \"approximation\";\n inputDomain:\n \"relative-pre-exposure-linear-signal\";\n captureRegion:\n \"oriented-active-capture\";\n orientation:\n ResolvedCaptureGeometry[\"orientedCapture\"][\"orientation\"];\n activeCaptureImagingAreaMm: {\n width: number;\n height: number;\n };\n policyKind:\n ExposureMeteringPolicy[\"kind\"];\n targetRelativeSignal: number;\n meteredRelativeSignal: number;\n totalSampleCount: number;\n selectedSampleCount: number;\n normalizedWeightSum: number;\n outputCropUsedForMetering: false;\n exposureCompensationApplied: false;\n finalToneMappingUsed: false;\n displayGammaUsed: false;\n calibratedLuminanceClaimAuthorized: false;\n calibratedSceneRadianceClaimAuthorized: false;\n automaticExposureResolved: false;\n resultReusableForAeLock: true;\n /** Present only for the highlight-priority policy. */\n highlightPriority?: HighlightPriorityMeteringDiagnostics;\n}\n\nexport type ExposureMeteringResult =\n | (ExposureMeteringResultBase & {\n status: \"resolved\";\n requiredExposureScaleToTarget:\n number;\n exposureOffsetStopsToTarget:\n number;\n })\n | (ExposureMeteringResultBase & {\n status: \"no-signal\";\n requiredExposureScaleResolved:\n false;\n exposureOffsetStopsResolved:\n false;\n });\n\nexport interface MeterRelativeExposureInput {\n profile: ExposureMeteringProfile;\n sampleSet: ExposureMeteringSampleSet;\n}\n\nfunction requireRecord(\n value: unknown,\n path: string,\n allowedKeys: readonly string[] | null\n): UnknownRecord {\n if (\n typeof value !== \"object\" ||\n value === null ||\n Array.isArray(value)\n ) {\n throw new InvalidConfigurationError(\n path + \" must be an object.\"\n );\n }\n if (allowedKeys !== null) rejectUnknownFields(value, allowedKeys, path);\n return value as UnknownRecord;\n}\n\nfunction requireNonEmptyString(\n value: unknown,\n path: string\n): string {\n if (\n typeof value !== \"string\" ||\n value.trim().length === 0\n ) {\n throw new InvalidConfigurationError(\n path + \" must be a non-empty string.\"\n );\n }\n return value.trim();\n}\n\nfunction requireFinite(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isFinite(value)\n ) {\n throw new InvalidConfigurationError(\n path + \" must be finite.\"\n );\n }\n return value;\n}\n\nfunction requirePositiveFinite(\n value: unknown,\n path: string\n): number {\n const parsed = requireFinite(\n value,\n path\n );\n if (parsed <= 0) {\n throw new InvalidConfigurationError(\n path + \" must be greater than zero.\"\n );\n }\n return parsed;\n}\n\nfunction requireFraction(\n value: unknown,\n path: string\n): number {\n const parsed = requireFinite(\n value,\n path\n );\n if (parsed < 0 || parsed > 1) {\n throw new InvalidConfigurationError(\n path +\n \" must be a finite fraction from 0 through 1.\"\n );\n }\n return parsed;\n}\n\nfunction parseUv(\n value: unknown,\n path: string\n): NormalizedRasterUv {\n const record = requireRecord(\n value,\n path, [\"u\", \"v\"]\n );\n const u = requireFinite(\n record.u,\n path + \".u\"\n );\n const v = requireFinite(\n record.v,\n path + \".v\"\n );\n if (\n u < 0 ||\n u > 1 ||\n v < 0 ||\n v > 1\n ) {\n throw new InvalidConfigurationError(\n path +\n \" coordinates must lie within [0, 1].\"\n );\n }\n return { u, v };\n}\n\nfunction parseLimitations(\n value: unknown,\n path: string\n): readonly string[] {\n if (!Array.isArray(value)) {\n throw new InvalidConfigurationError(\n path + \" must be an array.\"\n );\n }\n const limitations = value.map(\n (entry, index) =>\n requireNonEmptyString(\n entry,\n path + \"[\" + index + \"]\"\n )\n );\n if (\n new Set(limitations).size !==\n limitations.length\n ) {\n throw new InvalidConfigurationError(\n path + \" must not contain duplicates.\"\n );\n }\n return limitations;\n}\n\nfunction parsePolicy(\n value: unknown,\n path: string\n): ExposureMeteringPolicy {\n const record = requireRecord(\n value,\n path, null\n );\n rejectUnknownVariantFields(record, \"kind\", {\n \"multi-zone-uniform\": [\"kind\"],\n \"center-weighted-radial\": [\"kind\", \"edgeWeight\", \"exponent\"],\n spot: [\"kind\", \"centerOrientedCaptureUv\", \"radiusFractionOfCaptureDiagonal\"],\n \"highlight-weighted\": [\"kind\", \"minimumWeightFraction\", \"exponent\"],\n \"highlight-priority\": [\"kind\", \"highlightPercentile\", \"saturationRelativeSignal\", \"headroomStops\"]\n }, path);\n\n if (\n record.kind ===\n \"multi-zone-uniform\"\n ) {\n return {\n kind: \"multi-zone-uniform\"\n };\n }\n\n if (\n record.kind ===\n \"center-weighted-radial\"\n ) {\n return {\n kind:\n \"center-weighted-radial\",\n edgeWeight: requireFraction(\n record.edgeWeight,\n path + \".edgeWeight\"\n ),\n exponent:\n requirePositiveFinite(\n record.exponent,\n path + \".exponent\"\n )\n };\n }\n\n if (record.kind === \"spot\") {\n const radius =\n requirePositiveFinite(\n record\n .radiusFractionOfCaptureDiagonal,\n path +\n \".radiusFractionOfCaptureDiagonal\"\n );\n if (radius > 1) {\n throw new InvalidConfigurationError(\n path +\n \".radiusFractionOfCaptureDiagonal must be at most 1.\"\n );\n }\n return {\n kind: \"spot\",\n centerOrientedCaptureUv:\n parseUv(\n record\n .centerOrientedCaptureUv,\n path +\n \".centerOrientedCaptureUv\"\n ),\n radiusFractionOfCaptureDiagonal:\n radius\n };\n }\n\n if (\n record.kind ===\n \"highlight-weighted\"\n ) {\n return {\n kind: \"highlight-weighted\",\n minimumWeightFraction:\n requireFraction(\n record\n .minimumWeightFraction,\n path +\n \".minimumWeightFraction\"\n ),\n exponent:\n requirePositiveFinite(\n record.exponent,\n path + \".exponent\"\n )\n };\n }\n\n if (\n record.kind ===\n \"highlight-priority\"\n ) {\n const percentile =\n requirePositiveFinite(\n record.highlightPercentile,\n path + \".highlightPercentile\"\n );\n if (percentile > 1) {\n throw new InvalidConfigurationError(\n path +\n \".highlightPercentile must lie in (0, 1].\"\n );\n }\n const headroom =\n record.headroomStops;\n if (\n typeof headroom !== \"number\" ||\n !Number.isFinite(headroom) ||\n headroom < 0\n ) {\n throw new InvalidConfigurationError(\n path +\n \".headroomStops must be a finite number greater than or equal to zero.\"\n );\n }\n return {\n kind: \"highlight-priority\",\n highlightPercentile:\n percentile,\n saturationRelativeSignal:\n requirePositiveFinite(\n record.saturationRelativeSignal,\n path +\n \".saturationRelativeSignal\"\n ),\n headroomStops: headroom\n };\n }\n\n throw new InvalidConfigurationError(\n path + \".kind is invalid.\"\n );\n}\n\n/**\n * Relative signal at which a highlight-priority profile places its highlight\n * percentile: `saturationRelativeSignal * 2^-headroomStops`.\n */\nexport function calculateHighlightPriorityPlacementSignal(\n saturationRelativeSignal: number,\n headroomStops: number\n): number {\n if (\n !Number.isFinite(saturationRelativeSignal) ||\n saturationRelativeSignal <= 0 ||\n !Number.isFinite(headroomStops) ||\n headroomStops < 0\n ) {\n throw new InvalidScientificInputError(\n \"Highlight placement requires a positive saturation signal and non-negative headroom.\"\n );\n }\n return saturationRelativeSignal *\n Math.pow(2, -headroomStops);\n}\n\n/**\n * Lower area-weighted quantile (inverse empirical CDF). Sort samples by\n * ascending signal and add up their area weights. The result is the smallest\n * sample signal at which the running weight fraction reaches `percentile`.\n * Ties keep their input order, so the result is deterministic.\n */\nfunction areaWeightedLowerQuantile(\n samples:\n readonly ExposureMeteringZoneSample[],\n percentile: number\n): number {\n const ordered = samples\n .map((sample, index) => ({\n signal:\n sample.relativeLinearSignal,\n weight: sample.areaWeight,\n index\n }))\n .sort((a, b) =>\n a.signal - b.signal ||\n a.index - b.index\n );\n let total = 0;\n for (const entry of ordered) total += entry.weight;\n const threshold =\n percentile * total;\n let cumulative = 0;\n for (const entry of ordered) {\n cumulative += entry.weight;\n // Relative tolerance absorbs summation rounding at exact quantile boundaries.\n if (\n cumulative >=\n threshold * (1 - 1e-12)\n ) {\n return entry.signal;\n }\n }\n return ordered[ordered.length - 1]!\n .signal;\n}\n\n/**\n * Parses one generic relative exposure-metering profile.\n *\n * Schema 0.1.0 is an approximation only. It works on relative linear signal\n * measured before the exposure. It deliberately does not claim luminance,\n * spectral radiance, sensor-plane irradiance or any manufacturer's meter\n * calibration.\n */\nexport function parseExposureMeteringProfile(\n value: unknown\n): ExposureMeteringProfile {\n const record = requireRecord(\n value,\n \"exposureMeteringProfile\", [\"schemaVersion\", \"scientificStatus\", \"inputDomain\", \"captureRegion\", \"target\", \"policy\", \"profileId\", \"evidence\", \"limitations\"]\n );\n\n if (\n record.schemaVersion !==\n EXPOSURE_METERING_PROFILE_SCHEMA_VERSION\n ) {\n throw new InvalidConfigurationError(\n 'exposureMeteringProfile.schemaVersion must be \"' +\n EXPOSURE_METERING_PROFILE_SCHEMA_VERSION +\n '\".'\n );\n }\n if (\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n 'exposureMeteringProfile.scientificStatus must be \"approximation\" in schema 0.1.0.'\n );\n }\n if (\n record.inputDomain !==\n \"relative-pre-exposure-linear-signal\"\n ) {\n throw new InvalidConfigurationError(\n 'exposureMeteringProfile.inputDomain must be \"relative-pre-exposure-linear-signal\".'\n );\n }\n if (\n record.captureRegion !==\n \"oriented-active-capture\"\n ) {\n throw new InvalidConfigurationError(\n 'exposureMeteringProfile.captureRegion must be \"oriented-active-capture\".'\n );\n }\n\n const target = requireRecord(\n record.target,\n \"exposureMeteringProfile.target\", [\"kind\", \"targetRelativeSignal\", \"evidence\"]\n );\n if (\n target.kind !==\n \"relative-signal-reference\"\n ) {\n throw new InvalidConfigurationError(\n 'exposureMeteringProfile.target.kind must be \"relative-signal-reference\".'\n );\n }\n\n const policy = parsePolicy(\n record.policy,\n \"exposureMeteringProfile.policy\"\n );\n const targetRelativeSignal =\n requirePositiveFinite(\n target.targetRelativeSignal,\n \"exposureMeteringProfile.target.targetRelativeSignal\"\n );\n if (policy.kind === \"highlight-priority\") {\n const placement =\n calculateHighlightPriorityPlacementSignal(\n policy.saturationRelativeSignal,\n policy.headroomStops\n );\n if (\n Math.abs(\n targetRelativeSignal -\n placement\n ) >\n 1e-12 * placement\n ) {\n throw new InvalidConfigurationError(\n \"exposureMeteringProfile.target.targetRelativeSignal must equal saturationRelativeSignal * 2^-headroomStops for highlight-priority metering.\"\n );\n }\n }\n\n return {\n schemaVersion:\n EXPOSURE_METERING_PROFILE_SCHEMA_VERSION,\n profileId:\n requireNonEmptyString(\n record.profileId,\n \"exposureMeteringProfile.profileId\"\n ),\n scientificStatus: \"approximation\",\n inputDomain:\n \"relative-pre-exposure-linear-signal\",\n captureRegion:\n \"oriented-active-capture\",\n policy,\n target: {\n kind:\n \"relative-signal-reference\",\n targetRelativeSignal,\n evidence: parseEvidenceList(\n target.evidence,\n \"exposureMeteringProfile.target.evidence\"\n )\n },\n evidence: parseEvidenceList(\n record.evidence,\n \"exposureMeteringProfile.evidence\"\n ),\n limitations: parseLimitations(\n record.limitations,\n \"exposureMeteringProfile.limitations\"\n )\n };\n}\n\nfunction requireInputString(\n value: unknown,\n path: string\n): string {\n if (\n typeof value !== \"string\" ||\n value.trim().length === 0\n ) {\n throw new InvalidConfigurationError(\n path + \" must be a non-empty string.\"\n );\n }\n return value.trim();\n}\n\nfunction requireInputFinite(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isFinite(value)\n ) {\n throw new InvalidScientificInputError(\n path + \" must be finite.\"\n );\n }\n return value;\n}\n\nfunction requireInputPositiveFinite(\n value: unknown,\n path: string\n): number {\n const parsed = requireInputFinite(\n value,\n path\n );\n if (parsed <= 0) {\n throw new InvalidScientificInputError(\n path + \" must be greater than zero.\"\n );\n }\n return parsed;\n}\n\nfunction requireInputNonNegativeFinite(\n value: unknown,\n path: string\n): number {\n const parsed = requireInputFinite(\n value,\n path\n );\n if (parsed < 0) {\n throw new InvalidScientificInputError(\n path +\n \" must be greater than or equal to zero.\"\n );\n }\n return parsed;\n}\n\nfunction validateSampleUv(\n uv: NormalizedRasterUv,\n path: string\n): void {\n requireInputObject(uv, path);\n const u = requireInputFinite(\n uv.u,\n path + \".u\"\n );\n const v = requireInputFinite(\n uv.v,\n path + \".v\"\n );\n if (\n u < 0 ||\n u > 1 ||\n v < 0 ||\n v > 1\n ) {\n throw new InvalidScientificInputError(\n path +\n \" coordinates must lie within [0, 1].\"\n );\n }\n}\n\nfunction validateCaptureGeometry(\n geometry:\n ResolvedCaptureGeometry\n): void {\n requireInputObject(geometry, \"sampleSet.captureGeometry\");\n requireInputObject(geometry.orientedCapture, \"sampleSet.captureGeometry.orientedCapture\");\n requireInputObject(geometry.orientedCapture.imagingArea, \"sampleSet.captureGeometry.orientedCapture.imagingArea\");\n const width =\n requireInputPositiveFinite(\n geometry.orientedCapture\n .imagingArea.widthMm,\n \"sampleSet.captureGeometry.orientedCapture.imagingArea.widthMm\"\n );\n const height =\n requireInputPositiveFinite(\n geometry.orientedCapture\n .imagingArea.heightMm,\n \"sampleSet.captureGeometry.orientedCapture.imagingArea.heightMm\"\n );\n const diagonal = Math.hypot(\n width,\n height\n );\n if (!Number.isFinite(diagonal)) {\n throw new InvalidScientificInputError(\n \"sampleSet.captureGeometry oriented capture diagonal must remain finite.\"\n );\n }\n}\n\nfunction validateProcessingState(\n sampleSet:\n ExposureMeteringSampleSet\n): void {\n const state =\n sampleSet.processingState;\n requireInputObject(state, \"sampleSet.processingState\");\n if (\n state.exposureSettingsApplied !==\n false ||\n state.whiteBalanceApplied !==\n false ||\n state.toneMappingApplied !==\n false ||\n state.displayGammaApplied !==\n false ||\n state.sharpeningApplied !==\n false\n ) {\n throw new InvalidScientificInputError(\n \"Relative exposure metering requires pre-exposure linear samples before exposure settings, white balance, tone mapping, display gamma, and sharpening.\"\n );\n }\n}\n\nfunction physicalNormalizedDistanceFromCenter(\n uv: NormalizedRasterUv,\n widthMm: number,\n heightMm: number\n): number {\n const x =\n (uv.u - 0.5) * widthMm;\n const y =\n (uv.v - 0.5) * heightMm;\n const halfDiagonal =\n Math.hypot(\n widthMm,\n heightMm\n ) / 2;\n return Math.hypot(x, y) /\n halfDiagonal;\n}\n\nfunction physicalDistanceFractionOfDiagonal(\n a: NormalizedRasterUv,\n b: NormalizedRasterUv,\n widthMm: number,\n heightMm: number\n): number {\n const dx =\n (a.u - b.u) * widthMm;\n const dy =\n (a.v - b.v) * heightMm;\n const diagonal =\n Math.hypot(\n widthMm,\n heightMm\n );\n return Math.hypot(dx, dy) /\n diagonal;\n}\n\nfunction policyWeight(\n policy: ExposureMeteringPolicy,\n sample:\n ExposureMeteringZoneSample,\n widthMm: number,\n heightMm: number,\n maximumSignal: number\n): number {\n if (\n policy.kind ===\n \"multi-zone-uniform\"\n ) {\n return 1;\n }\n\n if (\n policy.kind ===\n \"center-weighted-radial\"\n ) {\n const radialPhase = Math.min(\n 1,\n physicalNormalizedDistanceFromCenter(\n sample\n .positionOrientedCaptureUv,\n widthMm,\n heightMm\n )\n );\n return (\n policy.edgeWeight +\n (1 - policy.edgeWeight) *\n Math.pow(\n 1 - radialPhase,\n policy.exponent\n )\n );\n }\n\n if (policy.kind === \"spot\") {\n const distance =\n physicalDistanceFractionOfDiagonal(\n sample\n .positionOrientedCaptureUv,\n policy\n .centerOrientedCaptureUv,\n widthMm,\n heightMm\n );\n return distance <=\n policy\n .radiusFractionOfCaptureDiagonal\n ? 1\n : 0;\n }\n\n if (policy.kind === \"highlight-priority\") {\n // Quantile placement is not a weighted mean; meterRelativeExposure handles it separately.\n return 1;\n }\n if (maximumSignal <= 0) {\n return policy\n .minimumWeightFraction;\n }\n const normalizedHighlight =\n sample.relativeLinearSignal /\n maximumSignal;\n return (\n policy.minimumWeightFraction +\n (1 -\n policy.minimumWeightFraction) *\n Math.pow(\n normalizedHighlight,\n policy.exponent\n )\n );\n}\n\n/**\n * Meters relative pre-exposure linear signal over the actual active capture\n * frame, in its capture orientation.\n *\n * This first contract is an educational, relative approximation. It\n * deliberately ignores the final output crop, display and tone mapping,\n * exposure compensation and the choice of automatic exposure settings.\n */\nexport function meterRelativeExposure(\n input:\n MeterRelativeExposureInput\n): CalculationResult<ExposureMeteringResult> {\n requireInputKeys(input, \"meterRelativeExposure\", [\"profile\", \"sampleSet\"]);\n const profile =\n parseExposureMeteringProfile(\n input.profile\n );\n const sampleSet = input.sampleSet;\n requireInputObject(sampleSet, \"sampleSet\");\n\n requireInputString(\n sampleSet.measurementId,\n \"sampleSet.measurementId\"\n );\n requireInputString(\n sampleSet.sceneStateId,\n \"sampleSet.sceneStateId\"\n );\n\n if (\n sampleSet.inputDomain !==\n profile.inputDomain\n ) {\n throw new InvalidScientificInputError(\n \"sampleSet.inputDomain must match the metering profile.\"\n );\n }\n if (\n sampleSet.captureRegion !==\n profile.captureRegion\n ) {\n throw new InvalidScientificInputError(\n \"sampleSet.captureRegion must match the metering profile.\"\n );\n }\n\n validateCaptureGeometry(\n sampleSet.captureGeometry\n );\n validateProcessingState(\n sampleSet\n );\n\n if (\n !Array.isArray(\n sampleSet.samples\n ) ||\n sampleSet.samples.length === 0\n ) {\n throw new InvalidConfigurationError(\n \"sampleSet.samples must be a non-empty array.\"\n );\n }\n\n const sampleIds: string[] = [];\n let maximumSignal = 0;\n for (\n let index = 0;\n index < sampleSet.samples.length;\n index += 1\n ) {\n const sample =\n sampleSet.samples[index]!;\n const path =\n \"sampleSet.samples[\" +\n index +\n \"]\";\n requireInputObject(sample, path);\n sampleIds.push(\n requireInputString(\n sample.sampleId,\n path + \".sampleId\"\n )\n );\n validateSampleUv(\n sample\n .positionOrientedCaptureUv,\n path +\n \".positionOrientedCaptureUv\"\n );\n const signal =\n requireInputNonNegativeFinite(\n sample.relativeLinearSignal,\n path +\n \".relativeLinearSignal\"\n );\n requireInputPositiveFinite(\n sample.areaWeight,\n path + \".areaWeight\"\n );\n maximumSignal = Math.max(\n maximumSignal,\n signal\n );\n }\n\n if (\n new Set(sampleIds).size !==\n sampleIds.length\n ) {\n throw new InvalidScientificInputError(\n \"sampleSet.samples[].sampleId must not contain duplicates.\"\n );\n }\n\n const widthMm =\n sampleSet.captureGeometry\n .orientedCapture.imagingArea\n .widthMm;\n const heightMm =\n sampleSet.captureGeometry\n .orientedCapture.imagingArea\n .heightMm;\n\n let weightedSignal = 0;\n let weightSum = 0;\n let selectedSampleCount = 0;\n const highlightPolicy =\n profile.policy.kind ===\n \"highlight-priority\"\n ? profile.policy\n : null;\n\n for (const sample of highlightPolicy === null ? sampleSet.samples : []) {\n const policyMultiplier =\n policyWeight(\n profile.policy,\n sample,\n widthMm,\n heightMm,\n maximumSignal\n );\n if (policyMultiplier <= 0) {\n continue;\n }\n const weight =\n sample.areaWeight *\n policyMultiplier;\n weightedSignal +=\n sample.relativeLinearSignal *\n weight;\n weightSum += weight;\n selectedSampleCount += 1;\n }\n\n if (\n !Number.isFinite(\n weightedSignal\n ) ||\n !Number.isFinite(weightSum)\n ) {\n throw new InvalidScientificInputError(\n \"Metering weighted sums must remain finite.\"\n );\n }\n if (highlightPolicy !== null) {\n // Every sample participates in the quantile with its area weight.\n weightSum = 1;\n selectedSampleCount =\n sampleSet.samples.length;\n }\n if (weightSum <= 0) {\n throw new InvalidScientificInputError(\n \"Metering policy selected no samples with positive weight.\"\n );\n }\n\n const meteredRelativeSignal =\n highlightPolicy === null\n ? weightedSignal / weightSum\n : areaWeightedLowerQuantile(\n sampleSet.samples,\n highlightPolicy.highlightPercentile\n );\n if (\n !Number.isFinite(\n meteredRelativeSignal\n ) ||\n meteredRelativeSignal < 0\n ) {\n throw new InvalidScientificInputError(\n \"Metered relative signal must remain finite and non-negative.\"\n );\n }\n\n const base = {\n measurementId:\n sampleSet.measurementId,\n sceneStateId:\n sampleSet.sceneStateId,\n profileId: profile.profileId,\n scientificStatus:\n \"approximation\" as const,\n inputDomain:\n \"relative-pre-exposure-linear-signal\" as const,\n captureRegion:\n \"oriented-active-capture\" as const,\n orientation:\n sampleSet.captureGeometry\n .orientedCapture.orientation,\n activeCaptureImagingAreaMm: {\n width: widthMm,\n height: heightMm\n },\n policyKind:\n profile.policy.kind,\n targetRelativeSignal:\n profile.target\n .targetRelativeSignal,\n meteredRelativeSignal,\n totalSampleCount:\n sampleSet.samples.length,\n selectedSampleCount,\n normalizedWeightSum: 1,\n outputCropUsedForMetering:\n false as const,\n exposureCompensationApplied:\n false as const,\n finalToneMappingUsed:\n false as const,\n displayGammaUsed: false as const,\n calibratedLuminanceClaimAuthorized:\n false as const,\n calibratedSceneRadianceClaimAuthorized:\n false as const,\n automaticExposureResolved:\n false as const,\n resultReusableForAeLock:\n true as const,\n ...(highlightPolicy === null\n ? {}\n : {\n highlightPriority: {\n highlightPercentile:\n highlightPolicy.highlightPercentile,\n percentileRelativeSignal:\n meteredRelativeSignal,\n saturationRelativeSignal:\n highlightPolicy.saturationRelativeSignal,\n headroomStops:\n highlightPolicy.headroomStops,\n placementRelativeSignal:\n profile.target\n .targetRelativeSignal,\n quantileDefinition:\n \"area-weighted-lower-quantile-inverse-empirical-cdf\" as const\n }\n })\n };\n\n const value:\n ExposureMeteringResult =\n meteredRelativeSignal === 0\n ? {\n ...base,\n status: \"no-signal\",\n requiredExposureScaleResolved:\n false,\n exposureOffsetStopsResolved:\n false\n }\n : {\n ...base,\n status: \"resolved\",\n requiredExposureScaleToTarget:\n profile.target\n .targetRelativeSignal /\n meteredRelativeSignal,\n exposureOffsetStopsToTarget:\n Math.log2(\n profile.target\n .targetRelativeSignal /\n meteredRelativeSignal\n )\n };\n\n return approximationResult(\n value,\n \"relative-pre-exposure-metering\",\n \"1.1.0\",\n [\n \"Input samples are relative pre-exposure linear signal, not calibrated luminance, scene spectral radiance, sensor-plane irradiance, or final rendered output.\",\n \"Sample areaWeight is supplied by the producer and is assumed to represent the active capture frame consistently.\",\n \"Metering coordinates are normalized over the physically oriented active capture frame, not the final output crop or CSS/display box.\",\n \"Multi-zone, center-weighted, spot, highlight-weighted and highlight-priority policies are generic explicit approximations and do not claim to reproduce a specific commercial camera.\",\n \"highlight-weighted is a highlight-emphasis weighted average placed at the target; it does not place highlights relative to saturation. highlight-priority places an area-weighted signal quantile at a declared headroom below a declared relative saturation signal.\",\n \"The declared relative target is profile policy, not a universal 18% gray truth.\",\n \"Exposure compensation is not applied by the meter.\",\n \"Automatic aperture, shutter, and ISO resolution remains downstream.\",\n \"White balance, tone mapping, display gamma, sharpening, and final output crop do not participate in this metering calculation.\",\n \"A no-signal result does not fabricate an infinite exposure correction.\"\n ]\n );\n}\n"]}
1
+ {"version":3,"file":"metering.js","sourceRoot":"","sources":["../../src/exposure/metering.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Generic relative exposure metering.\n *\n * The profile parser reads one metering profile. Schema 0.1.0 is an approximation only. It works on\n * relative linear signal measured before the exposure (pre-exposure). It deliberately does not claim\n * luminance, spectral radiance, sensor-plane irradiance or any manufacturer's meter calibration.\n *\n * The meter works over the actual active capture frame, in its capture orientation. This first\n * contract is an educational, relative approximation. It deliberately ignores the final output crop,\n * display and tone mapping, exposure compensation and the choice of automatic exposure settings.\n * @see docs/USAGE.md for the equations, units, coordinate conventions, known blockers and support\n * limits.\n */\n\nimport {\n approximationResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport {\n parseEvidenceList,\n type EvidenceProvenance\n} from \"../core/evidence-provenance.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport type {\n NormalizedRasterUv,\n ResolvedCaptureGeometry\n} from \"../output/capture-geometry.js\";\nimport { requireInputKeys, requireInputObject, rejectUnknownVariantFields, requireConfigRecord as requireRecord, requireTrimmedConfigString as requireNonEmptyString, requireTrimmedConfigString as requireInputString, requireConfigFinite as requireFinite, requireInputFinite, requireConfigFiniteAboveZero as requirePositiveFinite, parseConfigLimitations as parseLimitations, requireInputFiniteAboveZero as requireInputPositiveFinite, requireInputFiniteAtLeastZero as requireInputNonNegativeFinite } from \"../core/record-validation.js\";\n\nexport const EXPOSURE_METERING_PROFILE_SCHEMA_VERSION =\n \"0.1.0\" as const;\n\nexport type ExposureMeteringPolicy =\n | {\n kind: \"multi-zone-uniform\";\n }\n | {\n kind: \"center-weighted-radial\";\n edgeWeight: number;\n exponent: number;\n }\n | {\n kind: \"spot\";\n centerOrientedCaptureUv:\n NormalizedRasterUv;\n radiusFractionOfCaptureDiagonal:\n number;\n }\n | {\n /**\n * Weighted average that emphasises highlights (unchanged since 1.x).\n * Brighter samples get the weight `minimumWeightFraction + (1 -\n * minimumWeightFraction) * (signal / maximum)^exponent`. The weighted mean\n * is then placed at the profile target.\n *\n * This is NOT highlight protection. No sample is placed relative to\n * saturation. In a mostly mid-grey scene with a small bright area, it\n * underexposes the mid-tones compared with uniform metering, yet does not\n * guarantee headroom. Use `highlight-priority` to keep highlights below\n * saturation.\n */\n kind: \"highlight-weighted\";\n minimumWeightFraction: number;\n exponent: number;\n }\n | {\n /**\n * Highlight-priority placement. The area-weighted `highlightPercentile`\n * quantile of the relative pre-exposure signal is placed `headroomStops`\n * below `saturationRelativeSignal`. The profile target must state that\n * placement exactly: `targetRelativeSignal = saturationRelativeSignal *\n * 2^-headroomStops` (see {@link calculateHighlightPriorityPlacementSignal}).\n */\n kind: \"highlight-priority\";\n /** Area-weighted quantile in (0, 1]; 1 places the brightest sample. */\n highlightPercentile: number;\n /** Relative pre-exposure signal at which the declared output saturates. */\n saturationRelativeSignal: number;\n /** Non-negative headroom in stops between the placed highlight and saturation. */\n headroomStops: number;\n };\n\n/** Diagnostics reported only for the highlight-priority policy. */\nexport interface HighlightPriorityMeteringDiagnostics {\n highlightPercentile: number;\n /** Lower area-weighted quantile (inverse empirical CDF) of the samples. */\n percentileRelativeSignal: number;\n saturationRelativeSignal: number;\n headroomStops: number;\n placementRelativeSignal: number;\n quantileDefinition:\n \"area-weighted-lower-quantile-inverse-empirical-cdf\";\n}\n\nexport interface ExposureMeteringProfile {\n schemaVersion:\n typeof EXPOSURE_METERING_PROFILE_SCHEMA_VERSION;\n profileId: string;\n scientificStatus: \"approximation\";\n inputDomain:\n \"relative-pre-exposure-linear-signal\";\n captureRegion:\n \"oriented-active-capture\";\n policy: ExposureMeteringPolicy;\n target: {\n kind: \"relative-signal-reference\";\n targetRelativeSignal: number;\n evidence: readonly EvidenceProvenance[];\n };\n evidence: readonly EvidenceProvenance[];\n limitations: readonly string[];\n}\n\nexport interface ExposureMeteringZoneSample {\n sampleId: string;\n positionOrientedCaptureUv:\n NormalizedRasterUv;\n relativeLinearSignal: number;\n areaWeight: number;\n}\n\nexport interface ExposureMeteringSampleSet {\n measurementId: string;\n sceneStateId: string;\n inputDomain:\n \"relative-pre-exposure-linear-signal\";\n captureRegion:\n \"oriented-active-capture\";\n captureGeometry:\n ResolvedCaptureGeometry;\n processingState: {\n exposureSettingsApplied: false;\n whiteBalanceApplied: false;\n toneMappingApplied: false;\n displayGammaApplied: false;\n sharpeningApplied: false;\n };\n samples:\n readonly ExposureMeteringZoneSample[];\n}\n\ninterface ExposureMeteringResultBase {\n measurementId: string;\n sceneStateId: string;\n profileId: string;\n scientificStatus: \"approximation\";\n inputDomain:\n \"relative-pre-exposure-linear-signal\";\n captureRegion:\n \"oriented-active-capture\";\n orientation:\n ResolvedCaptureGeometry[\"orientedCapture\"][\"orientation\"];\n activeCaptureImagingAreaMm: {\n width: number;\n height: number;\n };\n policyKind:\n ExposureMeteringPolicy[\"kind\"];\n targetRelativeSignal: number;\n meteredRelativeSignal: number;\n totalSampleCount: number;\n selectedSampleCount: number;\n normalizedWeightSum: number;\n outputCropUsedForMetering: false;\n exposureCompensationApplied: false;\n finalToneMappingUsed: false;\n displayGammaUsed: false;\n calibratedLuminanceClaimAuthorized: false;\n calibratedSceneRadianceClaimAuthorized: false;\n automaticExposureResolved: false;\n resultReusableForAeLock: true;\n /** Present only for the highlight-priority policy. */\n highlightPriority?: HighlightPriorityMeteringDiagnostics;\n}\n\nexport type ExposureMeteringResult =\n | (ExposureMeteringResultBase & {\n status: \"resolved\";\n requiredExposureScaleToTarget:\n number;\n exposureOffsetStopsToTarget:\n number;\n })\n | (ExposureMeteringResultBase & {\n status: \"no-signal\";\n requiredExposureScaleResolved:\n false;\n exposureOffsetStopsResolved:\n false;\n });\n\nexport interface MeterRelativeExposureInput {\n profile: ExposureMeteringProfile;\n sampleSet: ExposureMeteringSampleSet;\n}\n\nfunction requireFraction(\n value: unknown,\n path: string\n): number {\n const parsed = requireFinite(\n value,\n path\n );\n if (parsed < 0 || parsed > 1) {\n throw new InvalidConfigurationError(\n path +\n \" must be a finite fraction from 0 through 1.\"\n );\n }\n return parsed;\n}\n\nfunction parseUv(\n value: unknown,\n path: string\n): NormalizedRasterUv {\n const record = requireRecord(\n value,\n path, [\"u\", \"v\"]\n );\n const u = requireFinite(\n record.u,\n path + \".u\"\n );\n const v = requireFinite(\n record.v,\n path + \".v\"\n );\n if (\n u < 0 ||\n u > 1 ||\n v < 0 ||\n v > 1\n ) {\n throw new InvalidConfigurationError(\n path +\n \" coordinates must lie within [0, 1].\"\n );\n }\n return { u, v };\n}\n\nfunction parsePolicy(\n value: unknown,\n path: string\n): ExposureMeteringPolicy {\n const record = requireRecord(\n value,\n path, null\n );\n rejectUnknownVariantFields(record, \"kind\", {\n \"multi-zone-uniform\": [\"kind\"],\n \"center-weighted-radial\": [\"kind\", \"edgeWeight\", \"exponent\"],\n spot: [\"kind\", \"centerOrientedCaptureUv\", \"radiusFractionOfCaptureDiagonal\"],\n \"highlight-weighted\": [\"kind\", \"minimumWeightFraction\", \"exponent\"],\n \"highlight-priority\": [\"kind\", \"highlightPercentile\", \"saturationRelativeSignal\", \"headroomStops\"]\n }, path);\n\n if (\n record.kind ===\n \"multi-zone-uniform\"\n ) {\n return {\n kind: \"multi-zone-uniform\"\n };\n }\n\n if (\n record.kind ===\n \"center-weighted-radial\"\n ) {\n return {\n kind:\n \"center-weighted-radial\",\n edgeWeight: requireFraction(\n record.edgeWeight,\n path + \".edgeWeight\"\n ),\n exponent:\n requirePositiveFinite(\n record.exponent,\n path + \".exponent\"\n )\n };\n }\n\n if (record.kind === \"spot\") {\n const radius =\n requirePositiveFinite(\n record\n .radiusFractionOfCaptureDiagonal,\n path +\n \".radiusFractionOfCaptureDiagonal\"\n );\n if (radius > 1) {\n throw new InvalidConfigurationError(\n path +\n \".radiusFractionOfCaptureDiagonal must be at most 1.\"\n );\n }\n return {\n kind: \"spot\",\n centerOrientedCaptureUv:\n parseUv(\n record\n .centerOrientedCaptureUv,\n path +\n \".centerOrientedCaptureUv\"\n ),\n radiusFractionOfCaptureDiagonal:\n radius\n };\n }\n\n if (\n record.kind ===\n \"highlight-weighted\"\n ) {\n return {\n kind: \"highlight-weighted\",\n minimumWeightFraction:\n requireFraction(\n record\n .minimumWeightFraction,\n path +\n \".minimumWeightFraction\"\n ),\n exponent:\n requirePositiveFinite(\n record.exponent,\n path + \".exponent\"\n )\n };\n }\n\n if (\n record.kind ===\n \"highlight-priority\"\n ) {\n const percentile =\n requirePositiveFinite(\n record.highlightPercentile,\n path + \".highlightPercentile\"\n );\n if (percentile > 1) {\n throw new InvalidConfigurationError(\n path +\n \".highlightPercentile must lie in (0, 1].\"\n );\n }\n const headroom =\n record.headroomStops;\n if (\n typeof headroom !== \"number\" ||\n !Number.isFinite(headroom) ||\n headroom < 0\n ) {\n throw new InvalidConfigurationError(\n path +\n \".headroomStops must be a finite number greater than or equal to zero.\"\n );\n }\n return {\n kind: \"highlight-priority\",\n highlightPercentile:\n percentile,\n saturationRelativeSignal:\n requirePositiveFinite(\n record.saturationRelativeSignal,\n path +\n \".saturationRelativeSignal\"\n ),\n headroomStops: headroom\n };\n }\n\n throw new InvalidConfigurationError(\n path + \".kind is invalid.\"\n );\n}\n\n/**\n * Relative signal at which a highlight-priority profile places its highlight\n * percentile: `saturationRelativeSignal * 2^-headroomStops`.\n */\nexport function calculateHighlightPriorityPlacementSignal(\n saturationRelativeSignal: number,\n headroomStops: number\n): number {\n if (\n !Number.isFinite(saturationRelativeSignal) ||\n saturationRelativeSignal <= 0 ||\n !Number.isFinite(headroomStops) ||\n headroomStops < 0\n ) {\n throw new InvalidScientificInputError(\n \"Highlight placement requires a positive saturation signal and non-negative headroom.\"\n );\n }\n return saturationRelativeSignal *\n Math.pow(2, -headroomStops);\n}\n\n/**\n * Lower area-weighted quantile (inverse empirical CDF). Sort samples by\n * ascending signal and add up their area weights. The result is the smallest\n * sample signal at which the running weight fraction reaches `percentile`.\n * Ties keep their input order, so the result is deterministic.\n */\nfunction areaWeightedLowerQuantile(\n samples:\n readonly ExposureMeteringZoneSample[],\n percentile: number\n): number {\n const ordered = samples\n .map((sample, index) => ({\n signal:\n sample.relativeLinearSignal,\n weight: sample.areaWeight,\n index\n }))\n .sort((a, b) =>\n a.signal - b.signal ||\n a.index - b.index\n );\n let total = 0;\n for (const entry of ordered) total += entry.weight;\n const threshold =\n percentile * total;\n let cumulative = 0;\n for (const entry of ordered) {\n cumulative += entry.weight;\n // Relative tolerance absorbs summation rounding at exact quantile boundaries.\n if (\n cumulative >=\n threshold * (1 - 1e-12)\n ) {\n return entry.signal;\n }\n }\n return ordered[ordered.length - 1]!\n .signal;\n}\n\n/**\n * Parses one generic relative exposure-metering profile.\n *\n * Schema 0.1.0 is an approximation only. It works on relative linear signal\n * measured before the exposure. It deliberately does not claim luminance,\n * spectral radiance, sensor-plane irradiance or any manufacturer's meter\n * calibration.\n */\nexport function parseExposureMeteringProfile(\n value: unknown\n): ExposureMeteringProfile {\n const record = requireRecord(\n value,\n \"exposureMeteringProfile\", [\"schemaVersion\", \"scientificStatus\", \"inputDomain\", \"captureRegion\", \"target\", \"policy\", \"profileId\", \"evidence\", \"limitations\"]\n );\n\n if (\n record.schemaVersion !==\n EXPOSURE_METERING_PROFILE_SCHEMA_VERSION\n ) {\n throw new InvalidConfigurationError(\n 'exposureMeteringProfile.schemaVersion must be \"' +\n EXPOSURE_METERING_PROFILE_SCHEMA_VERSION +\n '\".'\n );\n }\n if (\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n 'exposureMeteringProfile.scientificStatus must be \"approximation\" in schema 0.1.0.'\n );\n }\n if (\n record.inputDomain !==\n \"relative-pre-exposure-linear-signal\"\n ) {\n throw new InvalidConfigurationError(\n 'exposureMeteringProfile.inputDomain must be \"relative-pre-exposure-linear-signal\".'\n );\n }\n if (\n record.captureRegion !==\n \"oriented-active-capture\"\n ) {\n throw new InvalidConfigurationError(\n 'exposureMeteringProfile.captureRegion must be \"oriented-active-capture\".'\n );\n }\n\n const target = requireRecord(\n record.target,\n \"exposureMeteringProfile.target\", [\"kind\", \"targetRelativeSignal\", \"evidence\"]\n );\n if (\n target.kind !==\n \"relative-signal-reference\"\n ) {\n throw new InvalidConfigurationError(\n 'exposureMeteringProfile.target.kind must be \"relative-signal-reference\".'\n );\n }\n\n const policy = parsePolicy(\n record.policy,\n \"exposureMeteringProfile.policy\"\n );\n const targetRelativeSignal =\n requirePositiveFinite(\n target.targetRelativeSignal,\n \"exposureMeteringProfile.target.targetRelativeSignal\"\n );\n if (policy.kind === \"highlight-priority\") {\n const placement =\n calculateHighlightPriorityPlacementSignal(\n policy.saturationRelativeSignal,\n policy.headroomStops\n );\n if (\n Math.abs(\n targetRelativeSignal -\n placement\n ) >\n 1e-12 * placement\n ) {\n throw new InvalidConfigurationError(\n \"exposureMeteringProfile.target.targetRelativeSignal must equal saturationRelativeSignal * 2^-headroomStops for highlight-priority metering.\"\n );\n }\n }\n\n return {\n schemaVersion:\n EXPOSURE_METERING_PROFILE_SCHEMA_VERSION,\n profileId:\n requireNonEmptyString(\n record.profileId,\n \"exposureMeteringProfile.profileId\"\n ),\n scientificStatus: \"approximation\",\n inputDomain:\n \"relative-pre-exposure-linear-signal\",\n captureRegion:\n \"oriented-active-capture\",\n policy,\n target: {\n kind:\n \"relative-signal-reference\",\n targetRelativeSignal,\n evidence: parseEvidenceList(\n target.evidence,\n \"exposureMeteringProfile.target.evidence\"\n )\n },\n evidence: parseEvidenceList(\n record.evidence,\n \"exposureMeteringProfile.evidence\"\n ),\n limitations: parseLimitations(\n record.limitations,\n \"exposureMeteringProfile.limitations\"\n )\n };\n}\n\nfunction validateSampleUv(\n uv: NormalizedRasterUv,\n path: string\n): void {\n requireInputObject(uv, path);\n const u = requireInputFinite(\n uv.u,\n path + \".u\"\n );\n const v = requireInputFinite(\n uv.v,\n path + \".v\"\n );\n if (\n u < 0 ||\n u > 1 ||\n v < 0 ||\n v > 1\n ) {\n throw new InvalidScientificInputError(\n path +\n \" coordinates must lie within [0, 1].\"\n );\n }\n}\n\nfunction validateCaptureGeometry(\n geometry:\n ResolvedCaptureGeometry\n): void {\n requireInputObject(geometry, \"sampleSet.captureGeometry\");\n requireInputObject(geometry.orientedCapture, \"sampleSet.captureGeometry.orientedCapture\");\n requireInputObject(geometry.orientedCapture.imagingArea, \"sampleSet.captureGeometry.orientedCapture.imagingArea\");\n const width =\n requireInputPositiveFinite(\n geometry.orientedCapture\n .imagingArea.widthMm,\n \"sampleSet.captureGeometry.orientedCapture.imagingArea.widthMm\"\n );\n const height =\n requireInputPositiveFinite(\n geometry.orientedCapture\n .imagingArea.heightMm,\n \"sampleSet.captureGeometry.orientedCapture.imagingArea.heightMm\"\n );\n const diagonal = Math.hypot(\n width,\n height\n );\n if (!Number.isFinite(diagonal)) {\n throw new InvalidScientificInputError(\n \"sampleSet.captureGeometry oriented capture diagonal must remain finite.\"\n );\n }\n}\n\nfunction validateProcessingState(\n sampleSet:\n ExposureMeteringSampleSet\n): void {\n const state =\n sampleSet.processingState;\n requireInputObject(state, \"sampleSet.processingState\");\n if (\n state.exposureSettingsApplied !==\n false ||\n state.whiteBalanceApplied !==\n false ||\n state.toneMappingApplied !==\n false ||\n state.displayGammaApplied !==\n false ||\n state.sharpeningApplied !==\n false\n ) {\n throw new InvalidScientificInputError(\n \"Relative exposure metering requires pre-exposure linear samples before exposure settings, white balance, tone mapping, display gamma, and sharpening.\"\n );\n }\n}\n\nfunction physicalNormalizedDistanceFromCenter(\n uv: NormalizedRasterUv,\n widthMm: number,\n heightMm: number\n): number {\n const x =\n (uv.u - 0.5) * widthMm;\n const y =\n (uv.v - 0.5) * heightMm;\n const halfDiagonal =\n Math.hypot(\n widthMm,\n heightMm\n ) / 2;\n return Math.hypot(x, y) /\n halfDiagonal;\n}\n\nfunction physicalDistanceFractionOfDiagonal(\n a: NormalizedRasterUv,\n b: NormalizedRasterUv,\n widthMm: number,\n heightMm: number\n): number {\n const dx =\n (a.u - b.u) * widthMm;\n const dy =\n (a.v - b.v) * heightMm;\n const diagonal =\n Math.hypot(\n widthMm,\n heightMm\n );\n return Math.hypot(dx, dy) /\n diagonal;\n}\n\nfunction policyWeight(\n policy: ExposureMeteringPolicy,\n sample:\n ExposureMeteringZoneSample,\n widthMm: number,\n heightMm: number,\n maximumSignal: number\n): number {\n if (\n policy.kind ===\n \"multi-zone-uniform\"\n ) {\n return 1;\n }\n\n if (\n policy.kind ===\n \"center-weighted-radial\"\n ) {\n const radialPhase = Math.min(\n 1,\n physicalNormalizedDistanceFromCenter(\n sample\n .positionOrientedCaptureUv,\n widthMm,\n heightMm\n )\n );\n return (\n policy.edgeWeight +\n (1 - policy.edgeWeight) *\n Math.pow(\n 1 - radialPhase,\n policy.exponent\n )\n );\n }\n\n if (policy.kind === \"spot\") {\n const distance =\n physicalDistanceFractionOfDiagonal(\n sample\n .positionOrientedCaptureUv,\n policy\n .centerOrientedCaptureUv,\n widthMm,\n heightMm\n );\n return distance <=\n policy\n .radiusFractionOfCaptureDiagonal\n ? 1\n : 0;\n }\n\n if (policy.kind === \"highlight-priority\") {\n // Quantile placement is not a weighted mean; meterRelativeExposure handles it separately.\n return 1;\n }\n if (maximumSignal <= 0) {\n return policy\n .minimumWeightFraction;\n }\n const normalizedHighlight =\n sample.relativeLinearSignal /\n maximumSignal;\n return (\n policy.minimumWeightFraction +\n (1 -\n policy.minimumWeightFraction) *\n Math.pow(\n normalizedHighlight,\n policy.exponent\n )\n );\n}\n\n/**\n * Meters relative pre-exposure linear signal over the actual active capture\n * frame, in its capture orientation.\n *\n * This first contract is an educational, relative approximation. It\n * deliberately ignores the final output crop, display and tone mapping,\n * exposure compensation and the choice of automatic exposure settings.\n */\nexport function meterRelativeExposure(\n input:\n MeterRelativeExposureInput\n): CalculationResult<ExposureMeteringResult> {\n requireInputKeys(input, \"meterRelativeExposure\", [\"profile\", \"sampleSet\"]);\n const profile =\n parseExposureMeteringProfile(\n input.profile\n );\n const sampleSet = input.sampleSet;\n requireInputObject(sampleSet, \"sampleSet\");\n\n requireInputString(\n sampleSet.measurementId,\n \"sampleSet.measurementId\"\n );\n requireInputString(\n sampleSet.sceneStateId,\n \"sampleSet.sceneStateId\"\n );\n\n if (\n sampleSet.inputDomain !==\n profile.inputDomain\n ) {\n throw new InvalidScientificInputError(\n \"sampleSet.inputDomain must match the metering profile.\"\n );\n }\n if (\n sampleSet.captureRegion !==\n profile.captureRegion\n ) {\n throw new InvalidScientificInputError(\n \"sampleSet.captureRegion must match the metering profile.\"\n );\n }\n\n validateCaptureGeometry(\n sampleSet.captureGeometry\n );\n validateProcessingState(\n sampleSet\n );\n\n if (\n !Array.isArray(\n sampleSet.samples\n ) ||\n sampleSet.samples.length === 0\n ) {\n throw new InvalidConfigurationError(\n \"sampleSet.samples must be a non-empty array.\"\n );\n }\n\n const sampleIds: string[] = [];\n let maximumSignal = 0;\n for (\n let index = 0;\n index < sampleSet.samples.length;\n index += 1\n ) {\n const sample =\n sampleSet.samples[index]!;\n const path =\n \"sampleSet.samples[\" +\n index +\n \"]\";\n requireInputObject(sample, path);\n sampleIds.push(\n requireInputString(\n sample.sampleId,\n path + \".sampleId\"\n )\n );\n validateSampleUv(\n sample\n .positionOrientedCaptureUv,\n path +\n \".positionOrientedCaptureUv\"\n );\n const signal =\n requireInputNonNegativeFinite(\n sample.relativeLinearSignal,\n path +\n \".relativeLinearSignal\"\n );\n requireInputPositiveFinite(\n sample.areaWeight,\n path + \".areaWeight\"\n );\n maximumSignal = Math.max(\n maximumSignal,\n signal\n );\n }\n\n if (\n new Set(sampleIds).size !==\n sampleIds.length\n ) {\n throw new InvalidScientificInputError(\n \"sampleSet.samples[].sampleId must not contain duplicates.\"\n );\n }\n\n const widthMm =\n sampleSet.captureGeometry\n .orientedCapture.imagingArea\n .widthMm;\n const heightMm =\n sampleSet.captureGeometry\n .orientedCapture.imagingArea\n .heightMm;\n\n let weightedSignal = 0;\n let weightSum = 0;\n let selectedSampleCount = 0;\n const highlightPolicy =\n profile.policy.kind ===\n \"highlight-priority\"\n ? profile.policy\n : null;\n\n for (const sample of highlightPolicy === null ? sampleSet.samples : []) {\n const policyMultiplier =\n policyWeight(\n profile.policy,\n sample,\n widthMm,\n heightMm,\n maximumSignal\n );\n if (policyMultiplier <= 0) {\n continue;\n }\n const weight =\n sample.areaWeight *\n policyMultiplier;\n weightedSignal +=\n sample.relativeLinearSignal *\n weight;\n weightSum += weight;\n selectedSampleCount += 1;\n }\n\n if (\n !Number.isFinite(\n weightedSignal\n ) ||\n !Number.isFinite(weightSum)\n ) {\n throw new InvalidScientificInputError(\n \"Metering weighted sums must remain finite.\"\n );\n }\n if (highlightPolicy !== null) {\n // Every sample participates in the quantile with its area weight.\n weightSum = 1;\n selectedSampleCount =\n sampleSet.samples.length;\n }\n if (weightSum <= 0) {\n throw new InvalidScientificInputError(\n \"Metering policy selected no samples with positive weight.\"\n );\n }\n\n const meteredRelativeSignal =\n highlightPolicy === null\n ? weightedSignal / weightSum\n : areaWeightedLowerQuantile(\n sampleSet.samples,\n highlightPolicy.highlightPercentile\n );\n if (\n !Number.isFinite(\n meteredRelativeSignal\n ) ||\n meteredRelativeSignal < 0\n ) {\n throw new InvalidScientificInputError(\n \"Metered relative signal must remain finite and non-negative.\"\n );\n }\n\n const base = {\n measurementId:\n sampleSet.measurementId,\n sceneStateId:\n sampleSet.sceneStateId,\n profileId: profile.profileId,\n scientificStatus:\n \"approximation\" as const,\n inputDomain:\n \"relative-pre-exposure-linear-signal\" as const,\n captureRegion:\n \"oriented-active-capture\" as const,\n orientation:\n sampleSet.captureGeometry\n .orientedCapture.orientation,\n activeCaptureImagingAreaMm: {\n width: widthMm,\n height: heightMm\n },\n policyKind:\n profile.policy.kind,\n targetRelativeSignal:\n profile.target\n .targetRelativeSignal,\n meteredRelativeSignal,\n totalSampleCount:\n sampleSet.samples.length,\n selectedSampleCount,\n normalizedWeightSum: 1,\n outputCropUsedForMetering:\n false as const,\n exposureCompensationApplied:\n false as const,\n finalToneMappingUsed:\n false as const,\n displayGammaUsed: false as const,\n calibratedLuminanceClaimAuthorized:\n false as const,\n calibratedSceneRadianceClaimAuthorized:\n false as const,\n automaticExposureResolved:\n false as const,\n resultReusableForAeLock:\n true as const,\n ...(highlightPolicy === null\n ? {}\n : {\n highlightPriority: {\n highlightPercentile:\n highlightPolicy.highlightPercentile,\n percentileRelativeSignal:\n meteredRelativeSignal,\n saturationRelativeSignal:\n highlightPolicy.saturationRelativeSignal,\n headroomStops:\n highlightPolicy.headroomStops,\n placementRelativeSignal:\n profile.target\n .targetRelativeSignal,\n quantileDefinition:\n \"area-weighted-lower-quantile-inverse-empirical-cdf\" as const\n }\n })\n };\n\n const value:\n ExposureMeteringResult =\n meteredRelativeSignal === 0\n ? {\n ...base,\n status: \"no-signal\",\n requiredExposureScaleResolved:\n false,\n exposureOffsetStopsResolved:\n false\n }\n : {\n ...base,\n status: \"resolved\",\n requiredExposureScaleToTarget:\n profile.target\n .targetRelativeSignal /\n meteredRelativeSignal,\n exposureOffsetStopsToTarget:\n Math.log2(\n profile.target\n .targetRelativeSignal /\n meteredRelativeSignal\n )\n };\n\n return approximationResult(\n value,\n \"relative-pre-exposure-metering\",\n \"1.1.0\",\n [\n \"Input samples are relative pre-exposure linear signal, not calibrated luminance, scene spectral radiance, sensor-plane irradiance, or final rendered output.\",\n \"Sample areaWeight is supplied by the producer and is assumed to represent the active capture frame consistently.\",\n \"Metering coordinates are normalized over the physically oriented active capture frame, not the final output crop or CSS/display box.\",\n \"Multi-zone, center-weighted, spot, highlight-weighted and highlight-priority policies are generic explicit approximations and do not claim to reproduce a specific commercial camera.\",\n \"highlight-weighted is a highlight-emphasis weighted average placed at the target; it does not place highlights relative to saturation. highlight-priority places an area-weighted signal quantile at a declared headroom below a declared relative saturation signal.\",\n \"The declared relative target is profile policy, not a universal 18% gray truth.\",\n \"Exposure compensation is not applied by the meter.\",\n \"Automatic aperture, shutter, and ISO resolution remains downstream.\",\n \"White balance, tone mapping, display gamma, sharpening, and final output crop do not participate in this metering calculation.\",\n \"A no-signal result does not fabricate an infinite exposure correction.\"\n ]\n );\n}\n"]}
@@ -10,54 +10,10 @@
10
10
  * @see docs/USAGE.md for the equations, units, coordinate conventions, known blockers and support
11
11
  * limits.
12
12
  */
13
- import { rejectUnknownFields } from "../core/record-validation.js";
14
13
  import { InvalidConfigurationError } from "../core/configuration-error.js";
15
14
  import { parseEvidenceList } from "../core/evidence-provenance.js";
15
+ import { requireConfigRecord as requireRecord, requireTrimmedConfigString as requireNonEmptyString, requireConfigFinite as requireFinite, parseConfigLimitations as parseLimitations, requireConfigFiniteAboveZero as requirePositiveFinite } from "../core/record-validation.js";
16
16
  export const EXPOSURE_PROGRAM_LINE_SCHEMA_VERSION = "0.1.0";
17
- function requireRecord(value, path, allowedKeys) {
18
- if (typeof value !== "object" ||
19
- value === null ||
20
- Array.isArray(value)) {
21
- throw new InvalidConfigurationError(path + " must be an object.");
22
- }
23
- if (allowedKeys !== null)
24
- rejectUnknownFields(value, allowedKeys, path);
25
- return value;
26
- }
27
- function requireNonEmptyString(value, path) {
28
- if (typeof value !== "string" ||
29
- value.trim().length === 0) {
30
- throw new InvalidConfigurationError(path + " must be a non-empty string.");
31
- }
32
- return value.trim();
33
- }
34
- function requireFinite(value, path) {
35
- if (typeof value !== "number" ||
36
- !Number.isFinite(value)) {
37
- throw new InvalidConfigurationError(path + " must be finite.");
38
- }
39
- return value;
40
- }
41
- function requirePositiveFinite(value, path) {
42
- const parsed = requireFinite(value, path);
43
- if (parsed <= 0) {
44
- throw new InvalidConfigurationError(path +
45
- " must be greater than zero.");
46
- }
47
- return parsed;
48
- }
49
- function parseLimitations(value, path) {
50
- if (!Array.isArray(value)) {
51
- throw new InvalidConfigurationError(path + " must be an array.");
52
- }
53
- const parsed = value.map((entry, index) => requireNonEmptyString(entry, path + "[" + index + "]"));
54
- if (new Set(parsed).size !==
55
- parsed.length) {
56
- throw new InvalidConfigurationError(path +
57
- " must not contain duplicates.");
58
- }
59
- return parsed;
60
- }
61
17
  /**
62
18
  * Parses a generic educational/product exposure program line.
63
19
  *