@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
@@ -60,6 +60,11 @@ export interface TabulatedNativeRawInput {
60
60
  /** Called once per native tile-equivalent (progress), as the environment task reads its tiles. */
61
61
  onTile?: () => void;
62
62
  tileWidth: number;
63
+ /**
64
+ * #568: the largest relative difference the verification accepts (default `TABULATED_RELATIVE_TOLERANCE`): a fast method that
65
+ * approximates the chain (`lens.catalogue`'s field-grid PSF) passes its own measured bound.
66
+ */
67
+ relativeTolerance?: number;
63
68
  yieldControl(): Promise<void>;
64
69
  checkAbort(): void;
65
70
  }
@@ -78,7 +78,7 @@ export async function renderTabulatedNativeRaw(t) {
78
78
  t.electrons.window(x, y, x + 1, y + 1);
79
79
  const chain = photo.expectedGeneratedElectronCount, tabulated = testHook?.tabulatedElectrons ? testHook.tabulatedElectrons(t.electrons.site(x, y), x, y) : t.electrons.site(x, y);
80
80
  const difference = chain === tabulated ? 0 : Math.abs(tabulated - chain) / Math.max(Math.abs(chain), Number.MIN_VALUE);
81
- if (!(difference <= TABULATED_RELATIVE_TOLERANCE))
81
+ if (!(difference <= (t.relativeTolerance ?? TABULATED_RELATIVE_TOLERANCE)))
82
82
  return undefined;
83
83
  worst = Math.max(worst, difference);
84
84
  readouts[(y & 1) * 2 + (x & 1)] = readout;
@@ -1 +1 @@
1
- {"version":3,"file":"native-environment-tabulated.js","sourceRoot":"","sources":["../../src/capture/native-environment-tabulated.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Internal (#394 step 2, not exported from the package): the native RAW of an engine-built planar scene (a chart preset or a\n * caller material scene, still camera, rectilinear, no distortion, vignetting, pupil rays or caller PSF) from tabulated\n * expected electrons instead of the per-site environment chain.\n *\n * Each site's expected photo electrons come from the display view's tabulated expected signal (`createDisplayViewExpectedSignal`):\n * the same sum over pixel-aperture nodes, anti-aliasing spots, diffraction taps and spectral nodes as the environment chain,\n * formed in another order, so the value differs from the chain's only in the last bits. The sensor stage is the native one:\n * dark charge, both Poisson draws, read noise and the ADC with the site's own seeds (`createVerifiedNativeSiteReadout`). A\n * site whose expected electrons differ in the last bit can therefore, rarely, draw a different integer; the statistics are\n * unchanged.\n *\n * Verification at run time: one site of each CFA phase runs the full chain (planning, the evaluator's callbacks, composition,\n * parsing and `calculateNativeRawSite`). Its readout template must reproduce that site bit for bit, and its tabulated\n * electrons must agree with the chain's within `TABULATED_RELATIVE_TOLERANCE`; otherwise the caller runs the environment task.\n * #453: the chain's photo signal comes from `executeEngineOwnedEnvironmentPhotoSignal`, the same operations and values as\n * `executeEnvironmentSensorPhotoSignal` for these engine-built records, without re-validating records that cannot have changed.\n */\n\nimport type { EnvironmentRadianceEvaluator } from \"../sensor/environment-photo-signal.js\";\nimport { executeEngineOwnedEnvironmentPhotoSignal, executeEnvironmentSensorPhotoSignal } from \"../sensor/environment-photo-signal.js\";\nimport { isInternalSceneRadianceEvaluator } from \"../core/internal-radiance-evaluator.js\";\nimport { calculateCaptureExposureWindows, prepareCaptureExposureWindowSampler } from \"../sensor/exposure-window.js\";\nimport { createEnvironmentRawFrameCanonicalMemo, planEnvironmentRawSite, type SimulateEnvironmentSensorRawFrameInput } from \"./environment-raw-producer.js\";\nimport { admitNativeEnvironmentInput, type NativeEnvironmentRawInput } from \"./native-environment-raw.js\";\nimport { composeNativeEnvironmentSensorSite } from \"./native-environment-optical-stage.js\";\nimport { calculateNativeRawSite, parseNativeRawProducerSite } from \"./sensor-raw-producer.js\";\nimport { calculateSiteEffectiveSaturationCode, createPhaseWhiteLevelAccumulator } from \"./raw-white-level.js\";\nimport { createVerifiedNativeSiteReadout, type NativeSiteReadout } from \"./native-environment-lean.js\";\nimport type { NativeRawOutput } from \"./native-raw.js\";\n\n/** Largest relative difference between a verification site's tabulated and chain electrons: rounding only (a few ulps of each term). */\nexport const TABULATED_RELATIVE_TOLERANCE = 1e-9;\n/** Rows per expected-signal cache window. */\nconst BAND_ROWS = 32;\n/** Host-yield budget in milliseconds (the native task's cadence); the task also yields after its last row. */\nconst YIELD_MS = 8;\n\n/** Internal counters (tests assert how many verification sites ran the chain per call; four, one per CFA phase). */\nexport const tabulatedVerificationStatistics = { chainExecutions: 0 };\n\n/**\n * Internal test hook (never set by the engine): `fullPhoto` also runs each verification site through\n * `executeEnvironmentSensorPhotoSignal` (with the uncounted evaluator) and hands both photo signals to the test;\n * `tabulatedElectrons` replaces the tabulated electrons the gate compares, to show that a wrong fast path is rejected.\n */\nexport interface TabulatedVerificationTestHook {\n fullPhoto?(engineOwned: unknown, full: unknown): void;\n tabulatedElectrons?(value: number, x: number, y: number): number;\n}\nlet testHook: TabulatedVerificationTestHook | undefined;\nexport function setTabulatedVerificationTestHook(hook: TabulatedVerificationTestHook | undefined): void { testHook = hook; }\n\nlet tabulatedDisabled = false;\n/** Disables the tabulated path process-wide (internal; tests of the environment path itself turn it off). */\nexport function setTabulatedNativeRawEnabled(enabled: boolean): void { tabulatedDisabled = !enabled; }\n\nexport interface TabulatedNativeRawInput {\n input: NativeEnvironmentRawInput;\n /** Native rectangle to render (region rendering); absent: the whole raster. */\n region?: { x: number; y: number; width: number; height: number } | undefined;\n /** The engine-built site record of a native index (the facade's `siteInput`). */\n siteInput(index: number): SimulateEnvironmentSensorRawFrameInput[\"sites\"][number];\n /** The evaluator the environment task would use (for the verification sites). */\n evaluateRadiance: EnvironmentRadianceEvaluator;\n /** Expected photo electrons of native sites (`createDisplayViewExpectedSignal`). */\n electrons: { window(x0: number, y0: number, x1: number, y1: number): void; site(x: number, y: number): number; readonly lookups: number };\n /** Called once per native tile-equivalent (progress), as the environment task reads its tiles. */\n onTile?: () => void;\n tileWidth: number;\n yieldControl(): Promise<void>;\n checkAbort(): void;\n}\n\nexport interface TabulatedNativeRawResult {\n raw: NativeRawOutput;\n /** Evaluator callbacks of the verification sites plus the tabulated signal's scene lookups. */\n providerEvaluationCount: number;\n /** Largest relative difference between tabulated and chain electrons over the verification sites. */\n verificationRelativeDifference: number;\n}\n\n/**\n * #449: the local exposure duration shared by every site of `region`, or undefined when the sampler must run per site. With\n * simultaneous opening and closing boundaries both boundary phases are 0 at every point (exposure-window.ts, boundaryPhase), so\n * each site's duration is the nominal one the sampler gives at the region's first site. The sampler's only other per-point step\n * is its check that the point lies in the active capture rectangle; the region's first and last site centres bound every other\n * centre, so when both pass every site passes. When either throws, the per-site path runs (and throws where it did).\n */\nexport function simultaneousExposureDuration(schedule: { opening: unknown; closing: unknown }, sampler: (point: { x: number; y: number }) => { localExposureDurationSeconds: number },\n region: { x: number; y: number; width: number; height: number }): number | undefined {\n if ((schedule.opening as { kind?: unknown }).kind !== \"simultaneous\" || (schedule.closing as { kind?: unknown }).kind !== \"simultaneous\") return undefined;\n try {\n const first = sampler({ x: region.x + 0.5, y: region.y + 0.5 }).localExposureDurationSeconds;\n const last = sampler({ x: region.x + region.width - 0.5, y: region.y + region.height - 0.5 }).localExposureDurationSeconds;\n return Object.is(first, last) ? first : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Renders the native RAW from tabulated expected electrons, or returns undefined when verification fails (see the module comment). */\nexport async function renderTabulatedNativeRaw(t: TabulatedNativeRawInput): Promise<TabulatedNativeRawResult | undefined> {\n if (tabulatedDisabled) return undefined;\n const { plan, sceneBinding } = admitNativeEnvironmentInput(t.input);\n const native = plan.exposure.geometry.nativeRaster, W = native.pixelWidth, layout = plan.frame.colorSamplingProfile.layout;\n if (layout.kind !== \"periodic-mosaic\" || layout.repeatWidthSites !== 2 || layout.repeatHeightSites !== 2 || !plan.exposureWindow) return undefined;\n const region = t.region ?? { x: 0, y: 0, width: W, height: native.pixelHeight };\n if (region.width < 2 || region.height < 2) return undefined;\n const seed = plan.exposure.noise.seedUint32, schedule = plan.exposureWindow, memo = createEnvironmentRawFrameCanonicalMemo(plan.frame, schedule);\n let evaluations = 0;\n const evaluate: EnvironmentRadianceEvaluator = (request) => { evaluations++; return t.evaluateRadiance(request); };\n // The counting wrapper returns the evaluator's own results, so they are engine-built exactly when the evaluator is.\n const engineBuiltResults = isInternalSceneRadianceEvaluator(t.evaluateRadiance);\n // Verification: the region's top-left 2 × 2 block, one site per CFA phase, through the full chain.\n const readouts: NativeSiteReadout[] = [];\n let worst = 0;\n for (let k = 0; k < 4; k++) {\n const x = region.x + (k & 1), y = region.y + (k >> 1), index = y * W + x, site = t.siteInput(index);\n const full = planEnvironmentRawSite(site, index, plan.frame, sceneBinding, schedule, \"engine-owned\", memo, \"photo-only\");\n // #453: the chain for these engine-built site records (the same operations and values as executeEnvironmentSensorPhotoSignal,\n // without re-validating records that have not changed; see executeEngineOwnedEnvironmentPhotoSignal).\n const photoResult = executeEngineOwnedEnvironmentPhotoSignal(full, evaluate, engineBuiltResults), photo = photoResult.value.photoSignal;\n tabulatedVerificationStatistics.chainExecutions++;\n if (testHook?.fullPhoto) testHook.fullPhoto(photoResult, executeEnvironmentSensorPhotoSignal(full, t.evaluateRadiance, undefined, \"photo-only\").value.photo);\n const timing = calculateCaptureExposureWindows({ ...schedule, nativeRaster: native, samplePointsNative: [{ x: x + 0.5, y: y + 0.5 }] }).value.samples[0];\n const parsed = parseNativeRawProducerSite(composeNativeEnvironmentSensorSite(site, photo), index, plan.frame, timing);\n const result = calculateNativeRawSite(parsed, seed, index);\n const whiteCode = calculateSiteEffectiveSaturationCode(result.readout.value, parsed.readoutProfile, parsed.regimeId);\n const readout = createVerifiedNativeSiteReadout(parsed, result, whiteCode, photo, x, y, index, seed);\n if (!readout) return undefined;\n t.electrons.window(x, y, x + 1, y + 1);\n const chain = photo.expectedGeneratedElectronCount, tabulated = testHook?.tabulatedElectrons ? testHook.tabulatedElectrons(t.electrons.site(x, y), x, y) : t.electrons.site(x, y);\n const difference = chain === tabulated ? 0 : Math.abs(tabulated - chain) / Math.max(Math.abs(chain), Number.MIN_VALUE);\n if (!(difference <= TABULATED_RELATIVE_TOLERANCE)) return undefined;\n worst = Math.max(worst, difference);\n readouts[(y & 1) * 2 + (x & 1)] = readout;\n }\n const length = region.width * region.height;\n const raw: NativeRawOutput = { plan, codes: new Uint16Array(length), blackLevels: new Uint16Array(length), digitalSaturationCodes: new Uint16Array(length),\n saturationFlags: new Uint8Array(length), effectiveSaturationCodes: new Uint16Array(4) };\n const white = createPhaseWhiteLevelAccumulator(W, 2, 2), sampler = prepareCaptureExposureWindowSampler({ ...schedule, nativeRaster: native });\n const tilesPerRow = Math.ceil(region.width / t.tileWidth), point = { x: 0, y: 0 };\n const constantDuration = simultaneousExposureDuration(schedule, sampler, region);\n let last = Date.now();\n for (let y0 = region.y; y0 < region.y + region.height; y0 += BAND_ROWS) {\n const y1 = Math.min(region.y + region.height, y0 + BAND_ROWS);\n t.electrons.window(region.x, y0, region.x + region.width, y1);\n for (let y = y0; y < y1; y++) {\n for (let x = region.x; x < region.x + region.width; x++) {\n const index = y * W + x, at = (y - region.y) * region.width + x - region.x;\n let duration = constantDuration;\n if (duration === undefined) { point.x = x + 0.5; point.y = y + 0.5; duration = sampler(point).localExposureDurationSeconds; }\n const o = readouts[(y & 1) * 2 + (x & 1)]!.compute(t.electrons.site(x, y), duration, x, y, index, seed);\n // A check of the full path failed for this site: let the environment task decide (it throws its own error).\n if (!o) return undefined;\n raw.codes[at] = o.rawCode; raw.blackLevels[at] = o.blackLevelCode; raw.digitalSaturationCodes[at] = o.digitalSaturationCode; raw.saturationFlags[at] = o.flags;\n white.record(index, o.whiteCode);\n }\n for (let k = 0; k < tilesPerRow; k++) t.onTile?.();\n if (y === region.y + region.height - 1 || Date.now() - last >= YIELD_MS) { await t.yieldControl(); t.checkAbort(); last = Date.now(); }\n }\n }\n raw.effectiveSaturationCodes.set(t.region ? white.partial() : white.finish());\n return { raw, providerEvaluationCount: evaluations + t.electrons.lookups, verificationRelativeDifference: worst };\n}\n"]}
1
+ {"version":3,"file":"native-environment-tabulated.js","sourceRoot":"","sources":["../../src/capture/native-environment-tabulated.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Internal (#394 step 2, not exported from the package): the native RAW of an engine-built planar scene (a chart preset or a\n * caller material scene, still camera, rectilinear, no distortion, vignetting, pupil rays or caller PSF) from tabulated\n * expected electrons instead of the per-site environment chain.\n *\n * Each site's expected photo electrons come from the display view's tabulated expected signal (`createDisplayViewExpectedSignal`):\n * the same sum over pixel-aperture nodes, anti-aliasing spots, diffraction taps and spectral nodes as the environment chain,\n * formed in another order, so the value differs from the chain's only in the last bits. The sensor stage is the native one:\n * dark charge, both Poisson draws, read noise and the ADC with the site's own seeds (`createVerifiedNativeSiteReadout`). A\n * site whose expected electrons differ in the last bit can therefore, rarely, draw a different integer; the statistics are\n * unchanged.\n *\n * Verification at run time: one site of each CFA phase runs the full chain (planning, the evaluator's callbacks, composition,\n * parsing and `calculateNativeRawSite`). Its readout template must reproduce that site bit for bit, and its tabulated\n * electrons must agree with the chain's within `TABULATED_RELATIVE_TOLERANCE`; otherwise the caller runs the environment task.\n * #453: the chain's photo signal comes from `executeEngineOwnedEnvironmentPhotoSignal`, the same operations and values as\n * `executeEnvironmentSensorPhotoSignal` for these engine-built records, without re-validating records that cannot have changed.\n */\n\nimport type { EnvironmentRadianceEvaluator } from \"../sensor/environment-photo-signal.js\";\nimport { executeEngineOwnedEnvironmentPhotoSignal, executeEnvironmentSensorPhotoSignal } from \"../sensor/environment-photo-signal.js\";\nimport { isInternalSceneRadianceEvaluator } from \"../core/internal-radiance-evaluator.js\";\nimport { calculateCaptureExposureWindows, prepareCaptureExposureWindowSampler } from \"../sensor/exposure-window.js\";\nimport { createEnvironmentRawFrameCanonicalMemo, planEnvironmentRawSite, type SimulateEnvironmentSensorRawFrameInput } from \"./environment-raw-producer.js\";\nimport { admitNativeEnvironmentInput, type NativeEnvironmentRawInput } from \"./native-environment-raw.js\";\nimport { composeNativeEnvironmentSensorSite } from \"./native-environment-optical-stage.js\";\nimport { calculateNativeRawSite, parseNativeRawProducerSite } from \"./sensor-raw-producer.js\";\nimport { calculateSiteEffectiveSaturationCode, createPhaseWhiteLevelAccumulator } from \"./raw-white-level.js\";\nimport { createVerifiedNativeSiteReadout, type NativeSiteReadout } from \"./native-environment-lean.js\";\nimport type { NativeRawOutput } from \"./native-raw.js\";\n\n/** Largest relative difference between a verification site's tabulated and chain electrons: rounding only (a few ulps of each term). */\nexport const TABULATED_RELATIVE_TOLERANCE = 1e-9;\n/** Rows per expected-signal cache window. */\nconst BAND_ROWS = 32;\n/** Host-yield budget in milliseconds (the native task's cadence); the task also yields after its last row. */\nconst YIELD_MS = 8;\n\n/** Internal counters (tests assert how many verification sites ran the chain per call; four, one per CFA phase). */\nexport const tabulatedVerificationStatistics = { chainExecutions: 0 };\n\n/**\n * Internal test hook (never set by the engine): `fullPhoto` also runs each verification site through\n * `executeEnvironmentSensorPhotoSignal` (with the uncounted evaluator) and hands both photo signals to the test;\n * `tabulatedElectrons` replaces the tabulated electrons the gate compares, to show that a wrong fast path is rejected.\n */\nexport interface TabulatedVerificationTestHook {\n fullPhoto?(engineOwned: unknown, full: unknown): void;\n tabulatedElectrons?(value: number, x: number, y: number): number;\n}\nlet testHook: TabulatedVerificationTestHook | undefined;\nexport function setTabulatedVerificationTestHook(hook: TabulatedVerificationTestHook | undefined): void { testHook = hook; }\n\nlet tabulatedDisabled = false;\n/** Disables the tabulated path process-wide (internal; tests of the environment path itself turn it off). */\nexport function setTabulatedNativeRawEnabled(enabled: boolean): void { tabulatedDisabled = !enabled; }\n\nexport interface TabulatedNativeRawInput {\n input: NativeEnvironmentRawInput;\n /** Native rectangle to render (region rendering); absent: the whole raster. */\n region?: { x: number; y: number; width: number; height: number } | undefined;\n /** The engine-built site record of a native index (the facade's `siteInput`). */\n siteInput(index: number): SimulateEnvironmentSensorRawFrameInput[\"sites\"][number];\n /** The evaluator the environment task would use (for the verification sites). */\n evaluateRadiance: EnvironmentRadianceEvaluator;\n /** Expected photo electrons of native sites (`createDisplayViewExpectedSignal`). */\n electrons: { window(x0: number, y0: number, x1: number, y1: number): void; site(x: number, y: number): number; readonly lookups: number };\n /** Called once per native tile-equivalent (progress), as the environment task reads its tiles. */\n onTile?: () => void;\n tileWidth: number;\n /**\n * #568: the largest relative difference the verification accepts (default `TABULATED_RELATIVE_TOLERANCE`): a fast method that\n * approximates the chain (`lens.catalogue`'s field-grid PSF) passes its own measured bound.\n */\n relativeTolerance?: number;\n yieldControl(): Promise<void>;\n checkAbort(): void;\n}\n\nexport interface TabulatedNativeRawResult {\n raw: NativeRawOutput;\n /** Evaluator callbacks of the verification sites plus the tabulated signal's scene lookups. */\n providerEvaluationCount: number;\n /** Largest relative difference between tabulated and chain electrons over the verification sites. */\n verificationRelativeDifference: number;\n}\n\n/**\n * #449: the local exposure duration shared by every site of `region`, or undefined when the sampler must run per site. With\n * simultaneous opening and closing boundaries both boundary phases are 0 at every point (exposure-window.ts, boundaryPhase), so\n * each site's duration is the nominal one the sampler gives at the region's first site. The sampler's only other per-point step\n * is its check that the point lies in the active capture rectangle; the region's first and last site centres bound every other\n * centre, so when both pass every site passes. When either throws, the per-site path runs (and throws where it did).\n */\nexport function simultaneousExposureDuration(schedule: { opening: unknown; closing: unknown }, sampler: (point: { x: number; y: number }) => { localExposureDurationSeconds: number },\n region: { x: number; y: number; width: number; height: number }): number | undefined {\n if ((schedule.opening as { kind?: unknown }).kind !== \"simultaneous\" || (schedule.closing as { kind?: unknown }).kind !== \"simultaneous\") return undefined;\n try {\n const first = sampler({ x: region.x + 0.5, y: region.y + 0.5 }).localExposureDurationSeconds;\n const last = sampler({ x: region.x + region.width - 0.5, y: region.y + region.height - 0.5 }).localExposureDurationSeconds;\n return Object.is(first, last) ? first : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Renders the native RAW from tabulated expected electrons, or returns undefined when verification fails (see the module comment). */\nexport async function renderTabulatedNativeRaw(t: TabulatedNativeRawInput): Promise<TabulatedNativeRawResult | undefined> {\n if (tabulatedDisabled) return undefined;\n const { plan, sceneBinding } = admitNativeEnvironmentInput(t.input);\n const native = plan.exposure.geometry.nativeRaster, W = native.pixelWidth, layout = plan.frame.colorSamplingProfile.layout;\n if (layout.kind !== \"periodic-mosaic\" || layout.repeatWidthSites !== 2 || layout.repeatHeightSites !== 2 || !plan.exposureWindow) return undefined;\n const region = t.region ?? { x: 0, y: 0, width: W, height: native.pixelHeight };\n if (region.width < 2 || region.height < 2) return undefined;\n const seed = plan.exposure.noise.seedUint32, schedule = plan.exposureWindow, memo = createEnvironmentRawFrameCanonicalMemo(plan.frame, schedule);\n let evaluations = 0;\n const evaluate: EnvironmentRadianceEvaluator = (request) => { evaluations++; return t.evaluateRadiance(request); };\n // The counting wrapper returns the evaluator's own results, so they are engine-built exactly when the evaluator is.\n const engineBuiltResults = isInternalSceneRadianceEvaluator(t.evaluateRadiance);\n // Verification: the region's top-left 2 × 2 block, one site per CFA phase, through the full chain.\n const readouts: NativeSiteReadout[] = [];\n let worst = 0;\n for (let k = 0; k < 4; k++) {\n const x = region.x + (k & 1), y = region.y + (k >> 1), index = y * W + x, site = t.siteInput(index);\n const full = planEnvironmentRawSite(site, index, plan.frame, sceneBinding, schedule, \"engine-owned\", memo, \"photo-only\");\n // #453: the chain for these engine-built site records (the same operations and values as executeEnvironmentSensorPhotoSignal,\n // without re-validating records that have not changed; see executeEngineOwnedEnvironmentPhotoSignal).\n const photoResult = executeEngineOwnedEnvironmentPhotoSignal(full, evaluate, engineBuiltResults), photo = photoResult.value.photoSignal;\n tabulatedVerificationStatistics.chainExecutions++;\n if (testHook?.fullPhoto) testHook.fullPhoto(photoResult, executeEnvironmentSensorPhotoSignal(full, t.evaluateRadiance, undefined, \"photo-only\").value.photo);\n const timing = calculateCaptureExposureWindows({ ...schedule, nativeRaster: native, samplePointsNative: [{ x: x + 0.5, y: y + 0.5 }] }).value.samples[0];\n const parsed = parseNativeRawProducerSite(composeNativeEnvironmentSensorSite(site, photo), index, plan.frame, timing);\n const result = calculateNativeRawSite(parsed, seed, index);\n const whiteCode = calculateSiteEffectiveSaturationCode(result.readout.value, parsed.readoutProfile, parsed.regimeId);\n const readout = createVerifiedNativeSiteReadout(parsed, result, whiteCode, photo, x, y, index, seed);\n if (!readout) return undefined;\n t.electrons.window(x, y, x + 1, y + 1);\n const chain = photo.expectedGeneratedElectronCount, tabulated = testHook?.tabulatedElectrons ? testHook.tabulatedElectrons(t.electrons.site(x, y), x, y) : t.electrons.site(x, y);\n const difference = chain === tabulated ? 0 : Math.abs(tabulated - chain) / Math.max(Math.abs(chain), Number.MIN_VALUE);\n if (!(difference <= (t.relativeTolerance ?? TABULATED_RELATIVE_TOLERANCE))) return undefined;\n worst = Math.max(worst, difference);\n readouts[(y & 1) * 2 + (x & 1)] = readout;\n }\n const length = region.width * region.height;\n const raw: NativeRawOutput = { plan, codes: new Uint16Array(length), blackLevels: new Uint16Array(length), digitalSaturationCodes: new Uint16Array(length),\n saturationFlags: new Uint8Array(length), effectiveSaturationCodes: new Uint16Array(4) };\n const white = createPhaseWhiteLevelAccumulator(W, 2, 2), sampler = prepareCaptureExposureWindowSampler({ ...schedule, nativeRaster: native });\n const tilesPerRow = Math.ceil(region.width / t.tileWidth), point = { x: 0, y: 0 };\n const constantDuration = simultaneousExposureDuration(schedule, sampler, region);\n let last = Date.now();\n for (let y0 = region.y; y0 < region.y + region.height; y0 += BAND_ROWS) {\n const y1 = Math.min(region.y + region.height, y0 + BAND_ROWS);\n t.electrons.window(region.x, y0, region.x + region.width, y1);\n for (let y = y0; y < y1; y++) {\n for (let x = region.x; x < region.x + region.width; x++) {\n const index = y * W + x, at = (y - region.y) * region.width + x - region.x;\n let duration = constantDuration;\n if (duration === undefined) { point.x = x + 0.5; point.y = y + 0.5; duration = sampler(point).localExposureDurationSeconds; }\n const o = readouts[(y & 1) * 2 + (x & 1)]!.compute(t.electrons.site(x, y), duration, x, y, index, seed);\n // A check of the full path failed for this site: let the environment task decide (it throws its own error).\n if (!o) return undefined;\n raw.codes[at] = o.rawCode; raw.blackLevels[at] = o.blackLevelCode; raw.digitalSaturationCodes[at] = o.digitalSaturationCode; raw.saturationFlags[at] = o.flags;\n white.record(index, o.whiteCode);\n }\n for (let k = 0; k < tilesPerRow; k++) t.onTile?.();\n if (y === region.y + region.height - 1 || Date.now() - last >= YIELD_MS) { await t.yieldControl(); t.checkAbort(); last = Date.now(); }\n }\n }\n raw.effectiveSaturationCodes.set(t.region ? white.partial() : white.finish());\n return { raw, providerEvaluationCount: evaluations + t.electrons.lookups, verificationRelativeDifference: worst };\n}\n"]}
@@ -11,39 +11,9 @@
11
11
  import { InvalidScientificInputError } from "../core/validation.js";
12
12
  import { parseGenericReleaseCapabilityProfile } from "../equipment/release-capabilities.js";
13
13
  import { parseFocusPlane } from "../optics/focus-state.js";
14
- import { requireInputKeys } from "../core/record-validation.js";
15
- import { InvalidConfigurationError } from "../core/configuration-error.js";
14
+ import { requireInputKeys, requireTrimmedConfigString as requireNonEmptyString, requireInputFinite as requireFinite, requireInputFiniteAboveZero as requirePositiveFinite, requireInputFiniteAtLeastZero as requireNonNegativeFinite } from "../core/record-validation.js";
15
+ import { deepFreeze, cloneJson } from "../core/shared-helpers.js";
16
16
  export const RELEASE_SEQUENCE_VERSION = "0.1.0";
17
- function requireNonEmptyString(value, path) {
18
- if (typeof value !== "string" ||
19
- value.trim().length === 0) {
20
- throw new InvalidConfigurationError(path + " must be a non-empty string.");
21
- }
22
- return value.trim();
23
- }
24
- function requireFinite(value, path) {
25
- if (typeof value !== "number" ||
26
- !Number.isFinite(value)) {
27
- throw new InvalidScientificInputError(path + " must be finite.");
28
- }
29
- return value;
30
- }
31
- function requireNonNegativeFinite(value, path) {
32
- const parsed = requireFinite(value, path);
33
- if (parsed < 0) {
34
- throw new InvalidScientificInputError(path +
35
- " must be greater than or equal to zero.");
36
- }
37
- return parsed;
38
- }
39
- function requirePositiveFinite(value, path) {
40
- const parsed = requireFinite(value, path);
41
- if (parsed <= 0) {
42
- throw new InvalidScientificInputError(path +
43
- " must be greater than zero.");
44
- }
45
- return parsed;
46
- }
47
17
  function requirePositiveSafeInteger(value, path) {
48
18
  if (!Number.isSafeInteger(value) ||
49
19
  value <= 0) {
@@ -61,21 +31,6 @@ function requireUint32(value, path) {
61
31
  }
62
32
  return value;
63
33
  }
64
- function cloneJson(value) {
65
- return JSON.parse(JSON.stringify(value));
66
- }
67
- function deepFreeze(value) {
68
- if (typeof value !== "object" ||
69
- value === null ||
70
- Object.isFrozen(value)) {
71
- return value;
72
- }
73
- Object.freeze(value);
74
- for (const child of Object.values(value)) {
75
- deepFreeze(child);
76
- }
77
- return value;
78
- }
79
34
  function hashFrameSeed(sequenceSeedUint32, frameIndex) {
80
35
  let hash = 0x811c9dc5;
81
36
  const text = sequenceSeedUint32.toString(10) +
@@ -1 +1 @@
1
- {"version":3,"file":"release-sequence.js","sourceRoot":"","sources":["../../src/capture/release-sequence.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Shutter-release sequences for stills (single, burst, self-timer, bracketing), scheduled as logical exposures, and\n * cancelling a sequence part-way.\n *\n * This only schedules exposures; how the sensor captures each one is handled elsewhere. Render speed\n * or frame rate is never used as capture timing.\n * @see docs/SIMULATED_CAPTURE.md for equations, coordinate/unit conventions, blockers and support\n * limits.\n */\n\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport {\n parseGenericReleaseCapabilityProfile,\n type GenericExposureBracketAxis,\n type GenericReleaseCapabilityProfile\n} from \"../equipment/release-capabilities.js\";\nimport type {\n ResolvedGenericEquipmentExposureCapabilities,\n ResolvedNumericSettingGrid\n} from \"../equipment/exposure-capabilities.js\";\nimport {\n parseFocusPlane,\n type FocusPlane\n} from \"../optics/focus-state.js\";\nimport { requireInputKeys } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\n\nexport const RELEASE_SEQUENCE_VERSION =\n \"0.1.0\" as const;\n\nexport type ReleaseAutomationState =\n | \"manual\"\n | \"locked\"\n | \"continuous\";\n\nexport interface ReleaseBaseCaptureState {\n exposure: {\n aperture: number;\n shutterSeconds: number;\n iso: number;\n };\n focus: FocusPlane;\n whiteBalanceStateId?: string;\n automation: {\n ae: ReleaseAutomationState;\n af: ReleaseAutomationState;\n awb: ReleaseAutomationState;\n };\n}\n\nexport type ReleaseDrivePolicy =\n | {\n kind: \"single\";\n }\n | {\n kind: \"burst\";\n frameCount: number;\n }\n | {\n kind: \"self-timer\";\n delaySeconds: number;\n frameCount: number;\n };\n\nexport type ReleaseBracketPolicy =\n | {\n kind: \"none\";\n }\n | {\n kind: \"exposure\";\n axis:\n GenericExposureBracketAxis;\n offsetsStops:\n readonly number[];\n }\n | {\n kind: \"focus\";\n focusStates:\n readonly FocusPlane[];\n };\n\nexport interface ResolveReleaseSequenceInput {\n sequenceId: string;\n releaseRequestTimeSeconds: number;\n sequenceSeedUint32: number;\n drive: ReleaseDrivePolicy;\n bracket: ReleaseBracketPolicy;\n requestedCadenceFps?: number;\n baseState:\n ReleaseBaseCaptureState;\n releaseCapabilities:\n GenericReleaseCapabilityProfile;\n exposureCapabilities:\n ResolvedGenericEquipmentExposureCapabilities;\n}\n\nexport type ReleaseTimingConstraint =\n | \"requested-cadence\"\n | \"body-maximum-cadence\"\n | \"exposure-duration\"\n | \"minimum-inter-frame-gap\";\n\nexport interface ResolvedReleaseFrame {\n sequenceId: string;\n frameIndex: number;\n releaseFrameId: string;\n exposureStartTimeSeconds: number;\n exposureEndTimeSeconds: number;\n sceneTimeSecondsFromSequenceStart:\n number;\n startIntervalFromPreviousSeconds:\n number | null;\n timingConstraints:\n readonly ReleaseTimingConstraint[];\n stochasticSeedUint32: number;\n exposure: {\n aperture: number;\n shutterSeconds: number;\n iso: number;\n };\n focus: FocusPlane;\n exposureBracketOffsetStops?:\n number;\n automation:\n ReleaseBaseCaptureState[\"automation\"];\n whiteBalanceStateId?: string;\n}\n\nexport interface ResolvedReleaseSequence {\n version:\n typeof RELEASE_SEQUENCE_VERSION;\n sequenceId: string;\n status: \"scheduled\";\n releaseRequestTimeSeconds: number;\n selfTimerDelaySeconds: number;\n requestedCadenceFps:\n number | null;\n capabilityMaximumCadenceFps:\n number;\n minimumInterFrameGapSeconds:\n number;\n requestedFrameCount: number;\n actualFrameCount: number;\n sequenceSeedUint32: number;\n baseStateSnapshot:\n ReleaseBaseCaptureState;\n frames:\n readonly ResolvedReleaseFrame[];\n sensorCaptureModeOwnership:\n \"separate-logical-capture-contract\";\n whiteBalanceBracketingPhysicalCaptures:\n false;\n preReleaseCaptureModeled:\n false;\n bufferMediaThermalSlowdownModeled:\n false;\n}\n\nexport interface CancelReleaseSequenceInput {\n sequence:\n ResolvedReleaseSequence;\n completedFrameCount: number;\n cancelledAtSeconds: number;\n}\n\nexport interface CancelledReleaseSequence {\n version:\n typeof RELEASE_SEQUENCE_VERSION;\n sequenceId: string;\n status: \"cancelled\";\n cancelledAtSeconds: number;\n completedFrameCount: number;\n omittedFrameCount: number;\n completedFrames:\n readonly ResolvedReleaseFrame[];\n sourceSequenceFrameCount: number;\n sourceSequenceUnmodified: true;\n}\n\nfunction requireNonEmptyString(\n value: string,\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: number,\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 requireNonNegativeFinite(\n value: number,\n path: string\n): number {\n const parsed =\n requireFinite(value, path);\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 requirePositiveFinite(\n value: number,\n path: string\n): number {\n const parsed =\n requireFinite(value, path);\n if (parsed <= 0) {\n throw new InvalidScientificInputError(\n path +\n \" must be greater than zero.\"\n );\n }\n return parsed;\n}\n\nfunction requirePositiveSafeInteger(\n value: number,\n path: string\n): number {\n if (\n !Number.isSafeInteger(value) ||\n value <= 0\n ) {\n throw new InvalidScientificInputError(\n path +\n \" must be a positive safe integer.\"\n );\n }\n return value;\n}\n\nfunction requireUint32(\n value: number,\n path: string\n): number {\n if (\n !Number.isSafeInteger(value) ||\n value < 0 ||\n value > 0xffff_ffff\n ) {\n throw new InvalidScientificInputError(\n path +\n \" must be an unsigned 32-bit integer.\"\n );\n }\n return value;\n}\n\nfunction cloneJson<T>(value: T): T {\n return JSON.parse(\n JSON.stringify(value)\n ) as T;\n}\n\nfunction deepFreeze<T>(value: T): T {\n if (\n typeof value !== \"object\" ||\n value === null ||\n Object.isFrozen(value)\n ) {\n return value;\n }\n\n Object.freeze(value);\n for (\n const child of\n Object.values(\n value as Record<string, unknown>\n )\n ) {\n deepFreeze(child);\n }\n return value;\n}\n\nfunction hashFrameSeed(\n sequenceSeedUint32: number,\n frameIndex: number\n): number {\n let hash = 0x811c9dc5;\n const text =\n sequenceSeedUint32.toString(10) +\n \":\" +\n frameIndex.toString(10);\n for (\n let index = 0;\n index < text.length;\n index += 1\n ) {\n hash ^= text.charCodeAt(index);\n hash = Math.imul(\n hash,\n 0x01000193\n ) >>> 0;\n }\n return hash >>> 0;\n}\n\nfunction includesDriveMode(\n capabilities:\n GenericReleaseCapabilityProfile,\n drive:\n ReleaseDrivePolicy[\"kind\"]\n): boolean {\n return capabilities\n .supportedDriveModes\n .value.includes(drive);\n}\n\nfunction driveFrameCount(\n drive: ReleaseDrivePolicy\n): number {\n if (drive.kind === \"single\") {\n return 1;\n }\n return requirePositiveSafeInteger(\n drive.frameCount,\n \"drive.frameCount\"\n );\n}\n\nfunction bracketFrameCount(\n bracket:\n ReleaseBracketPolicy\n): number | null {\n if (bracket.kind === \"none\") {\n return null;\n }\n const count =\n bracket.kind === \"exposure\"\n ? bracket.offsetsStops.length\n : bracket.focusStates.length;\n if (count <= 0) {\n throw new InvalidScientificInputError(\n \"Bracket sequences must contain at least one frame.\"\n );\n }\n return count;\n}\n\nfunction resolveFrameCount(\n drive: ReleaseDrivePolicy,\n bracket:\n ReleaseBracketPolicy\n): number {\n const driveCount =\n driveFrameCount(drive);\n const bracketCount =\n bracketFrameCount(bracket);\n\n if (bracketCount === null) {\n return driveCount;\n }\n\n if (drive.kind === \"burst\") {\n throw new InvalidScientificInputError(\n \"Schema 0.1.0 does not combine burst drive with bracketing; choose one multi-frame policy explicitly.\"\n );\n }\n\n if (\n drive.kind === \"self-timer\" &&\n driveCount !== bracketCount\n ) {\n throw new InvalidScientificInputError(\n \"Self-timer frameCount must match the bracket frame count.\"\n );\n }\n\n return bracketCount;\n}\n\nfunction validateGridSetting(\n value: number,\n minimum: number,\n maximum: number,\n grid:\n ResolvedNumericSettingGrid,\n path: string\n): void {\n if (\n !Number.isFinite(value) ||\n value < minimum ||\n value > maximum\n ) {\n throw new InvalidScientificInputError(\n path +\n \" lies outside the resolved equipment capability range.\"\n );\n }\n\n if (\n grid.kind ===\n \"discrete-values\" &&\n !grid.values.some(\n (candidate) =>\n Math.abs(\n candidate - value\n ) <=\n Math.max(\n 1,\n Math.abs(value)\n ) *\n 1e-12\n )\n ) {\n throw new InvalidScientificInputError(\n path +\n \" is not present in the resolved equipment setting grid.\"\n );\n }\n}\n\nfunction validateExposure(\n exposure:\n ReleaseBaseCaptureState[\"exposure\"],\n capabilities:\n ResolvedGenericEquipmentExposureCapabilities,\n path: string\n): void {\n validateGridSetting(\n exposure.aperture,\n capabilities.aperture\n .widestAvailableFNumber,\n capabilities.aperture\n .narrowestAvailableFNumber,\n capabilities.aperture\n .settingGrid,\n path + \".aperture\"\n );\n validateGridSetting(\n exposure.shutterSeconds,\n capabilities.shutter\n .minimumSeconds,\n capabilities.shutter\n .maximumSeconds,\n capabilities.shutter\n .settingGrid,\n path + \".shutterSeconds\"\n );\n validateGridSetting(\n exposure.iso,\n capabilities.iso.minimum,\n capabilities.iso.maximum,\n capabilities.iso.settingGrid,\n path + \".iso\"\n );\n}\n\nfunction resolveExposureBracket(\n base:\n ReleaseBaseCaptureState[\"exposure\"],\n axis:\n GenericExposureBracketAxis,\n offsetStops: number\n): ReleaseBaseCaptureState[\"exposure\"] {\n requireFinite(\n offsetStops,\n \"bracket.offsetsStops[]\"\n );\n\n if (axis === \"shutter\") {\n return {\n ...base,\n shutterSeconds:\n base.shutterSeconds *\n Math.pow(2, offsetStops)\n };\n }\n if (axis === \"iso\") {\n return {\n ...base,\n iso:\n base.iso *\n Math.pow(2, offsetStops)\n };\n }\n return {\n ...base,\n aperture:\n base.aperture /\n Math.pow(\n 2,\n offsetStops / 2\n )\n };\n}\n\nfunction resolveFrameState(\n input:\n ResolveReleaseSequenceInput,\n frameIndex: number\n): {\n exposure:\n ReleaseBaseCaptureState[\"exposure\"];\n focus: FocusPlane;\n exposureBracketOffsetStops?:\n number;\n} {\n if (input.bracket.kind === \"none\") {\n return {\n exposure:\n cloneJson(\n input.baseState.exposure\n ),\n focus:\n parseFocusPlane(\n input.baseState.focus\n )\n };\n }\n\n if (\n input.bracket.kind ===\n \"exposure\"\n ) {\n const offset =\n input.bracket\n .offsetsStops[\n frameIndex\n ];\n if (offset === undefined) {\n throw new InvalidScientificInputError(\n \"Exposure bracket frame index is out of range.\"\n );\n }\n return {\n exposure:\n resolveExposureBracket(\n input.baseState.exposure,\n input.bracket.axis,\n offset\n ),\n focus:\n parseFocusPlane(\n input.baseState.focus\n ),\n exposureBracketOffsetStops:\n offset\n };\n }\n\n const focus =\n input.bracket\n .focusStates[frameIndex];\n if (focus === undefined) {\n throw new InvalidScientificInputError(\n \"Focus bracket frame index is out of range.\"\n );\n }\n return {\n exposure:\n cloneJson(\n input.baseState.exposure\n ),\n focus:\n parseFocusPlane(focus)\n };\n}\n\nfunction validateBracketSupport(\n input:\n ResolveReleaseSequenceInput,\n capabilities:\n GenericReleaseCapabilityProfile\n): void {\n if (\n input.bracket.kind ===\n \"exposure\"\n ) {\n if (\n capabilities\n .exposureBracketing\n .availability.value !==\n \"supported\"\n ) {\n throw new InvalidScientificInputError(\n \"Exposure bracketing is not supported by the selected release capability profile.\"\n );\n }\n if (\n !capabilities\n .exposureBracketing\n .supportedAxes\n .includes(\n input.bracket.axis\n )\n ) {\n throw new InvalidScientificInputError(\n \"The selected exposure-bracket axis is not supported by the release capability profile.\"\n );\n }\n }\n\n if (\n input.bracket.kind ===\n \"focus\" &&\n capabilities\n .focusBracketing\n .availability.value !==\n \"supported\"\n ) {\n throw new InvalidScientificInputError(\n \"Focus bracketing is not supported by the selected release capability profile.\"\n );\n }\n}\n\nfunction resolveSelfTimerDelay(\n drive: ReleaseDrivePolicy\n): number {\n if (drive.kind !== \"self-timer\") {\n return 0;\n }\n return requireNonNegativeFinite(\n drive.delaySeconds,\n \"drive.delaySeconds\"\n );\n}\n\nfunction resolveRequestedCadence(\n frameCount: number,\n requestedCadenceFps:\n number | undefined\n): number | null {\n if (frameCount <= 1) {\n if (\n requestedCadenceFps !==\n undefined\n ) {\n requirePositiveFinite(\n requestedCadenceFps,\n \"requestedCadenceFps\"\n );\n }\n return null;\n }\n if (\n requestedCadenceFps ===\n undefined\n ) {\n throw new InvalidScientificInputError(\n \"requestedCadenceFps is required for a multi-frame physical sequence.\"\n );\n }\n return requirePositiveFinite(\n requestedCadenceFps,\n \"requestedCadenceFps\"\n );\n}\n\nfunction intervalForPreviousFrame(\n previousFrame:\n ResolvedReleaseFrame,\n requestedCadenceFps: number,\n capabilities:\n GenericReleaseCapabilityProfile\n): {\n seconds: number;\n constraints:\n readonly ReleaseTimingConstraint[];\n} {\n const requested =\n 1 / requestedCadenceFps;\n const capability =\n 1 /\n capabilities\n .maximumCadenceFps.value;\n const exposure =\n previousFrame\n .exposure.shutterSeconds;\n const gap =\n capabilities\n .minimumInterFrameGapSeconds\n .value;\n const physical =\n exposure + gap;\n const seconds =\n Math.max(\n requested,\n capability,\n physical\n );\n\n const tolerance =\n Math.max(1, seconds) *\n 1e-12;\n const constraints:\n ReleaseTimingConstraint[] =\n [];\n\n if (\n Math.abs(\n seconds - requested\n ) <= tolerance\n ) {\n constraints.push(\n \"requested-cadence\"\n );\n }\n if (\n Math.abs(\n seconds - capability\n ) <= tolerance\n ) {\n constraints.push(\n \"body-maximum-cadence\"\n );\n }\n if (\n Math.abs(\n seconds - physical\n ) <= tolerance\n ) {\n constraints.push(\n \"exposure-duration\"\n );\n if (gap > 0) {\n constraints.push(\n \"minimum-inter-frame-gap\"\n );\n }\n }\n\n return {\n seconds,\n constraints\n };\n}\n\n/**\n * Resolves one deterministic still-release sequence.\n *\n * It schedules logical exposures only; how the sensor captures each one is\n * handled elsewhere. Render speed or frame rate is never used as capture timing.\n */\nexport function resolveReleaseSequence(\n input:\n ResolveReleaseSequenceInput\n): ResolvedReleaseSequence {\n requireInputKeys(input, \"resolveReleaseSequence\", [\"sequenceId\", \"releaseRequestTimeSeconds\", \"sequenceSeedUint32\", \"drive\", \"bracket\", \"requestedCadenceFps\", \"baseState\", \"releaseCapabilities\", \"exposureCapabilities\"]);\n const sequenceId =\n requireNonEmptyString(\n input.sequenceId,\n \"sequenceId\"\n );\n const releaseRequestTime =\n requireFinite(\n input\n .releaseRequestTimeSeconds,\n \"releaseRequestTimeSeconds\"\n );\n const sequenceSeed =\n requireUint32(\n input.sequenceSeedUint32,\n \"sequenceSeedUint32\"\n );\n\n const releaseCapabilities =\n parseGenericReleaseCapabilityProfile(\n input.releaseCapabilities\n );\n\n if (\n !includesDriveMode(\n releaseCapabilities,\n input.drive.kind\n )\n ) {\n throw new InvalidScientificInputError(\n \"The selected drive mode is not supported by the release capability profile.\"\n );\n }\n\n validateBracketSupport(\n input,\n releaseCapabilities\n );\n\n const frameCount =\n resolveFrameCount(\n input.drive,\n input.bracket\n );\n\n if (\n frameCount >\n releaseCapabilities\n .maximumLogicalFramesPerSequence\n .value\n ) {\n throw new InvalidScientificInputError(\n \"Requested sequence frame count exceeds the release capability profile.\"\n );\n }\n\n const requestedCadenceFps =\n resolveRequestedCadence(\n frameCount,\n input.requestedCadenceFps\n );\n\n const selfTimerDelay =\n resolveSelfTimerDelay(\n input.drive\n );\n\n const baseState:\n ReleaseBaseCaptureState = {\n exposure:\n cloneJson(\n input.baseState.exposure\n ),\n focus:\n parseFocusPlane(\n input.baseState.focus\n ),\n automation:\n cloneJson(\n input.baseState.automation\n ),\n ...(input.baseState\n .whiteBalanceStateId ===\n undefined\n ? {}\n : {\n whiteBalanceStateId:\n requireNonEmptyString(\n input.baseState\n .whiteBalanceStateId,\n \"baseState.whiteBalanceStateId\"\n )\n })\n };\n\n validateExposure(\n baseState.exposure,\n input.exposureCapabilities,\n \"baseState.exposure\"\n );\n\n const frames:\n ResolvedReleaseFrame[] = [];\n\n for (\n let frameIndex = 0;\n frameIndex < frameCount;\n frameIndex += 1\n ) {\n const state =\n resolveFrameState(\n input,\n frameIndex\n );\n validateExposure(\n state.exposure,\n input.exposureCapabilities,\n \"frames[\" +\n frameIndex +\n \"].exposure\"\n );\n\n let exposureStartTimeSeconds:\n number;\n let startInterval:\n number | null = null;\n let timingConstraints:\n readonly ReleaseTimingConstraint[] =\n [];\n\n if (frameIndex === 0) {\n exposureStartTimeSeconds =\n releaseRequestTime +\n selfTimerDelay;\n } else {\n const previous =\n frames[\n frameIndex - 1\n ]!;\n if (\n requestedCadenceFps ===\n null\n ) {\n throw new InvalidScientificInputError(\n \"Multi-frame sequence lost its requested cadence.\"\n );\n }\n const interval =\n intervalForPreviousFrame(\n previous,\n requestedCadenceFps,\n releaseCapabilities\n );\n startInterval =\n interval.seconds;\n timingConstraints =\n interval.constraints;\n exposureStartTimeSeconds =\n previous\n .exposureStartTimeSeconds +\n interval.seconds;\n }\n\n const exposureEndTimeSeconds =\n exposureStartTimeSeconds +\n state.exposure\n .shutterSeconds;\n\n if (\n !Number.isFinite(\n exposureStartTimeSeconds\n ) ||\n !Number.isFinite(\n exposureEndTimeSeconds\n )\n ) {\n throw new InvalidScientificInputError(\n \"Resolved release-sequence timing must remain finite.\"\n );\n }\n\n frames.push({\n sequenceId,\n frameIndex,\n releaseFrameId:\n sequenceId +\n \":frame:\" +\n frameIndex,\n exposureStartTimeSeconds,\n exposureEndTimeSeconds,\n sceneTimeSecondsFromSequenceStart:\n exposureStartTimeSeconds -\n releaseRequestTime,\n startIntervalFromPreviousSeconds:\n startInterval,\n timingConstraints,\n stochasticSeedUint32:\n hashFrameSeed(\n sequenceSeed,\n frameIndex\n ),\n exposure:\n cloneJson(\n state.exposure\n ),\n focus:\n cloneJson(\n state.focus\n ),\n ...(state\n .exposureBracketOffsetStops ===\n undefined\n ? {}\n : {\n exposureBracketOffsetStops:\n state\n .exposureBracketOffsetStops\n }),\n automation:\n cloneJson(\n baseState.automation\n ),\n ...(baseState\n .whiteBalanceStateId ===\n undefined\n ? {}\n : {\n whiteBalanceStateId:\n baseState\n .whiteBalanceStateId\n })\n });\n }\n\n const sequence:\n ResolvedReleaseSequence = {\n version:\n RELEASE_SEQUENCE_VERSION,\n sequenceId,\n status: \"scheduled\",\n releaseRequestTimeSeconds:\n releaseRequestTime,\n selfTimerDelaySeconds:\n selfTimerDelay,\n requestedCadenceFps,\n capabilityMaximumCadenceFps:\n releaseCapabilities\n .maximumCadenceFps.value,\n minimumInterFrameGapSeconds:\n releaseCapabilities\n .minimumInterFrameGapSeconds\n .value,\n requestedFrameCount:\n frameCount,\n actualFrameCount:\n frames.length,\n sequenceSeedUint32:\n sequenceSeed,\n baseStateSnapshot:\n baseState,\n frames,\n sensorCaptureModeOwnership:\n \"separate-logical-capture-contract\",\n whiteBalanceBracketingPhysicalCaptures:\n false,\n preReleaseCaptureModeled:\n false,\n bufferMediaThermalSlowdownModeled:\n false\n };\n\n return deepFreeze(\n cloneJson(sequence)\n );\n}\n\n/**\n * Returns a cancelled copy of a sequence. The original scheduled sequence is not\n * changed, and it is always clear which frames were completed and which were dropped.\n */\nexport function createCancelledReleaseSequence(\n input:\n CancelReleaseSequenceInput\n): CancelledReleaseSequence {\n requireInputKeys(input, \"createCancelledReleaseSequence\", [\"sequence\", \"completedFrameCount\", \"cancelledAtSeconds\"]);\n const count =\n requireNonNegativeFinite(\n input.completedFrameCount,\n \"completedFrameCount\"\n );\n if (\n !Number.isSafeInteger(count)\n ) {\n throw new InvalidScientificInputError(\n \"completedFrameCount must be a safe integer.\"\n );\n }\n if (\n count >\n input.sequence.frames.length\n ) {\n throw new InvalidScientificInputError(\n \"completedFrameCount must not exceed the source sequence frame count.\"\n );\n }\n\n const cancelledAt =\n requireFinite(\n input.cancelledAtSeconds,\n \"cancelledAtSeconds\"\n );\n\n const result:\n CancelledReleaseSequence = {\n version:\n RELEASE_SEQUENCE_VERSION,\n sequenceId:\n input.sequence.sequenceId,\n status: \"cancelled\",\n cancelledAtSeconds:\n cancelledAt,\n completedFrameCount: count,\n omittedFrameCount:\n input.sequence.frames.length -\n count,\n completedFrames:\n cloneJson(\n input.sequence.frames.slice(\n 0,\n count\n )\n ),\n sourceSequenceFrameCount:\n input.sequence.frames.length,\n sourceSequenceUnmodified:\n true\n };\n\n return deepFreeze(\n cloneJson(result)\n );\n}\n"]}
1
+ {"version":3,"file":"release-sequence.js","sourceRoot":"","sources":["../../src/capture/release-sequence.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC;;;;;;;;GAQG;AAEH,OAAO,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,EACL,oCAAoC,EAGrC,MAAM,sCAAsC,CAAC;AAK9C,OAAO,EACL,eAAe,EAEhB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,IAAI,qBAAqB,EAAE,kBAAkB,IAAI,aAAa,EAAE,2BAA2B,IAAI,qBAAqB,EAAE,6BAA6B,IAAI,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAC3Q,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAElE,MAAM,CAAC,MAAM,wBAAwB,GACnC,OAAgB,CAAC;AAuJnB,SAAS,0BAA0B,CACjC,KAAa,EACb,IAAY;IAEZ,IACE,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC;QAC5B,KAAK,IAAI,CAAC,EACV,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,IAAI;YACF,mCAAmC,CACtC,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,aAAa,CACpB,KAAa,EACb,IAAY;IAEZ,IACE,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC;QAC5B,KAAK,GAAG,CAAC;QACT,KAAK,GAAG,WAAW,EACnB,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,IAAI;YACF,sCAAsC,CACzC,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,aAAa,CACpB,kBAA0B,EAC1B,UAAkB;IAElB,IAAI,IAAI,GAAG,UAAU,CAAC;IACtB,MAAM,IAAI,GACR,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/B,GAAG;QACH,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC1B,KACE,IAAI,KAAK,GAAG,CAAC,EACb,KAAK,GAAG,IAAI,CAAC,MAAM,EACnB,KAAK,IAAI,CAAC,EACV,CAAC;QACD,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAC/B,IAAI,GAAG,IAAI,CAAC,IAAI,CACd,IAAI,EACJ,UAAU,CACX,KAAK,CAAC,CAAC;IACV,CAAC;IACD,OAAO,IAAI,KAAK,CAAC,CAAC;AACpB,CAAC;AAED,SAAS,iBAAiB,CACxB,YACiC,EACjC,KAC4B;IAE5B,OAAO,YAAY;SAChB,mBAAmB;SACnB,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC3B,CAAC;AAED,SAAS,eAAe,CACtB,KAAyB;IAEzB,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,0BAA0B,CAC/B,KAAK,CAAC,UAAU,EAChB,kBAAkB,CACnB,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CACxB,OACsB;IAEtB,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,KAAK,GACT,OAAO,CAAC,IAAI,KAAK,UAAU;QACzB,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM;QAC7B,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC;IACjC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QACf,MAAM,IAAI,2BAA2B,CACnC,oDAAoD,CACrD,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,iBAAiB,CACxB,KAAyB,EACzB,OACsB;IAEtB,MAAM,UAAU,GACd,eAAe,CAAC,KAAK,CAAC,CAAC;IACzB,MAAM,YAAY,GAChB,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAE7B,IAAI,YAAY,KAAK,IAAI,EAAE,CAAC;QAC1B,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC3B,MAAM,IAAI,2BAA2B,CACnC,sGAAsG,CACvG,CAAC;IACJ,CAAC;IAED,IACE,KAAK,CAAC,IAAI,KAAK,YAAY;QAC3B,UAAU,KAAK,YAAY,EAC3B,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,2DAA2D,CAC5D,CAAC;IACJ,CAAC;IAED,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,SAAS,mBAAmB,CAC1B,KAAa,EACb,OAAe,EACf,OAAe,EACf,IAC4B,EAC5B,IAAY;IAEZ,IACE,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QACvB,KAAK,GAAG,OAAO;QACf,KAAK,GAAG,OAAO,EACf,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,IAAI;YACF,wDAAwD,CAC3D,CAAC;IACJ,CAAC;IAED,IACE,IAAI,CAAC,IAAI;QACP,iBAAiB;QACnB,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CACf,CAAC,SAAS,EAAE,EAAE,CACZ,IAAI,CAAC,GAAG,CACN,SAAS,GAAG,KAAK,CAClB;YACD,IAAI,CAAC,GAAG,CACN,CAAC,EACD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAChB;gBACC,KAAK,CACV,EACD,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,IAAI;YACF,yDAAyD,CAC5D,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CACvB,QACqC,EACrC,YAC8C,EAC9C,IAAY;IAEZ,mBAAmB,CACjB,QAAQ,CAAC,QAAQ,EACjB,YAAY,CAAC,QAAQ;SAClB,sBAAsB,EACzB,YAAY,CAAC,QAAQ;SAClB,yBAAyB,EAC5B,YAAY,CAAC,QAAQ;SAClB,WAAW,EACd,IAAI,GAAG,WAAW,CACnB,CAAC;IACF,mBAAmB,CACjB,QAAQ,CAAC,cAAc,EACvB,YAAY,CAAC,OAAO;SACjB,cAAc,EACjB,YAAY,CAAC,OAAO;SACjB,cAAc,EACjB,YAAY,CAAC,OAAO;SACjB,WAAW,EACd,IAAI,GAAG,iBAAiB,CACzB,CAAC;IACF,mBAAmB,CACjB,QAAQ,CAAC,GAAG,EACZ,YAAY,CAAC,GAAG,CAAC,OAAO,EACxB,YAAY,CAAC,GAAG,CAAC,OAAO,EACxB,YAAY,CAAC,GAAG,CAAC,WAAW,EAC5B,IAAI,GAAG,MAAM,CACd,CAAC;AACJ,CAAC;AAED,SAAS,sBAAsB,CAC7B,IACqC,EACrC,IAC4B,EAC5B,WAAmB;IAEnB,aAAa,CACX,WAAW,EACX,wBAAwB,CACzB,CAAC;IAEF,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO;YACL,GAAG,IAAI;YACP,cAAc,EACZ,IAAI,CAAC,cAAc;gBACnB,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC;SAC3B,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACnB,OAAO;YACL,GAAG,IAAI;YACP,GAAG,EACD,IAAI,CAAC,GAAG;gBACR,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC;SAC3B,CAAC;IACJ,CAAC;IACD,OAAO;QACL,GAAG,IAAI;QACP,QAAQ,EACN,IAAI,CAAC,QAAQ;YACb,IAAI,CAAC,GAAG,CACN,CAAC,EACD,WAAW,GAAG,CAAC,CAChB;KACJ,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CACxB,KAC6B,EAC7B,UAAkB;IAQlB,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAClC,OAAO;YACL,QAAQ,EACN,SAAS,CACP,KAAK,CAAC,SAAS,CAAC,QAAQ,CACzB;YACH,KAAK,EACH,eAAe,CACb,KAAK,CAAC,SAAS,CAAC,KAAK,CACtB;SACJ,CAAC;IACJ,CAAC;IAED,IACE,KAAK,CAAC,OAAO,CAAC,IAAI;QAClB,UAAU,EACV,CAAC;QACD,MAAM,MAAM,GACV,KAAK,CAAC,OAAO;aACV,YAAY,CACX,UAAU,CACX,CAAC;QACN,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,IAAI,2BAA2B,CACnC,+CAA+C,CAChD,CAAC;QACJ,CAAC;QACD,OAAO;YACL,QAAQ,EACN,sBAAsB,CACpB,KAAK,CAAC,SAAS,CAAC,QAAQ,EACxB,KAAK,CAAC,OAAO,CAAC,IAAI,EAClB,MAAM,CACP;YACH,KAAK,EACH,eAAe,CACb,KAAK,CAAC,SAAS,CAAC,KAAK,CACtB;YACH,0BAA0B,EACxB,MAAM;SACT,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GACT,KAAK,CAAC,OAAO;SACV,WAAW,CAAC,UAAU,CAAC,CAAC;IAC7B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,IAAI,2BAA2B,CACnC,4CAA4C,CAC7C,CAAC;IACJ,CAAC;IACD,OAAO;QACL,QAAQ,EACN,SAAS,CACP,KAAK,CAAC,SAAS,CAAC,QAAQ,CACzB;QACH,KAAK,EACH,eAAe,CAAC,KAAK,CAAC;KACzB,CAAC;AACJ,CAAC;AAED,SAAS,sBAAsB,CAC7B,KAC6B,EAC7B,YACiC;IAEjC,IACE,KAAK,CAAC,OAAO,CAAC,IAAI;QAClB,UAAU,EACV,CAAC;QACD,IACE,YAAY;aACT,kBAAkB;aAClB,YAAY,CAAC,KAAK;YACrB,WAAW,EACX,CAAC;YACD,MAAM,IAAI,2BAA2B,CACnC,kFAAkF,CACnF,CAAC;QACJ,CAAC;QACD,IACE,CAAC,YAAY;aACV,kBAAkB;aAClB,aAAa;aACb,QAAQ,CACP,KAAK,CAAC,OAAO,CAAC,IAAI,CACnB,EACH,CAAC;YACD,MAAM,IAAI,2BAA2B,CACnC,wFAAwF,CACzF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IACE,KAAK,CAAC,OAAO,CAAC,IAAI;QAChB,OAAO;QACT,YAAY;aACT,eAAe;aACf,YAAY,CAAC,KAAK;YACnB,WAAW,EACb,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,+EAA+E,CAChF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,qBAAqB,CAC5B,KAAyB;IAEzB,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;QAChC,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,wBAAwB,CAC7B,KAAK,CAAC,YAAY,EAClB,oBAAoB,CACrB,CAAC;AACJ,CAAC;AAED,SAAS,uBAAuB,CAC9B,UAAkB,EAClB,mBACoB;IAEpB,IAAI,UAAU,IAAI,CAAC,EAAE,CAAC;QACpB,IACE,mBAAmB;YACnB,SAAS,EACT,CAAC;YACD,qBAAqB,CACnB,mBAAmB,EACnB,qBAAqB,CACtB,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IACE,mBAAmB;QACnB,SAAS,EACT,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,sEAAsE,CACvE,CAAC;IACJ,CAAC;IACD,OAAO,qBAAqB,CAC1B,mBAAmB,EACnB,qBAAqB,CACtB,CAAC;AACJ,CAAC;AAED,SAAS,wBAAwB,CAC/B,aACsB,EACtB,mBAA2B,EAC3B,YACiC;IAMjC,MAAM,SAAS,GACb,CAAC,GAAG,mBAAmB,CAAC;IAC1B,MAAM,UAAU,GACd,CAAC;QACD,YAAY;aACT,iBAAiB,CAAC,KAAK,CAAC;IAC7B,MAAM,QAAQ,GACZ,aAAa;SACV,QAAQ,CAAC,cAAc,CAAC;IAC7B,MAAM,GAAG,GACP,YAAY;SACT,2BAA2B;SAC3B,KAAK,CAAC;IACX,MAAM,QAAQ,GACZ,QAAQ,GAAG,GAAG,CAAC;IACjB,MAAM,OAAO,GACX,IAAI,CAAC,GAAG,CACN,SAAS,EACT,UAAU,EACV,QAAQ,CACT,CAAC;IAEJ,MAAM,SAAS,GACb,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC;QACpB,KAAK,CAAC;IACR,MAAM,WAAW,GAEb,EAAE,CAAC;IAEP,IACE,IAAI,CAAC,GAAG,CACN,OAAO,GAAG,SAAS,CACpB,IAAI,SAAS,EACd,CAAC;QACD,WAAW,CAAC,IAAI,CACd,mBAAmB,CACpB,CAAC;IACJ,CAAC;IACD,IACE,IAAI,CAAC,GAAG,CACN,OAAO,GAAG,UAAU,CACrB,IAAI,SAAS,EACd,CAAC;QACD,WAAW,CAAC,IAAI,CACd,sBAAsB,CACvB,CAAC;IACJ,CAAC;IACD,IACE,IAAI,CAAC,GAAG,CACN,OAAO,GAAG,QAAQ,CACnB,IAAI,SAAS,EACd,CAAC;QACD,WAAW,CAAC,IAAI,CACd,mBAAmB,CACpB,CAAC;QACF,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;YACZ,WAAW,CAAC,IAAI,CACd,yBAAyB,CAC1B,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO;QACL,OAAO;QACP,WAAW;KACZ,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CACpC,KAC6B;IAE7B,gBAAgB,CAAC,KAAK,EAAE,wBAAwB,EAAE,CAAC,YAAY,EAAE,2BAA2B,EAAE,oBAAoB,EAAE,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,WAAW,EAAE,qBAAqB,EAAE,sBAAsB,CAAC,CAAC,CAAC;IAC5N,MAAM,UAAU,GACd,qBAAqB,CACnB,KAAK,CAAC,UAAU,EAChB,YAAY,CACb,CAAC;IACJ,MAAM,kBAAkB,GACtB,aAAa,CACX,KAAK;SACF,yBAAyB,EAC5B,2BAA2B,CAC5B,CAAC;IACJ,MAAM,YAAY,GAChB,aAAa,CACX,KAAK,CAAC,kBAAkB,EACxB,oBAAoB,CACrB,CAAC;IAEJ,MAAM,mBAAmB,GACvB,oCAAoC,CAClC,KAAK,CAAC,mBAAmB,CAC1B,CAAC;IAEJ,IACE,CAAC,iBAAiB,CAChB,mBAAmB,EACnB,KAAK,CAAC,KAAK,CAAC,IAAI,CACjB,EACD,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,6EAA6E,CAC9E,CAAC;IACJ,CAAC;IAED,sBAAsB,CACpB,KAAK,EACL,mBAAmB,CACpB,CAAC;IAEF,MAAM,UAAU,GACd,iBAAiB,CACf,KAAK,CAAC,KAAK,EACX,KAAK,CAAC,OAAO,CACd,CAAC;IAEJ,IACE,UAAU;QACV,mBAAmB;aAChB,+BAA+B;aAC/B,KAAK,EACR,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,wEAAwE,CACzE,CAAC;IACJ,CAAC;IAED,MAAM,mBAAmB,GACvB,uBAAuB,CACrB,UAAU,EACV,KAAK,CAAC,mBAAmB,CAC1B,CAAC;IAEJ,MAAM,cAAc,GAClB,qBAAqB,CACnB,KAAK,CAAC,KAAK,CACZ,CAAC;IAEJ,MAAM,SAAS,GACa;QAC1B,QAAQ,EACN,SAAS,CACP,KAAK,CAAC,SAAS,CAAC,QAAQ,CACzB;QACH,KAAK,EACH,eAAe,CACb,KAAK,CAAC,SAAS,CAAC,KAAK,CACtB;QACH,UAAU,EACR,SAAS,CACP,KAAK,CAAC,SAAS,CAAC,UAAU,CAC3B;QACH,GAAG,CAAC,KAAK,CAAC,SAAS;aAChB,mBAAmB;YACtB,SAAS;YACP,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC;gBACE,mBAAmB,EACjB,qBAAqB,CACnB,KAAK,CAAC,SAAS;qBACZ,mBAAmB,EACtB,+BAA+B,CAChC;aACJ,CAAC;KACP,CAAC;IAEF,gBAAgB,CACd,SAAS,CAAC,QAAQ,EAClB,KAAK,CAAC,oBAAoB,EAC1B,oBAAoB,CACrB,CAAC;IAEF,MAAM,MAAM,GACe,EAAE,CAAC;IAE9B,KACE,IAAI,UAAU,GAAG,CAAC,EAClB,UAAU,GAAG,UAAU,EACvB,UAAU,IAAI,CAAC,EACf,CAAC;QACD,MAAM,KAAK,GACT,iBAAiB,CACf,KAAK,EACL,UAAU,CACX,CAAC;QACJ,gBAAgB,CACd,KAAK,CAAC,QAAQ,EACd,KAAK,CAAC,oBAAoB,EAC1B,SAAS;YACP,UAAU;YACV,YAAY,CACf,CAAC;QAEF,IAAI,wBACI,CAAC;QACT,IAAI,aAAa,GACC,IAAI,CAAC;QACvB,IAAI,iBAAiB,GAEjB,EAAE,CAAC;QAEP,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;YACrB,wBAAwB;gBACtB,kBAAkB;oBAClB,cAAc,CAAC;QACnB,CAAC;aAAM,CAAC;YACN,MAAM,QAAQ,GACZ,MAAM,CACJ,UAAU,GAAG,CAAC,CACd,CAAC;YACL,IACE,mBAAmB;gBACnB,IAAI,EACJ,CAAC;gBACD,MAAM,IAAI,2BAA2B,CACnC,kDAAkD,CACnD,CAAC;YACJ,CAAC;YACD,MAAM,QAAQ,GACZ,wBAAwB,CACtB,QAAQ,EACR,mBAAmB,EACnB,mBAAmB,CACpB,CAAC;YACJ,aAAa;gBACX,QAAQ,CAAC,OAAO,CAAC;YACnB,iBAAiB;gBACf,QAAQ,CAAC,WAAW,CAAC;YACvB,wBAAwB;gBACtB,QAAQ;qBACL,wBAAwB;oBAC3B,QAAQ,CAAC,OAAO,CAAC;QACrB,CAAC;QAED,MAAM,sBAAsB,GAC1B,wBAAwB;YACxB,KAAK,CAAC,QAAQ;iBACX,cAAc,CAAC;QAEpB,IACE,CAAC,MAAM,CAAC,QAAQ,CACd,wBAAwB,CACzB;YACD,CAAC,MAAM,CAAC,QAAQ,CACd,sBAAsB,CACvB,EACD,CAAC;YACD,MAAM,IAAI,2BAA2B,CACnC,sDAAsD,CACvD,CAAC;QACJ,CAAC;QAED,MAAM,CAAC,IAAI,CAAC;YACV,UAAU;YACV,UAAU;YACV,cAAc,EACZ,UAAU;gBACV,SAAS;gBACT,UAAU;YACZ,wBAAwB;YACxB,sBAAsB;YACtB,iCAAiC,EAC/B,wBAAwB;gBACxB,kBAAkB;YACpB,gCAAgC,EAC9B,aAAa;YACf,iBAAiB;YACjB,oBAAoB,EAClB,aAAa,CACX,YAAY,EACZ,UAAU,CACX;YACH,QAAQ,EACN,SAAS,CACP,KAAK,CAAC,QAAQ,CACf;YACH,KAAK,EACH,SAAS,CACP,KAAK,CAAC,KAAK,CACZ;YACH,GAAG,CAAC,KAAK;iBACN,0BAA0B;gBAC7B,SAAS;gBACP,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC;oBACE,0BAA0B,EACxB,KAAK;yBACF,0BAA0B;iBAChC,CAAC;YACN,UAAU,EACR,SAAS,CACP,SAAS,CAAC,UAAU,CACrB;YACH,GAAG,CAAC,SAAS;iBACV,mBAAmB;gBACtB,SAAS;gBACP,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC;oBACE,mBAAmB,EACjB,SAAS;yBACN,mBAAmB;iBACzB,CAAC;SACP,CAAC,CAAC;IACL,CAAC;IAED,MAAM,QAAQ,GACc;QAC1B,OAAO,EACL,wBAAwB;QAC1B,UAAU;QACV,MAAM,EAAE,WAAW;QACnB,yBAAyB,EACvB,kBAAkB;QACpB,qBAAqB,EACnB,cAAc;QAChB,mBAAmB;QACnB,2BAA2B,EACzB,mBAAmB;aAChB,iBAAiB,CAAC,KAAK;QAC5B,2BAA2B,EACzB,mBAAmB;aAChB,2BAA2B;aAC3B,KAAK;QACV,mBAAmB,EACjB,UAAU;QACZ,gBAAgB,EACd,MAAM,CAAC,MAAM;QACf,kBAAkB,EAChB,YAAY;QACd,iBAAiB,EACf,SAAS;QACX,MAAM;QACN,0BAA0B,EACxB,mCAAmC;QACrC,sCAAsC,EACpC,KAAK;QACP,wBAAwB,EACtB,KAAK;QACP,iCAAiC,EAC/B,KAAK;KACR,CAAC;IAEF,OAAO,UAAU,CACf,SAAS,CAAC,QAAQ,CAAC,CACpB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,8BAA8B,CAC5C,KAC4B;IAE5B,gBAAgB,CAAC,KAAK,EAAE,gCAAgC,EAAE,CAAC,UAAU,EAAE,qBAAqB,EAAE,oBAAoB,CAAC,CAAC,CAAC;IACrH,MAAM,KAAK,GACT,wBAAwB,CACtB,KAAK,CAAC,mBAAmB,EACzB,qBAAqB,CACtB,CAAC;IACJ,IACE,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,EAC5B,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,6CAA6C,CAC9C,CAAC;IACJ,CAAC;IACD,IACE,KAAK;QACL,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAC5B,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,sEAAsE,CACvE,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GACf,aAAa,CACX,KAAK,CAAC,kBAAkB,EACxB,oBAAoB,CACrB,CAAC;IAEJ,MAAM,MAAM,GACiB;QAC3B,OAAO,EACL,wBAAwB;QAC1B,UAAU,EACR,KAAK,CAAC,QAAQ,CAAC,UAAU;QAC3B,MAAM,EAAE,WAAW;QACnB,kBAAkB,EAChB,WAAW;QACb,mBAAmB,EAAE,KAAK;QAC1B,iBAAiB,EACf,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM;YAC5B,KAAK;QACP,eAAe,EACb,SAAS,CACP,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CACzB,CAAC,EACD,KAAK,CACN,CACF;QACH,wBAAwB,EACtB,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM;QAC9B,wBAAwB,EACtB,IAAI;KACP,CAAC;IAEF,OAAO,UAAU,CACf,SAAS,CAAC,MAAM,CAAC,CAClB,CAAC;AACJ,CAAC","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Shutter-release sequences for stills (single, burst, self-timer, bracketing), scheduled as logical exposures, and\n * cancelling a sequence part-way.\n *\n * This only schedules exposures; how the sensor captures each one is handled elsewhere. Render speed\n * or frame rate is never used as capture timing.\n * @see docs/SIMULATED_CAPTURE.md for equations, coordinate/unit conventions, blockers and support\n * limits.\n */\n\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport {\n parseGenericReleaseCapabilityProfile,\n type GenericExposureBracketAxis,\n type GenericReleaseCapabilityProfile\n} from \"../equipment/release-capabilities.js\";\nimport type {\n ResolvedGenericEquipmentExposureCapabilities,\n ResolvedNumericSettingGrid\n} from \"../equipment/exposure-capabilities.js\";\nimport {\n parseFocusPlane,\n type FocusPlane\n} from \"../optics/focus-state.js\";\nimport { requireInputKeys, requireTrimmedConfigString as requireNonEmptyString, requireInputFinite as requireFinite, requireInputFiniteAboveZero as requirePositiveFinite, requireInputFiniteAtLeastZero as requireNonNegativeFinite } from \"../core/record-validation.js\";\nimport { deepFreeze, cloneJson } from \"../core/shared-helpers.js\";\n\nexport const RELEASE_SEQUENCE_VERSION =\n \"0.1.0\" as const;\n\nexport type ReleaseAutomationState =\n | \"manual\"\n | \"locked\"\n | \"continuous\";\n\nexport interface ReleaseBaseCaptureState {\n exposure: {\n aperture: number;\n shutterSeconds: number;\n iso: number;\n };\n focus: FocusPlane;\n whiteBalanceStateId?: string;\n automation: {\n ae: ReleaseAutomationState;\n af: ReleaseAutomationState;\n awb: ReleaseAutomationState;\n };\n}\n\nexport type ReleaseDrivePolicy =\n | {\n kind: \"single\";\n }\n | {\n kind: \"burst\";\n frameCount: number;\n }\n | {\n kind: \"self-timer\";\n delaySeconds: number;\n frameCount: number;\n };\n\nexport type ReleaseBracketPolicy =\n | {\n kind: \"none\";\n }\n | {\n kind: \"exposure\";\n axis:\n GenericExposureBracketAxis;\n offsetsStops:\n readonly number[];\n }\n | {\n kind: \"focus\";\n focusStates:\n readonly FocusPlane[];\n };\n\nexport interface ResolveReleaseSequenceInput {\n sequenceId: string;\n releaseRequestTimeSeconds: number;\n sequenceSeedUint32: number;\n drive: ReleaseDrivePolicy;\n bracket: ReleaseBracketPolicy;\n requestedCadenceFps?: number;\n baseState:\n ReleaseBaseCaptureState;\n releaseCapabilities:\n GenericReleaseCapabilityProfile;\n exposureCapabilities:\n ResolvedGenericEquipmentExposureCapabilities;\n}\n\nexport type ReleaseTimingConstraint =\n | \"requested-cadence\"\n | \"body-maximum-cadence\"\n | \"exposure-duration\"\n | \"minimum-inter-frame-gap\";\n\nexport interface ResolvedReleaseFrame {\n sequenceId: string;\n frameIndex: number;\n releaseFrameId: string;\n exposureStartTimeSeconds: number;\n exposureEndTimeSeconds: number;\n sceneTimeSecondsFromSequenceStart:\n number;\n startIntervalFromPreviousSeconds:\n number | null;\n timingConstraints:\n readonly ReleaseTimingConstraint[];\n stochasticSeedUint32: number;\n exposure: {\n aperture: number;\n shutterSeconds: number;\n iso: number;\n };\n focus: FocusPlane;\n exposureBracketOffsetStops?:\n number;\n automation:\n ReleaseBaseCaptureState[\"automation\"];\n whiteBalanceStateId?: string;\n}\n\nexport interface ResolvedReleaseSequence {\n version:\n typeof RELEASE_SEQUENCE_VERSION;\n sequenceId: string;\n status: \"scheduled\";\n releaseRequestTimeSeconds: number;\n selfTimerDelaySeconds: number;\n requestedCadenceFps:\n number | null;\n capabilityMaximumCadenceFps:\n number;\n minimumInterFrameGapSeconds:\n number;\n requestedFrameCount: number;\n actualFrameCount: number;\n sequenceSeedUint32: number;\n baseStateSnapshot:\n ReleaseBaseCaptureState;\n frames:\n readonly ResolvedReleaseFrame[];\n sensorCaptureModeOwnership:\n \"separate-logical-capture-contract\";\n whiteBalanceBracketingPhysicalCaptures:\n false;\n preReleaseCaptureModeled:\n false;\n bufferMediaThermalSlowdownModeled:\n false;\n}\n\nexport interface CancelReleaseSequenceInput {\n sequence:\n ResolvedReleaseSequence;\n completedFrameCount: number;\n cancelledAtSeconds: number;\n}\n\nexport interface CancelledReleaseSequence {\n version:\n typeof RELEASE_SEQUENCE_VERSION;\n sequenceId: string;\n status: \"cancelled\";\n cancelledAtSeconds: number;\n completedFrameCount: number;\n omittedFrameCount: number;\n completedFrames:\n readonly ResolvedReleaseFrame[];\n sourceSequenceFrameCount: number;\n sourceSequenceUnmodified: true;\n}\n\nfunction requirePositiveSafeInteger(\n value: number,\n path: string\n): number {\n if (\n !Number.isSafeInteger(value) ||\n value <= 0\n ) {\n throw new InvalidScientificInputError(\n path +\n \" must be a positive safe integer.\"\n );\n }\n return value;\n}\n\nfunction requireUint32(\n value: number,\n path: string\n): number {\n if (\n !Number.isSafeInteger(value) ||\n value < 0 ||\n value > 0xffff_ffff\n ) {\n throw new InvalidScientificInputError(\n path +\n \" must be an unsigned 32-bit integer.\"\n );\n }\n return value;\n}\n\nfunction hashFrameSeed(\n sequenceSeedUint32: number,\n frameIndex: number\n): number {\n let hash = 0x811c9dc5;\n const text =\n sequenceSeedUint32.toString(10) +\n \":\" +\n frameIndex.toString(10);\n for (\n let index = 0;\n index < text.length;\n index += 1\n ) {\n hash ^= text.charCodeAt(index);\n hash = Math.imul(\n hash,\n 0x01000193\n ) >>> 0;\n }\n return hash >>> 0;\n}\n\nfunction includesDriveMode(\n capabilities:\n GenericReleaseCapabilityProfile,\n drive:\n ReleaseDrivePolicy[\"kind\"]\n): boolean {\n return capabilities\n .supportedDriveModes\n .value.includes(drive);\n}\n\nfunction driveFrameCount(\n drive: ReleaseDrivePolicy\n): number {\n if (drive.kind === \"single\") {\n return 1;\n }\n return requirePositiveSafeInteger(\n drive.frameCount,\n \"drive.frameCount\"\n );\n}\n\nfunction bracketFrameCount(\n bracket:\n ReleaseBracketPolicy\n): number | null {\n if (bracket.kind === \"none\") {\n return null;\n }\n const count =\n bracket.kind === \"exposure\"\n ? bracket.offsetsStops.length\n : bracket.focusStates.length;\n if (count <= 0) {\n throw new InvalidScientificInputError(\n \"Bracket sequences must contain at least one frame.\"\n );\n }\n return count;\n}\n\nfunction resolveFrameCount(\n drive: ReleaseDrivePolicy,\n bracket:\n ReleaseBracketPolicy\n): number {\n const driveCount =\n driveFrameCount(drive);\n const bracketCount =\n bracketFrameCount(bracket);\n\n if (bracketCount === null) {\n return driveCount;\n }\n\n if (drive.kind === \"burst\") {\n throw new InvalidScientificInputError(\n \"Schema 0.1.0 does not combine burst drive with bracketing; choose one multi-frame policy explicitly.\"\n );\n }\n\n if (\n drive.kind === \"self-timer\" &&\n driveCount !== bracketCount\n ) {\n throw new InvalidScientificInputError(\n \"Self-timer frameCount must match the bracket frame count.\"\n );\n }\n\n return bracketCount;\n}\n\nfunction validateGridSetting(\n value: number,\n minimum: number,\n maximum: number,\n grid:\n ResolvedNumericSettingGrid,\n path: string\n): void {\n if (\n !Number.isFinite(value) ||\n value < minimum ||\n value > maximum\n ) {\n throw new InvalidScientificInputError(\n path +\n \" lies outside the resolved equipment capability range.\"\n );\n }\n\n if (\n grid.kind ===\n \"discrete-values\" &&\n !grid.values.some(\n (candidate) =>\n Math.abs(\n candidate - value\n ) <=\n Math.max(\n 1,\n Math.abs(value)\n ) *\n 1e-12\n )\n ) {\n throw new InvalidScientificInputError(\n path +\n \" is not present in the resolved equipment setting grid.\"\n );\n }\n}\n\nfunction validateExposure(\n exposure:\n ReleaseBaseCaptureState[\"exposure\"],\n capabilities:\n ResolvedGenericEquipmentExposureCapabilities,\n path: string\n): void {\n validateGridSetting(\n exposure.aperture,\n capabilities.aperture\n .widestAvailableFNumber,\n capabilities.aperture\n .narrowestAvailableFNumber,\n capabilities.aperture\n .settingGrid,\n path + \".aperture\"\n );\n validateGridSetting(\n exposure.shutterSeconds,\n capabilities.shutter\n .minimumSeconds,\n capabilities.shutter\n .maximumSeconds,\n capabilities.shutter\n .settingGrid,\n path + \".shutterSeconds\"\n );\n validateGridSetting(\n exposure.iso,\n capabilities.iso.minimum,\n capabilities.iso.maximum,\n capabilities.iso.settingGrid,\n path + \".iso\"\n );\n}\n\nfunction resolveExposureBracket(\n base:\n ReleaseBaseCaptureState[\"exposure\"],\n axis:\n GenericExposureBracketAxis,\n offsetStops: number\n): ReleaseBaseCaptureState[\"exposure\"] {\n requireFinite(\n offsetStops,\n \"bracket.offsetsStops[]\"\n );\n\n if (axis === \"shutter\") {\n return {\n ...base,\n shutterSeconds:\n base.shutterSeconds *\n Math.pow(2, offsetStops)\n };\n }\n if (axis === \"iso\") {\n return {\n ...base,\n iso:\n base.iso *\n Math.pow(2, offsetStops)\n };\n }\n return {\n ...base,\n aperture:\n base.aperture /\n Math.pow(\n 2,\n offsetStops / 2\n )\n };\n}\n\nfunction resolveFrameState(\n input:\n ResolveReleaseSequenceInput,\n frameIndex: number\n): {\n exposure:\n ReleaseBaseCaptureState[\"exposure\"];\n focus: FocusPlane;\n exposureBracketOffsetStops?:\n number;\n} {\n if (input.bracket.kind === \"none\") {\n return {\n exposure:\n cloneJson(\n input.baseState.exposure\n ),\n focus:\n parseFocusPlane(\n input.baseState.focus\n )\n };\n }\n\n if (\n input.bracket.kind ===\n \"exposure\"\n ) {\n const offset =\n input.bracket\n .offsetsStops[\n frameIndex\n ];\n if (offset === undefined) {\n throw new InvalidScientificInputError(\n \"Exposure bracket frame index is out of range.\"\n );\n }\n return {\n exposure:\n resolveExposureBracket(\n input.baseState.exposure,\n input.bracket.axis,\n offset\n ),\n focus:\n parseFocusPlane(\n input.baseState.focus\n ),\n exposureBracketOffsetStops:\n offset\n };\n }\n\n const focus =\n input.bracket\n .focusStates[frameIndex];\n if (focus === undefined) {\n throw new InvalidScientificInputError(\n \"Focus bracket frame index is out of range.\"\n );\n }\n return {\n exposure:\n cloneJson(\n input.baseState.exposure\n ),\n focus:\n parseFocusPlane(focus)\n };\n}\n\nfunction validateBracketSupport(\n input:\n ResolveReleaseSequenceInput,\n capabilities:\n GenericReleaseCapabilityProfile\n): void {\n if (\n input.bracket.kind ===\n \"exposure\"\n ) {\n if (\n capabilities\n .exposureBracketing\n .availability.value !==\n \"supported\"\n ) {\n throw new InvalidScientificInputError(\n \"Exposure bracketing is not supported by the selected release capability profile.\"\n );\n }\n if (\n !capabilities\n .exposureBracketing\n .supportedAxes\n .includes(\n input.bracket.axis\n )\n ) {\n throw new InvalidScientificInputError(\n \"The selected exposure-bracket axis is not supported by the release capability profile.\"\n );\n }\n }\n\n if (\n input.bracket.kind ===\n \"focus\" &&\n capabilities\n .focusBracketing\n .availability.value !==\n \"supported\"\n ) {\n throw new InvalidScientificInputError(\n \"Focus bracketing is not supported by the selected release capability profile.\"\n );\n }\n}\n\nfunction resolveSelfTimerDelay(\n drive: ReleaseDrivePolicy\n): number {\n if (drive.kind !== \"self-timer\") {\n return 0;\n }\n return requireNonNegativeFinite(\n drive.delaySeconds,\n \"drive.delaySeconds\"\n );\n}\n\nfunction resolveRequestedCadence(\n frameCount: number,\n requestedCadenceFps:\n number | undefined\n): number | null {\n if (frameCount <= 1) {\n if (\n requestedCadenceFps !==\n undefined\n ) {\n requirePositiveFinite(\n requestedCadenceFps,\n \"requestedCadenceFps\"\n );\n }\n return null;\n }\n if (\n requestedCadenceFps ===\n undefined\n ) {\n throw new InvalidScientificInputError(\n \"requestedCadenceFps is required for a multi-frame physical sequence.\"\n );\n }\n return requirePositiveFinite(\n requestedCadenceFps,\n \"requestedCadenceFps\"\n );\n}\n\nfunction intervalForPreviousFrame(\n previousFrame:\n ResolvedReleaseFrame,\n requestedCadenceFps: number,\n capabilities:\n GenericReleaseCapabilityProfile\n): {\n seconds: number;\n constraints:\n readonly ReleaseTimingConstraint[];\n} {\n const requested =\n 1 / requestedCadenceFps;\n const capability =\n 1 /\n capabilities\n .maximumCadenceFps.value;\n const exposure =\n previousFrame\n .exposure.shutterSeconds;\n const gap =\n capabilities\n .minimumInterFrameGapSeconds\n .value;\n const physical =\n exposure + gap;\n const seconds =\n Math.max(\n requested,\n capability,\n physical\n );\n\n const tolerance =\n Math.max(1, seconds) *\n 1e-12;\n const constraints:\n ReleaseTimingConstraint[] =\n [];\n\n if (\n Math.abs(\n seconds - requested\n ) <= tolerance\n ) {\n constraints.push(\n \"requested-cadence\"\n );\n }\n if (\n Math.abs(\n seconds - capability\n ) <= tolerance\n ) {\n constraints.push(\n \"body-maximum-cadence\"\n );\n }\n if (\n Math.abs(\n seconds - physical\n ) <= tolerance\n ) {\n constraints.push(\n \"exposure-duration\"\n );\n if (gap > 0) {\n constraints.push(\n \"minimum-inter-frame-gap\"\n );\n }\n }\n\n return {\n seconds,\n constraints\n };\n}\n\n/**\n * Resolves one deterministic still-release sequence.\n *\n * It schedules logical exposures only; how the sensor captures each one is\n * handled elsewhere. Render speed or frame rate is never used as capture timing.\n */\nexport function resolveReleaseSequence(\n input:\n ResolveReleaseSequenceInput\n): ResolvedReleaseSequence {\n requireInputKeys(input, \"resolveReleaseSequence\", [\"sequenceId\", \"releaseRequestTimeSeconds\", \"sequenceSeedUint32\", \"drive\", \"bracket\", \"requestedCadenceFps\", \"baseState\", \"releaseCapabilities\", \"exposureCapabilities\"]);\n const sequenceId =\n requireNonEmptyString(\n input.sequenceId,\n \"sequenceId\"\n );\n const releaseRequestTime =\n requireFinite(\n input\n .releaseRequestTimeSeconds,\n \"releaseRequestTimeSeconds\"\n );\n const sequenceSeed =\n requireUint32(\n input.sequenceSeedUint32,\n \"sequenceSeedUint32\"\n );\n\n const releaseCapabilities =\n parseGenericReleaseCapabilityProfile(\n input.releaseCapabilities\n );\n\n if (\n !includesDriveMode(\n releaseCapabilities,\n input.drive.kind\n )\n ) {\n throw new InvalidScientificInputError(\n \"The selected drive mode is not supported by the release capability profile.\"\n );\n }\n\n validateBracketSupport(\n input,\n releaseCapabilities\n );\n\n const frameCount =\n resolveFrameCount(\n input.drive,\n input.bracket\n );\n\n if (\n frameCount >\n releaseCapabilities\n .maximumLogicalFramesPerSequence\n .value\n ) {\n throw new InvalidScientificInputError(\n \"Requested sequence frame count exceeds the release capability profile.\"\n );\n }\n\n const requestedCadenceFps =\n resolveRequestedCadence(\n frameCount,\n input.requestedCadenceFps\n );\n\n const selfTimerDelay =\n resolveSelfTimerDelay(\n input.drive\n );\n\n const baseState:\n ReleaseBaseCaptureState = {\n exposure:\n cloneJson(\n input.baseState.exposure\n ),\n focus:\n parseFocusPlane(\n input.baseState.focus\n ),\n automation:\n cloneJson(\n input.baseState.automation\n ),\n ...(input.baseState\n .whiteBalanceStateId ===\n undefined\n ? {}\n : {\n whiteBalanceStateId:\n requireNonEmptyString(\n input.baseState\n .whiteBalanceStateId,\n \"baseState.whiteBalanceStateId\"\n )\n })\n };\n\n validateExposure(\n baseState.exposure,\n input.exposureCapabilities,\n \"baseState.exposure\"\n );\n\n const frames:\n ResolvedReleaseFrame[] = [];\n\n for (\n let frameIndex = 0;\n frameIndex < frameCount;\n frameIndex += 1\n ) {\n const state =\n resolveFrameState(\n input,\n frameIndex\n );\n validateExposure(\n state.exposure,\n input.exposureCapabilities,\n \"frames[\" +\n frameIndex +\n \"].exposure\"\n );\n\n let exposureStartTimeSeconds:\n number;\n let startInterval:\n number | null = null;\n let timingConstraints:\n readonly ReleaseTimingConstraint[] =\n [];\n\n if (frameIndex === 0) {\n exposureStartTimeSeconds =\n releaseRequestTime +\n selfTimerDelay;\n } else {\n const previous =\n frames[\n frameIndex - 1\n ]!;\n if (\n requestedCadenceFps ===\n null\n ) {\n throw new InvalidScientificInputError(\n \"Multi-frame sequence lost its requested cadence.\"\n );\n }\n const interval =\n intervalForPreviousFrame(\n previous,\n requestedCadenceFps,\n releaseCapabilities\n );\n startInterval =\n interval.seconds;\n timingConstraints =\n interval.constraints;\n exposureStartTimeSeconds =\n previous\n .exposureStartTimeSeconds +\n interval.seconds;\n }\n\n const exposureEndTimeSeconds =\n exposureStartTimeSeconds +\n state.exposure\n .shutterSeconds;\n\n if (\n !Number.isFinite(\n exposureStartTimeSeconds\n ) ||\n !Number.isFinite(\n exposureEndTimeSeconds\n )\n ) {\n throw new InvalidScientificInputError(\n \"Resolved release-sequence timing must remain finite.\"\n );\n }\n\n frames.push({\n sequenceId,\n frameIndex,\n releaseFrameId:\n sequenceId +\n \":frame:\" +\n frameIndex,\n exposureStartTimeSeconds,\n exposureEndTimeSeconds,\n sceneTimeSecondsFromSequenceStart:\n exposureStartTimeSeconds -\n releaseRequestTime,\n startIntervalFromPreviousSeconds:\n startInterval,\n timingConstraints,\n stochasticSeedUint32:\n hashFrameSeed(\n sequenceSeed,\n frameIndex\n ),\n exposure:\n cloneJson(\n state.exposure\n ),\n focus:\n cloneJson(\n state.focus\n ),\n ...(state\n .exposureBracketOffsetStops ===\n undefined\n ? {}\n : {\n exposureBracketOffsetStops:\n state\n .exposureBracketOffsetStops\n }),\n automation:\n cloneJson(\n baseState.automation\n ),\n ...(baseState\n .whiteBalanceStateId ===\n undefined\n ? {}\n : {\n whiteBalanceStateId:\n baseState\n .whiteBalanceStateId\n })\n });\n }\n\n const sequence:\n ResolvedReleaseSequence = {\n version:\n RELEASE_SEQUENCE_VERSION,\n sequenceId,\n status: \"scheduled\",\n releaseRequestTimeSeconds:\n releaseRequestTime,\n selfTimerDelaySeconds:\n selfTimerDelay,\n requestedCadenceFps,\n capabilityMaximumCadenceFps:\n releaseCapabilities\n .maximumCadenceFps.value,\n minimumInterFrameGapSeconds:\n releaseCapabilities\n .minimumInterFrameGapSeconds\n .value,\n requestedFrameCount:\n frameCount,\n actualFrameCount:\n frames.length,\n sequenceSeedUint32:\n sequenceSeed,\n baseStateSnapshot:\n baseState,\n frames,\n sensorCaptureModeOwnership:\n \"separate-logical-capture-contract\",\n whiteBalanceBracketingPhysicalCaptures:\n false,\n preReleaseCaptureModeled:\n false,\n bufferMediaThermalSlowdownModeled:\n false\n };\n\n return deepFreeze(\n cloneJson(sequence)\n );\n}\n\n/**\n * Returns a cancelled copy of a sequence. The original scheduled sequence is not\n * changed, and it is always clear which frames were completed and which were dropped.\n */\nexport function createCancelledReleaseSequence(\n input:\n CancelReleaseSequenceInput\n): CancelledReleaseSequence {\n requireInputKeys(input, \"createCancelledReleaseSequence\", [\"sequence\", \"completedFrameCount\", \"cancelledAtSeconds\"]);\n const count =\n requireNonNegativeFinite(\n input.completedFrameCount,\n \"completedFrameCount\"\n );\n if (\n !Number.isSafeInteger(count)\n ) {\n throw new InvalidScientificInputError(\n \"completedFrameCount must be a safe integer.\"\n );\n }\n if (\n count >\n input.sequence.frames.length\n ) {\n throw new InvalidScientificInputError(\n \"completedFrameCount must not exceed the source sequence frame count.\"\n );\n }\n\n const cancelledAt =\n requireFinite(\n input.cancelledAtSeconds,\n \"cancelledAtSeconds\"\n );\n\n const result:\n CancelledReleaseSequence = {\n version:\n RELEASE_SEQUENCE_VERSION,\n sequenceId:\n input.sequence.sequenceId,\n status: \"cancelled\",\n cancelledAtSeconds:\n cancelledAt,\n completedFrameCount: count,\n omittedFrameCount:\n input.sequence.frames.length -\n count,\n completedFrames:\n cloneJson(\n input.sequence.frames.slice(\n 0,\n count\n )\n ),\n sourceSequenceFrameCount:\n input.sequence.frames.length,\n sourceSequenceUnmodified:\n true\n };\n\n return deepFreeze(\n cloneJson(result)\n );\n}\n"]}
@@ -159,6 +159,7 @@ export interface CreateLockedWhiteBalanceStateInput {
159
159
  stateId: string;
160
160
  sourceState: ResolvedWhiteBalanceState;
161
161
  }
162
+ export declare function requireFraction(value: unknown, path: string): number;
162
163
  /**
163
164
  * Parses generic WB presets and AWB policies.
164
165
  *
@@ -166,6 +167,7 @@ export interface CreateLockedWhiteBalanceStateInput {
166
167
  * treated as a universal Kelvin value or as any manufacturer's behaviour.
167
168
  */
168
169
  export declare function parseWhiteBalanceProfile(value: unknown): WhiteBalanceProfile;
170
+ export declare function requireScientificFinite(value: number, path: string): number;
169
171
  /** Resolves a WB preset defined by the profile. No universal label-to-Kelvin rule is applied. */
170
172
  export declare function resolvePresetWhiteBalance(input: ResolvePresetWhiteBalanceInput): ResolvedWhiteBalanceState;
171
173
  /** Resolves explicit manual camera-linear channel gains. */
@@ -11,42 +11,13 @@
11
11
  import { InvalidConfigurationError } from "../core/configuration-error.js";
12
12
  import { parseEvidenceList } from "../core/evidence-provenance.js";
13
13
  import { InvalidScientificInputError } from "../core/validation.js";
14
- import { requireInputObject, requireInputKeys, rejectUnknownFields } from "../core/record-validation.js";
14
+ import { requireInputObject, requireInputKeys, requireConfigRecord as requireRecord, requireTrimmedConfigString as requireNonEmptyString, requireTrimmedConfigString as requireScientificNonEmptyString, requireConfigFinite as requireFinite, requireConfigNonNegativeSafeInteger as requireNonNegativeSafeInteger, parseConfigLimitations as parseLimitations, requireConfigFiniteAboveZero as requirePositiveFinite } from "../core/record-validation.js";
15
15
  import { calculateChromaticityFromCorrelatedColorTemperature, calculatePortableChromaticityFromCorrelatedColorTemperature } from "./illuminants.js";
16
16
  import { multiplyMatrixVector, parseMatrix3x3, xyzOfChromaticity } from "./colorimetry-math.js";
17
17
  export const WHITE_BALANCE_PROFILE_SCHEMA_VERSION = "0.1.0";
18
18
  export const WHITE_BALANCE_STATE_VERSION = "0.1.0";
19
19
  export const COLOR_TEMPERATURE_WHITE_BALANCE_INTENT_VERSION = "0.1.0";
20
- function requireRecord(value, path, allowedKeys) {
21
- if (typeof value !== "object" ||
22
- value === null ||
23
- Array.isArray(value)) {
24
- throw new InvalidConfigurationError(path + " must be an object.");
25
- }
26
- if (allowedKeys !== null)
27
- rejectUnknownFields(value, allowedKeys, path);
28
- return value;
29
- }
30
- function requireNonEmptyString(value, path) {
31
- if (typeof value !== "string" || value.trim().length === 0) {
32
- throw new InvalidConfigurationError(path + " must be a non-empty string.");
33
- }
34
- return value.trim();
35
- }
36
- function requireFinite(value, path) {
37
- if (typeof value !== "number" || !Number.isFinite(value)) {
38
- throw new InvalidConfigurationError(path + " must be finite.");
39
- }
40
- return value;
41
- }
42
- function requirePositiveFinite(value, path) {
43
- const parsed = requireFinite(value, path);
44
- if (parsed <= 0) {
45
- throw new InvalidConfigurationError(path + " must be greater than zero.");
46
- }
47
- return parsed;
48
- }
49
- function requireFraction(value, path) {
20
+ export function requireFraction(value, path) {
50
21
  const parsed = requireFinite(value, path);
51
22
  if (parsed < 0 || parsed > 1) {
52
23
  throw new InvalidConfigurationError(path + " must be from zero through one.");
@@ -61,16 +32,6 @@ function parseChannelGains(value, path) {
61
32
  blue: requirePositiveFinite(record.blue, path + ".blue")
62
33
  };
63
34
  }
64
- function parseLimitations(value, path) {
65
- if (!Array.isArray(value)) {
66
- throw new InvalidConfigurationError(path + " must be an array.");
67
- }
68
- const parsed = value.map((entry, index) => requireNonEmptyString(entry, path + "[" + index + "]"));
69
- if (new Set(parsed).size !== parsed.length) {
70
- throw new InvalidConfigurationError(path + " must not contain duplicates.");
71
- }
72
- return parsed;
73
- }
74
35
  function parseOptionalLimitations(value, path) {
75
36
  return value === undefined ? undefined : parseLimitations(value, path);
76
37
  }
@@ -171,13 +132,7 @@ export function parseWhiteBalanceProfile(value) {
171
132
  limitations: parseLimitations(record.limitations, "whiteBalanceProfile.limitations")
172
133
  };
173
134
  }
174
- function requireScientificNonEmptyString(value, path) {
175
- if (typeof value !== "string" || value.trim().length === 0) {
176
- throw new InvalidConfigurationError(path + " must be a non-empty string.");
177
- }
178
- return value.trim();
179
- }
180
- function requireScientificFinite(value, path) {
135
+ export function requireScientificFinite(value, path) {
181
136
  if (!Number.isFinite(value)) {
182
137
  throw new InvalidScientificInputError(path + " must be finite.");
183
138
  }
@@ -530,14 +485,6 @@ export function createLockedWhiteBalanceState(input) {
530
485
  limitations: [...source.limitations]
531
486
  });
532
487
  }
533
- function requireNonNegativeSafeInteger(value, path) {
534
- if (typeof value !== "number" ||
535
- !Number.isSafeInteger(value) ||
536
- value < 0) {
537
- throw new InvalidConfigurationError(path + " must be a non-negative safe integer.");
538
- }
539
- return value;
540
- }
541
488
  function requirePositiveSafeInteger(value, path) {
542
489
  const parsed = requireNonNegativeSafeInteger(value, path);
543
490
  if (parsed <= 0) {