@photivra/engine 2.18.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 (261) hide show
  1. package/CHANGELOG.md +68 -1
  2. package/CITATION.cff +1 -1
  3. package/README.md +6 -6
  4. package/SECURITY.md +1 -1
  5. package/dist/capture/focus-control.js +2 -75
  6. package/dist/capture/focus-control.js.map +1 -1
  7. package/dist/capture/native-environment-lean.d.ts +1 -0
  8. package/dist/capture/native-environment-lean.js +1 -1
  9. package/dist/capture/native-environment-lean.js.map +1 -1
  10. package/dist/capture/native-environment-optical-stage.d.ts +1 -0
  11. package/dist/capture/native-environment-optical-stage.js +2 -5
  12. package/dist/capture/native-environment-optical-stage.js.map +1 -1
  13. package/dist/capture/native-environment-raw.js +1 -5
  14. package/dist/capture/native-environment-raw.js.map +1 -1
  15. package/dist/capture/release-sequence.js +2 -47
  16. package/dist/capture/release-sequence.js.map +1 -1
  17. package/dist/color/white-balance.d.ts +2 -0
  18. package/dist/color/white-balance.js +3 -56
  19. package/dist/color/white-balance.js.map +1 -1
  20. package/dist/composition/image-formation-plan.d.ts +1 -0
  21. package/dist/composition/image-formation-plan.js +3 -49
  22. package/dist/composition/image-formation-plan.js.map +1 -1
  23. package/dist/core/evidence-provenance.js +1 -16
  24. package/dist/core/evidence-provenance.js.map +1 -1
  25. package/dist/core/record-validation.d.ts +54 -0
  26. package/dist/core/record-validation.js +176 -0
  27. package/dist/core/record-validation.js.map +1 -1
  28. package/dist/core/scientific-assurance.js +1 -18
  29. package/dist/core/scientific-assurance.js.map +1 -1
  30. package/dist/core/shared-helpers.d.ts +33 -0
  31. package/dist/core/shared-helpers.js +34 -0
  32. package/dist/core/shared-helpers.js.map +1 -0
  33. package/dist/core/spectral-composition.js +1 -36
  34. package/dist/core/spectral-composition.js.map +1 -1
  35. package/dist/core/spectral.js +1 -19
  36. package/dist/core/spectral.js.map +1 -1
  37. package/dist/core/version.d.ts +1 -1
  38. package/dist/core/version.js +1 -1
  39. package/dist/core/version.js.map +1 -1
  40. package/dist/equipment/exposure-capabilities.d.ts +1 -0
  41. package/dist/equipment/exposure-capabilities.js +2 -36
  42. package/dist/equipment/exposure-capabilities.js.map +1 -1
  43. package/dist/equipment/iso-capabilities.js +2 -49
  44. package/dist/equipment/iso-capabilities.js.map +1 -1
  45. package/dist/equipment/metering-capabilities.js +1 -26
  46. package/dist/equipment/metering-capabilities.js.map +1 -1
  47. package/dist/equipment/release-capabilities.js +1 -53
  48. package/dist/equipment/release-capabilities.js.map +1 -1
  49. package/dist/exposure/auto-iso-slowest-shutter.js +1 -7
  50. package/dist/exposure/auto-iso-slowest-shutter.js.map +1 -1
  51. package/dist/exposure/exposure-duration-control.js +1 -16
  52. package/dist/exposure/exposure-duration-control.js.map +1 -1
  53. package/dist/exposure/exposure-mode-resolver.js +1 -17
  54. package/dist/exposure/exposure-mode-resolver.js.map +1 -1
  55. package/dist/exposure/filtered-exposure-time.d.ts +93 -0
  56. package/dist/exposure/filtered-exposure-time.js +129 -0
  57. package/dist/exposure/filtered-exposure-time.js.map +1 -0
  58. package/dist/exposure/flash-ttl.d.ts +1 -0
  59. package/dist/exposure/flash-ttl.js +1 -1
  60. package/dist/exposure/flash-ttl.js.map +1 -1
  61. package/dist/exposure/flash.js +1 -49
  62. package/dist/exposure/flash.js.map +1 -1
  63. package/dist/exposure/metering-scene-radiance.js +1 -25
  64. package/dist/exposure/metering-scene-radiance.js.map +1 -1
  65. package/dist/exposure/metering-target.js +1 -30
  66. package/dist/exposure/metering-target.js.map +1 -1
  67. package/dist/exposure/metering-temporal.js +1 -8
  68. package/dist/exposure/metering-temporal.js.map +1 -1
  69. package/dist/exposure/metering.js +1 -72
  70. package/dist/exposure/metering.js.map +1 -1
  71. package/dist/exposure/program-line.js +1 -45
  72. package/dist/exposure/program-line.js.map +1 -1
  73. package/dist/facade/develop-joined-parts.d.ts +29 -0
  74. package/dist/facade/develop-joined-parts.js +33 -0
  75. package/dist/facade/develop-joined-parts.js.map +1 -0
  76. package/dist/facade/generic-lens-capture.d.ts +4 -2
  77. package/dist/facade/generic-lens-capture.js +4 -2
  78. package/dist/facade/generic-lens-capture.js.map +1 -1
  79. package/dist/facade/simulate-capture.d.ts +37 -5
  80. package/dist/facade/simulate-capture.js +153 -96
  81. package/dist/facade/simulate-capture.js.map +1 -1
  82. package/dist/gpu/chart-composite-gpu.js +1 -3
  83. package/dist/gpu/chart-composite-gpu.js.map +1 -1
  84. package/dist/gpu/chart-radiance-gpu.js +1 -5
  85. package/dist/gpu/chart-radiance-gpu.js.map +1 -1
  86. package/dist/gpu/display-view-quality-gpu.d.ts +13 -1
  87. package/dist/gpu/display-view-quality-gpu.js +4 -8
  88. package/dist/gpu/display-view-quality-gpu.js.map +1 -1
  89. package/dist/gpu/display-view-speed-gpu.js +3 -25
  90. package/dist/gpu/display-view-speed-gpu.js.map +1 -1
  91. package/dist/gpu/sensor-stage-gpu.js +1 -5
  92. package/dist/gpu/sensor-stage-gpu.js.map +1 -1
  93. package/dist/gpu/webgpu-runtime.d.ts +2 -0
  94. package/dist/gpu/webgpu-runtime.js +3 -0
  95. package/dist/gpu/webgpu-runtime.js.map +1 -1
  96. package/dist/index.d.ts +4 -1
  97. package/dist/index.js +3 -0
  98. package/dist/index.js.map +1 -1
  99. package/dist/motion/camera-rotation.js +1 -6
  100. package/dist/motion/camera-rotation.js.map +1 -1
  101. package/dist/motion/capture-rotation-inverse-mapping.d.ts +12 -0
  102. package/dist/motion/capture-rotation-inverse-mapping.js +2 -2
  103. package/dist/motion/capture-rotation-inverse-mapping.js.map +1 -1
  104. package/dist/motion/capture-rotation-temporal-quadrature.d.ts +1 -0
  105. package/dist/motion/capture-rotation-temporal-quadrature.js +1 -1
  106. package/dist/motion/capture-rotation-temporal-quadrature.js.map +1 -1
  107. package/dist/motion/capture-rotation-trajectory.js +2 -17
  108. package/dist/motion/capture-rotation-trajectory.js.map +1 -1
  109. package/dist/motion/capture-translation-parallax-temporal-quadrature.d.ts +1 -0
  110. package/dist/motion/capture-translation-parallax-temporal-quadrature.js +3 -14
  111. package/dist/motion/capture-translation-parallax-temporal-quadrature.js.map +1 -1
  112. package/dist/motion/extended-object-projection.js +5 -40
  113. package/dist/motion/extended-object-projection.js.map +1 -1
  114. package/dist/optics/atmospheric-haze.js +1 -7
  115. package/dist/optics/atmospheric-haze.js.map +1 -1
  116. package/dist/optics/brown-conrady-distortion.js +1 -7
  117. package/dist/optics/brown-conrady-distortion.js.map +1 -1
  118. package/dist/optics/complex-pupil-psf.d.ts +1 -0
  119. package/dist/optics/complex-pupil-psf.js +2 -84
  120. package/dist/optics/complex-pupil-psf.js.map +1 -1
  121. package/dist/optics/focus-state.js +1 -11
  122. package/dist/optics/focus-state.js.map +1 -1
  123. package/dist/optics/front-of-lens-filter.d.ts +5 -0
  124. package/dist/optics/front-of-lens-filter.js +3 -65
  125. package/dist/optics/front-of-lens-filter.js.map +1 -1
  126. package/dist/optics/generic-nd-filter-presets.d.ts +12 -0
  127. package/dist/optics/generic-nd-filter-presets.js +48 -0
  128. package/dist/optics/generic-nd-filter-presets.js.map +1 -0
  129. package/dist/optics/illumination-vignetting.js +1 -6
  130. package/dist/optics/illumination-vignetting.js.map +1 -1
  131. package/dist/optics/lens-psf-profile.d.ts +4 -1
  132. package/dist/optics/lens-psf-profile.js +12 -111
  133. package/dist/optics/lens-psf-profile.js.map +1 -1
  134. package/dist/optics/longitudinal-chromatic-aberration.js +1 -7
  135. package/dist/optics/longitudinal-chromatic-aberration.js.map +1 -1
  136. package/dist/optics/pupil-vignetting.js +1 -7
  137. package/dist/optics/pupil-vignetting.js.map +1 -1
  138. package/dist/optics/radial-distortion.js +1 -6
  139. package/dist/optics/radial-distortion.js.map +1 -1
  140. package/dist/optics/scene-to-sensor-irradiance.js +4 -96
  141. package/dist/optics/scene-to-sensor-irradiance.js.map +1 -1
  142. package/dist/optics/stray-light.d.ts +1 -0
  143. package/dist/optics/stray-light.js +1 -1
  144. package/dist/optics/stray-light.js.map +1 -1
  145. package/dist/optics/zernike-wavefront.js +1 -7
  146. package/dist/optics/zernike-wavefront.js.map +1 -1
  147. package/dist/output/capture-geometry.js +1 -8
  148. package/dist/output/capture-geometry.js.map +1 -1
  149. package/dist/output/developed-lens-corrections.d.ts +62 -0
  150. package/dist/output/developed-lens-corrections.js +135 -0
  151. package/dist/output/developed-lens-corrections.js.map +1 -0
  152. package/dist/output/generic-lens-corrections.d.ts +2 -6
  153. package/dist/output/generic-lens-corrections.js +3 -6
  154. package/dist/output/generic-lens-corrections.js.map +1 -1
  155. package/dist/output/geometric-transforms.js +1 -4
  156. package/dist/output/geometric-transforms.js.map +1 -1
  157. package/dist/output/native-capture-sdr.d.ts +2 -2
  158. package/dist/output/native-capture-sdr.js +3 -2
  159. package/dist/output/native-capture-sdr.js.map +1 -1
  160. package/dist/processing/development-detail.d.ts +29 -0
  161. package/dist/processing/development-detail.js +128 -107
  162. package/dist/processing/development-detail.js.map +1 -1
  163. package/dist/schema/illumination-temporal.js +3 -72
  164. package/dist/schema/illumination-temporal.js.map +1 -1
  165. package/dist/schema/illumination.d.ts +1 -0
  166. package/dist/schema/illumination.js +2 -65
  167. package/dist/schema/illumination.js.map +1 -1
  168. package/dist/schema/scene-radiance-temporal.js +2 -14
  169. package/dist/schema/scene-radiance-temporal.js.map +1 -1
  170. package/dist/schema/scene-radiance.js +2 -60
  171. package/dist/schema/scene-radiance.js.map +1 -1
  172. package/dist/schema/validation.d.ts +2 -0
  173. package/dist/schema/validation.js +2 -17
  174. package/dist/schema/validation.js.map +1 -1
  175. package/dist/sensor/accumulated-charge.js +2 -59
  176. package/dist/sensor/accumulated-charge.js.map +1 -1
  177. package/dist/sensor/architecture.js +1 -16
  178. package/dist/sensor/architecture.js.map +1 -1
  179. package/dist/sensor/camera-saturation-capacity.d.ts +1 -0
  180. package/dist/sensor/camera-saturation-capacity.js +3 -55
  181. package/dist/sensor/camera-saturation-capacity.js.map +1 -1
  182. package/dist/sensor/capture-color-sampling-binding.js +1 -26
  183. package/dist/sensor/capture-color-sampling-binding.js.map +1 -1
  184. package/dist/sensor/capture-mode-timing.js +1 -39
  185. package/dist/sensor/capture-mode-timing.js.map +1 -1
  186. package/dist/sensor/capture-mode.js +1 -23
  187. package/dist/sensor/capture-mode.js.map +1 -1
  188. package/dist/sensor/color-sampling.js +1 -26
  189. package/dist/sensor/color-sampling.js.map +1 -1
  190. package/dist/sensor/constant-rate-temporal-integration.js +2 -46
  191. package/dist/sensor/constant-rate-temporal-integration.js.map +1 -1
  192. package/dist/sensor/dark-current.js +2 -45
  193. package/dist/sensor/dark-current.js.map +1 -1
  194. package/dist/sensor/eqe-electron-rate.js +2 -35
  195. package/dist/sensor/eqe-electron-rate.js.map +1 -1
  196. package/dist/sensor/exposure-window.d.ts +5 -0
  197. package/dist/sensor/exposure-window.js +7 -22
  198. package/dist/sensor/exposure-window.js.map +1 -1
  199. package/dist/sensor/iso-gain.js +1 -7
  200. package/dist/sensor/iso-gain.js.map +1 -1
  201. package/dist/sensor/iso-signal-chain.js +1 -43
  202. package/dist/sensor/iso-signal-chain.js.map +1 -1
  203. package/dist/sensor/optical-stack.js +1 -25
  204. package/dist/sensor/optical-stack.js.map +1 -1
  205. package/dist/sensor/photon-energy.js +1 -26
  206. package/dist/sensor/photon-energy.js.map +1 -1
  207. package/dist/sensor/physical-charge-capacity.js +3 -77
  208. package/dist/sensor/physical-charge-capacity.js.map +1 -1
  209. package/dist/sensor/radiometry-readiness.js +2 -23
  210. package/dist/sensor/radiometry-readiness.js.map +1 -1
  211. package/dist/sensor/raw-readout.d.ts +3 -0
  212. package/dist/sensor/raw-readout.js +4 -66
  213. package/dist/sensor/raw-readout.js.map +1 -1
  214. package/dist/sensor/raw-reconstruction.js +2 -64
  215. package/dist/sensor/raw-reconstruction.js.map +1 -1
  216. package/dist/sensor/readout-exposure-linkage.js +1 -9
  217. package/dist/sensor/readout-exposure-linkage.js.map +1 -1
  218. package/dist/sensor/readout-timing.js +3 -57
  219. package/dist/sensor/readout-timing.js.map +1 -1
  220. package/dist/sensor/response-application-compatibility.d.ts +2 -0
  221. package/dist/sensor/response-application-compatibility.js +3 -29
  222. package/dist/sensor/response-application-compatibility.js.map +1 -1
  223. package/dist/sensor/response-operating-range.d.ts +1 -0
  224. package/dist/sensor/response-operating-range.js +3 -79
  225. package/dist/sensor/response-operating-range.js.map +1 -1
  226. package/dist/sensor/responsivity-photocurrent.js +4 -108
  227. package/dist/sensor/responsivity-photocurrent.js.map +1 -1
  228. package/dist/sensor/sampling-aperture.js +1 -25
  229. package/dist/sensor/sampling-aperture.js.map +1 -1
  230. package/dist/sensor/spatial-sample-reduction.js +2 -19
  231. package/dist/sensor/spatial-sample-reduction.js.map +1 -1
  232. package/dist/sensor/spatial-sampling-quadrature.js +1 -3
  233. package/dist/sensor/spatial-sampling-quadrature.js.map +1 -1
  234. package/dist/sensor/spatio-spectral-reduction.js +1 -18
  235. package/dist/sensor/spatio-spectral-reduction.js.map +1 -1
  236. package/dist/sensor/spectral-coverage.d.ts +10 -1
  237. package/dist/sensor/spectral-coverage.js +1 -1
  238. package/dist/sensor/spectral-coverage.js.map +1 -1
  239. package/dist/sensor/spectral-quadrature.js +2 -35
  240. package/dist/sensor/spectral-quadrature.js.map +1 -1
  241. package/dist/sensor/spectral-response.js +1 -47
  242. package/dist/sensor/spectral-response.js.map +1 -1
  243. package/dist/stabilization/camera-shake.js +1 -6
  244. package/dist/stabilization/camera-shake.js.map +1 -1
  245. package/dist/stabilization/system.js +2 -80
  246. package/dist/stabilization/system.js.map +1 -1
  247. package/docs/AGENT_GUIDE.md +1 -0
  248. package/docs/API_REFERENCE.md +41 -2
  249. package/docs/QUICK_START.md +90 -8
  250. package/docs/USAGE.md +47 -2
  251. package/docs/api/core-version.md +1 -1
  252. package/docs/api/exports.json +53 -3
  253. package/docs/api/exposure-filtered-exposure-time.md +105 -0
  254. package/docs/api/facade-develop-joined-parts.md +32 -0
  255. package/docs/api/facade-simulate-capture.md +36 -5
  256. package/docs/api/optics-generic-nd-filter-presets.md +34 -0
  257. package/docs/api/optics-lens-psf-profile.md +3 -1
  258. package/docs/api/output-developed-lens-corrections.md +12 -0
  259. package/docs/api/output-generic-lens-corrections.md +0 -9
  260. package/llms.txt +1 -0
  261. package/package.json +1 -1
package/docs/USAGE.md CHANGED
@@ -374,7 +374,7 @@ The actual focal length stays the physical optical quantity used by projection a
374
374
  - Focus distance is not part of conventional 35 mm-equivalent focal length. A later digital/output crop and the output resolution are also excluded.
375
375
  - If a product wants to describe the final digital framing, it should label it as output framing. It should not silently replace the optical focal length or the capture-equivalent focal length.
376
376
 
377
- To render a lens by its physical focal length on any sensor, give `simulateCapture()` or the display view `lens.ideal: { focalLengthMm }` with any `lens.fNumber` within `IDEAL_LENS_LIMITS` (f/0.5–f/128, 1–4000 mm; optional `widestFNumber` and `narrowestFNumber` narrow the f-number range for the automatic exposure modes): an aberration-free thin lens that is not scaled by the crop factor, unlike `lens.prime` (see docs/QUICK_START.md, "Ideal lens at any focal length and f-number").
377
+ To render a lens by its physical focal length on any sensor, give `simulateCapture()` or the display view `lens.ideal: { focalLengthMm }` with any `lens.fNumber` within `IDEAL_LENS_LIMITS` (f/0.5–f/128, 1–4000 mm; optional `widestFNumber` and `narrowestFNumber` narrow the f-number range for the automatic exposure modes; in program the engine's program line is limited to that range, #583): an aberration-free thin lens that is not scaled by the crop factor, unlike `lens.prime` (see docs/QUICK_START.md, "Ideal lens at any focal length and f-number").
378
378
 
379
379
  `calculateImagingAreaMetrics()` provides the same canonical diagonal crop-factor calculation, independent of raster density, so other engine modules do not need to repeat that formula.
380
380
 
@@ -1058,6 +1058,30 @@ T_stack = T_1 * T_2 * ... * T_n
1058
1058
 
1059
1059
  The stack keeps each filter's identity and evidence. The combined numeric uncertainty is deliberately reported as **not propagated**, unless it is established whether the filters' errors are correlated or independent. Each filter's own uncertainty stays attached to that filter.
1060
1060
 
1061
+ ### Exposure time with an ND filter
1062
+
1063
+ `calculateFilteredExposureTime({ shutterSeconds, filter, shutterDialSeconds? })` (model `filtered-exposure-time` 1.0.0, #578) gives the shutter time that keeps the same exposure at the same f-number and ISO after adding one filter or a stack (`filter` takes one profile or an array in light-path order): `filteredShutterSeconds = shutterSeconds / T`, with `T` the stack's combined transmittance from `composeFrontOfLensFilterTransmission()`. It also returns `attenuationStops`, `exposureFactor` (`1/T`) and the full `filterTransmission`. With a `shutterDialSeconds` list (for example `GENERIC_THIRD_STOP_SHUTTER_SECONDS`) it adds `dial`: the nearest dial value in stops (the shorter on a tie), its exposure error in stops, and `clamped: "maximum"` when the exact time is longer than the longest dial value (use bulb).
1064
+
1065
+ - Exact under the linear sensor model (exposure `H = E·t`); film reciprocity failure is not modelled.
1066
+ - Nominal filters vary: a "10-stop" filter (`T = 2^-10`) gives 1/125 s → 8.19 s, an ND1000 (OD 3.0, 9.97 stops) gives exactly 8 s. The result is only as good as the profile, and is an approximation whenever a profile is.
1067
+ - Generic presets: `GENERIC_ND_FILTER_PROFILES[3]`, `[6]` and `[10]` (keys in `GENERIC_ND_FILTER_STOPS`) are frozen, unbranded, spectrally flat profiles over 380–780 nm with a transmittance of exactly 2^-N (1/8, 1/64, 1/1024). They are idealized (`scientificStatus: "approximation"`, uncertainty not quantified), and any function that takes a front-of-lens filter profile accepts them. For other filters, build a profile with `parseFrontOfLensFilterProfile()`.
1068
+ - Filters are resolved at 550 nm unless `wavelengthNanometers` is given; neutral filters do not depend on it. `incidentPolarization` matters only with a polarizer in the stack.
1069
+
1070
+ ```ts
1071
+ import {
1072
+ GENERIC_ND_FILTER_PROFILES,
1073
+ GENERIC_THIRD_STOP_SHUTTER_SECONDS,
1074
+ calculateFilteredExposureTime
1075
+ } from "@photivra/engine";
1076
+
1077
+ const { value } = calculateFilteredExposureTime({
1078
+ shutterSeconds: 1 / 125,
1079
+ filter: GENERIC_ND_FILTER_PROFILES[10],
1080
+ shutterDialSeconds: GENERIC_THIRD_STOP_SHUTTER_SECONDS
1081
+ });
1082
+ console.log(value.filteredShutterSeconds.toFixed(2), value.dial?.shutterSeconds); // 8.19 8
1083
+ ```
1084
+
1061
1085
  ### Spectral boundary
1062
1086
 
1063
1087
  Spectral filter data stays wavelength-resolved all the way through the [scene-radiance-to-sensor bridge](#scene-radiance-to-sensor-irradiance). Photivra does not turn a spectral filter into an undocumented RGB tint.
@@ -5527,6 +5551,20 @@ All modes:
5527
5551
 
5528
5552
  Flash-aware behaviour, safety shift and the program behaviour of named cameras are separate work.
5529
5553
 
5554
+ ### The capture facade's program line and the lens's f-number range (#583)
5555
+
5556
+ `simulateCapture()` and the display view in `exposure.mode: "program"` use an engine-authored generic line (f/16 1/2000 s … f/8
5557
+ 1/125 s … f/4 1.024 s, assumption `exposure.programLine`). `resolveProgramAutoExposureMode()` refuses a line whose apertures leave
5558
+ the capability range, so with a lens range that does not hold f/4–f/16 (`lens.ideal` `widestFNumber`/`narrowestFNumber`,
5559
+ `lens.prime`, `lens.catalogue`) the facade limits the line to the range first: the line's f/4 and f/16 are clamped to the range, giving the same shape with
5560
+ the aperture held at the range's end and the shutter keeping each point's exposure (t/N², camera equation H = 0.65·L·t/N²),
5561
+ still from 1/2000 s to 1.024 s. Where both lines run, the limited line's aperture is the old one clamped in log2 to the range.
5562
+ Beyond the line's ends the resolver holds them as before. A range that holds f/4–f/16, or no range, keeps the line and its
5563
+ bytes. The limited line is recorded as its profile (id `"facade-generic-program-line-lens-range"`), with node stops measured from the
5564
+ capture's exposure anchor (f/8, 1/125 s; the same N²/t at the range's aperture nearest f/8; or, #585, that aperture at the
5565
+ body's shortest shutter when the same N²/t would need a shorter one). An approximation of a generic
5566
+ program, not a manufacturer's.
5567
+
5530
5568
  ## Program Auto and Full Auto exposure
5531
5569
 
5532
5570
  Program Auto and Full Auto exposure both need a declared, versioned **program line**: a curve that says which aperture and shutter to use at each brightness level. The engine does not invent one "correct" aperture/shutter pair.
@@ -7480,7 +7518,8 @@ own designs, not real products). `resolveGenericLens` resolves one for a sensor,
7480
7518
  (scaled by the crop factor), aperture range, close focus, projection and its character, Seidel aberrations, axial and lateral
7481
7519
  colour, distortion and vignetting profiles, flare, breathing and focus scatter, each a labelled approximation.
7482
7520
  `simulateGenericLensFocusError` draws a seeded autofocus error; `createGenericLensCorrectionProfile` builds the lens's generic
7483
- correction profile for `resolveLensCorrectionPlan`. `simulateCapture` renders a catalogue lens with `lens.catalogue`
7521
+ correction profile for `resolveLensCorrectionPlan`, which `simulateCapture` applies to its developed image with
7522
+ `output.lensCorrections` (#570; never to the RAW or the DNG). `simulateCapture` renders a catalogue lens with `lens.catalogue`
7484
7523
  ([quick start](QUICK_START.md#generic-lens-catalogue-549)); `fast: true` (or `lens: { catalogue: { kind, fast: true } }`) takes its
7485
7524
  fast method (#568), a labeled approximation that blends the PSF over a field grid, for interactive captures. Equations, tables and the
7486
7525
  chart measurements: [LENS_CATALOGUE.md](https://github.com/Photivra/photivra/blob/main/docs/LENS_CATALOGUE.md).
@@ -7742,6 +7781,12 @@ console.log(star.assumptions.filter((a) => a.kind === "default").map((a) => a.id
7742
7781
  console.log(star.request.siteInput(0).environment.optics.nominalFNumber);
7743
7782
  ```
7744
7783
 
7784
+ A capture can render its RAW in parallel parts (`output.region`, one call per part). `serializeSimulatedCaptureDngFromParts()`
7785
+ writes the whole frame's DNG from the parts, and `simulateCaptureFromParts()` (#571) develops the whole frame from them,
7786
+ including the whole-image stages `output.diffractionCorrection` and `output.sharpening` that a region refuses, with the same
7787
+ bytes as a whole-frame `simulateCapture()` and per-row progress (`diffraction-correction`, `sharpening`). See
7788
+ [Quick start, Developing a capture rendered in parts](QUICK_START.md#developing-a-capture-rendered-in-parts-571).
7789
+
7745
7790
  ### Color charts (2.14)
7746
7791
 
7747
7792
  The engine's own [color patches](https://github.com/Photivra/photivra/blob/main/docs/COLOR_PATCHES.md) are reflectance spectra lit by the capture's illuminant. Draw the
@@ -11,5 +11,5 @@ version they were made with. Proof-of-concept (POC), schema and model contracts
11
11
  own versions. Release checks confirm it matches package.json and the packed package.
12
12
 
13
13
  ```ts
14
- ENGINE_API_VERSION = "2.18.0" as const
14
+ ENGINE_API_VERSION = "2.19.0" as const
15
15
  ```
@@ -1,6 +1,6 @@
1
1
  {
2
- "packageVersion": "2.18.0",
3
- "engineApiVersion": "2.18.0",
2
+ "packageVersion": "2.19.0",
3
+ "engineApiVersion": "2.19.0",
4
4
  "exports": [
5
5
  {
6
6
  "name": "AbsoluteUncertaintyEstimate",
@@ -732,6 +732,16 @@
732
732
  "source": "src/camera/field-of-view.ts",
733
733
  "kind": "type"
734
734
  },
735
+ {
736
+ "name": "calculateFilteredExposureTime",
737
+ "source": "src/exposure/filtered-exposure-time.ts",
738
+ "kind": "runtime"
739
+ },
740
+ {
741
+ "name": "CalculateFilteredExposureTimeInput",
742
+ "source": "src/exposure/filtered-exposure-time.ts",
743
+ "kind": "type"
744
+ },
735
745
  {
736
746
  "name": "calculateFlashGuideNumber",
737
747
  "source": "src/exposure/flash-ttl.ts",
@@ -3317,6 +3327,21 @@
3317
3327
  "source": "src/camera/field-of-view.ts",
3318
3328
  "kind": "type"
3319
3329
  },
3330
+ {
3331
+ "name": "FILTERED_EXPOSURE_DEFAULT_WAVELENGTH_NANOMETERS",
3332
+ "source": "src/exposure/filtered-exposure-time.ts",
3333
+ "kind": "runtime"
3334
+ },
3335
+ {
3336
+ "name": "FilteredExposureDialSetting",
3337
+ "source": "src/exposure/filtered-exposure-time.ts",
3338
+ "kind": "type"
3339
+ },
3340
+ {
3341
+ "name": "FilteredExposureTime",
3342
+ "source": "src/exposure/filtered-exposure-time.ts",
3343
+ "kind": "type"
3344
+ },
3320
3345
  {
3321
3346
  "name": "FixedMultiFrameCaptureAcquisition",
3322
3347
  "source": "src/sensor/capture-mode.ts",
@@ -3584,7 +3609,7 @@
3584
3609
  },
3585
3610
  {
3586
3611
  "name": "GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM",
3587
- "source": "src/output/generic-lens-corrections.ts",
3612
+ "source": "src/output/developed-lens-corrections.ts",
3588
3613
  "kind": "runtime"
3589
3614
  },
3590
3615
  {
@@ -3597,6 +3622,16 @@
3597
3622
  "source": "src/equipment/generic-lens-catalogue.ts",
3598
3623
  "kind": "runtime"
3599
3624
  },
3625
+ {
3626
+ "name": "GENERIC_ND_FILTER_PROFILES",
3627
+ "source": "src/optics/generic-nd-filter-presets.ts",
3628
+ "kind": "runtime"
3629
+ },
3630
+ {
3631
+ "name": "GENERIC_ND_FILTER_STOPS",
3632
+ "source": "src/optics/generic-nd-filter-presets.ts",
3633
+ "kind": "runtime"
3634
+ },
3600
3635
  {
3601
3636
  "name": "GENERIC_PRIME_EQUIVALENT_FOCAL_LENGTHS_MM",
3602
3637
  "source": "src/equipment/generic-prime-lenses.ts",
@@ -3747,6 +3782,11 @@
3747
3782
  "source": "src/equipment/generic-lens-catalogue.ts",
3748
3783
  "kind": "type"
3749
3784
  },
3785
+ {
3786
+ "name": "GenericNdFilterStops",
3787
+ "source": "src/optics/generic-nd-filter-presets.ts",
3788
+ "kind": "type"
3789
+ },
3750
3790
  {
3751
3791
  "name": "GenericOpticalEvidence",
3752
3792
  "source": "src/optics/profile-contract.ts",
@@ -8572,11 +8612,21 @@
8572
8612
  "source": "src/facade/simulate-capture.ts",
8573
8613
  "kind": "type"
8574
8614
  },
8615
+ {
8616
+ "name": "simulateCaptureFromParts",
8617
+ "source": "src/facade/develop-joined-parts.ts",
8618
+ "kind": "runtime"
8619
+ },
8575
8620
  {
8576
8621
  "name": "SimulateCaptureLensCatalogueRecord",
8577
8622
  "source": "src/facade/simulate-capture.ts",
8578
8623
  "kind": "type"
8579
8624
  },
8625
+ {
8626
+ "name": "SimulateCaptureLensCorrections",
8627
+ "source": "src/facade/simulate-capture.ts",
8628
+ "kind": "type"
8629
+ },
8580
8630
  {
8581
8631
  "name": "SimulateCaptureLensDistortion",
8582
8632
  "source": "src/facade/simulate-capture.ts",
@@ -0,0 +1,105 @@
1
+ # exposure/filtered-exposure-time.ts public contracts
2
+
3
+ The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
4
+
5
+ ## calculateFilteredExposureTime
6
+
7
+ Shutter time that keeps the same exposure after adding front-of-lens filters, such as a
8
+ neutral-density (ND) filter: t / T, with T the combined transmittance of the filter profiles.
9
+ Aperture and ISO are unchanged.
10
+
11
+ Exact under the linear sensor model (exposure H = E·t); film reciprocity failure is not modelled.
12
+ Nominal filters vary: a "10-stop" or ND1000 (OD 3.0, 9.97 stops) filter transmits roughly, not
13
+ exactly, its rating, so the result is only as good as the profile. The envelope is an
14
+ approximation whenever any profile's `scientificStatus` is `"approximation"`.
15
+
16
+ Example: 1/125 s with `GENERIC_ND_FILTER_PROFILES[10]` (exactly 2^-10) gives 1024/125 = 8.192 s;
17
+ on a third-stop dial, 8 s.
18
+
19
+ ```ts
20
+ export function calculateFilteredExposureTime(
21
+ input: CalculateFilteredExposureTimeInput
22
+ ): CalculationResult<FilteredExposureTime>;
23
+ ```
24
+
25
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
26
+
27
+ ## CalculateFilteredExposureTimeInput
28
+
29
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
30
+
31
+ ```ts
32
+ export interface CalculateFilteredExposureTimeInput {
33
+ /** Shutter time without the filter, in seconds. */
34
+ shutterSeconds: number;
35
+ /**
36
+ * One front-of-lens filter profile, or several in light-path order (scene side first), as
37
+ * accepted by `composeFrontOfLensFilterTransmission`.
38
+ */
39
+ filter: FrontOfLensFilterProfile | readonly FrontOfLensFilterProfile[];
40
+ /**
41
+ * Optional shutter dial, in seconds (for example `GENERIC_THIRD_STOP_SHUTTER_SECONDS`). When
42
+ * given, the result also reports the nearest dial value (nearest in stops, the shorter on a tie).
43
+ */
44
+ shutterDialSeconds?: readonly number[];
45
+ /** Wavelength at which the filters are resolved. Defaults to 550 nm. Neutral filters do not depend on it. */
46
+ wavelengthNanometers?: number;
47
+ /** Wavelength basis; defaults to the first filter profile's basis and must match every profile. */
48
+ wavelengthBasis?: Exclude<SpectralWavelengthBasis, "unspecified">;
49
+ /** Incident polarization, which matters only when a polarizer is in the stack. Omitted means unpolarized. */
50
+ incidentPolarization?: LinearPolarizationState;
51
+ }
52
+ ```
53
+
54
+ ## FILTERED_EXPOSURE_DEFAULT_WAVELENGTH_NANOMETERS
55
+
56
+ Wavelength used when the caller gives none: 550 nm, near the photopic peak. Neutral filters do not depend on it.
57
+
58
+ ```ts
59
+ FILTERED_EXPOSURE_DEFAULT_WAVELENGTH_NANOMETERS = 550 as const
60
+ ```
61
+
62
+ ## FilteredExposureDialSetting
63
+
64
+ The dial value nearest the exact filtered shutter time.
65
+
66
+ ```ts
67
+ export interface FilteredExposureDialSetting {
68
+ /** Nearest dial value in seconds. */
69
+ shutterSeconds: number;
70
+ /**
71
+ * log2(dial / exact filtered time): exposure of the dial setting relative to the unfiltered
72
+ * exposure, in stops. Positive is brighter (longer), negative darker.
73
+ */
74
+ exposureErrorStops: number;
75
+ /** `"maximum"` when the exact time is longer than every dial value (use bulb for the exact time), `"minimum"` when shorter than every one. */
76
+ clamped: false | "minimum" | "maximum";
77
+ /** How the dial value was chosen. */
78
+ quantizationPolicy: "nearest-log2-shorter-on-tie";
79
+ }
80
+ ```
81
+
82
+ ## FilteredExposureTime
83
+
84
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
85
+
86
+ ```ts
87
+ export interface FilteredExposureTime {
88
+ /** Shutter time without the filter, in seconds. */
89
+ shutterSeconds: number;
90
+ /** Shutter time that gives the same exposure with the filter: shutterSeconds / linearTransmissionFactor. */
91
+ filteredShutterSeconds: number;
92
+ /** Combined intensity transmittance T of the filter stack, in (0, 1]. */
93
+ linearTransmissionFactor: number;
94
+ /** Combined attenuation −log2 T, in stops. */
95
+ attenuationStops: number;
96
+ /** Shutter multiplier 1/T = 2^attenuationStops. */
97
+ exposureFactor: number;
98
+ /** Wavelength at which the filters were resolved, in nanometres. */
99
+ wavelengthNanometers: number;
100
+ /** The filter-stack transmission this result is based on, with each filter's identity and evidence. */
101
+ filterTransmission: ComposedFrontOfLensFilterTransmission;
102
+ /** Nearest shutter-dial value; present only when `shutterDialSeconds` was given. */
103
+ dial?: FilteredExposureDialSetting;
104
+ }
105
+ ```
@@ -0,0 +1,32 @@
1
+ # facade/develop-joined-parts.ts public contracts
2
+
3
+ The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
4
+
5
+ ## simulateCaptureFromParts
6
+
7
+ Develops the whole frame of `options` from the packed RAW of region renders of the same request (#571), so a capture can
8
+ render its RAW in parallel parts and still take the development steps that filter across the whole image:
9
+ `output.diffractionCorrection` and `output.sharpening`, which a region render refuses, and `output.lensCorrections` (#570), which
10
+ runs on the whole developed frame here exactly as in `simulateCapture(options)`. The parts are joined exactly as
11
+ {@link serializeSimulatedCaptureDngFromParts } joins them (they must cover every native site and agree where they overlap),
12
+ and the joined RAW is the whole render's RAW, so the result's `image` (samples and JPEG or PNG bytes), `raw` codes and
13
+ `plan` are those of `simulateCapture(options)`, bit for bit.
14
+
15
+ `options` is the whole request (no `output.region`), with the development options it should have; render the parts with
16
+ the same request plus `output.region`, without `output.diffractionCorrection`, `output.sharpening` and `output.dng`, and with a
17
+ fixed white balance in place of `"auto"` (none of these change the RAW codes; auto white balance is measured here over the
18
+ joined frame). A part may keep or drop `output.lensCorrections` (a corrected region only widens its `region.nativeRect`).
19
+ Any `output.format` serves for a part.
20
+
21
+ `onProgress` reports the `white-balance` (with auto white balance), `development`, `diffraction-correction`, `sharpening` and
22
+ `jpeg` stages, the detail stages once per output row, so a host can show "Applying sharpening to the whole image". The lens
23
+ corrections report no stage of their own: they run between `diffraction-correction` and `sharpening`.
24
+ No `raw` stage is reported: no RAW is rendered. What differs from the whole render's result: `diagnostics.providerEvaluationCount`
25
+ is 0, `diagnostics.timingsMs` are this call's, `diagnostics.fast` and the `raw.*` assumptions of how the RAW was rendered are
26
+ not repeated (they are in the parts' results), and a `parts` assumption records the join.
27
+
28
+ ```ts
29
+ export async function simulateCaptureFromParts(options: SimulateCaptureOptions, parts: readonly SimulateCaptureDngPart[]): Promise<SimulateCaptureResult>;
30
+ ```
31
+
32
+ This call is asynchronous; byte hashing uses the caller runtime's Web Crypto.
@@ -144,7 +144,7 @@ a capture does. `motion.rotation` is a non-zero angular velocity; `sensor.tempor
144
144
  SIMULATE_CAPTURE_COST_OPTIONS = Object.freeze(["lens.pupilSampling", "lens.projection", "lens.distortion", "lens.vignetting", "lens.psf",
145
145
  "lens.diffraction", "lens.defocus", "lens.catalogue", "sensor.antiAliasing", "sensor.pixelAperture.box", "sensor.temporalSampleCount", "motion.rotation", "motion.shutter.rolling", "output.borderHandling.mirror", "output.region",
146
146
  "output.whiteBalance.auto", "output.demosaic.ahd", "output.highlightMode.reconstruct", "output.colorEncoding.wide-gamut", "output.gamutMapping.perceptual",
147
- "output.diffractionCorrection", "output.sharpening", "output.format.jpeg", "output.format.png", "output.format.dng"] as const)
147
+ "output.diffractionCorrection", "output.sharpening", "output.lensCorrections", "output.format.jpeg", "output.format.png", "output.format.dng"] as const)
148
148
  ```
149
149
 
150
150
  ## SIMULATE_CAPTURE_DEFAULT_ILLUMINANCE_LUX
@@ -467,6 +467,22 @@ export interface SimulateCaptureFastReport {
467
467
  export interface SimulateCaptureLensCatalogueRecord { kind: string; fast?: boolean }
468
468
  ```
469
469
 
470
+ ## SimulateCaptureLensCorrections
471
+
472
+ `output.lensCorrections` (#570): which of the catalogue lens's generic corrections (`createGenericLensCorrectionProfile`) the
473
+ development applies. Each is off unless set to true.
474
+
475
+ ```ts
476
+ export interface SimulateCaptureLensCorrections {
477
+ /** Radial distortion r_d = r(1 + k1ρ²), inverted by resampling: it stretches the corners and lowers their resolution. */
478
+ distortion?: boolean;
479
+ /** Lateral colour: each channel rescaled by 1/(1 + L·D(λ_c)) into register; axial (longitudinal) colour stays. */
480
+ lateralColor?: boolean;
481
+ /** Vignetting: a gain 1/E(ρ) at each pixel's source point; corner noise rises with the signal, the SNR does not. */
482
+ vignetting?: boolean;
483
+ }
484
+ ```
485
+
470
486
  ## SimulateCaptureLensDistortion
471
487
 
472
488
  The exact contract is declared below; use the domain guide for assumptions and staged integration.
@@ -531,7 +547,8 @@ export interface SimulateCaptureOptions {
531
547
  * distance beyond the focal length (no minimum focus distance; `subjectDistanceM` must be at least twice the focal length, as for
532
548
  * every lens). Works in `simulateCapture` and the display view. Not combinable with `prime`, `catalogue` or `focalLengthMm`.
533
549
  * Optional `widestFNumber` and `narrowestFNumber` (within `IDEAL_LENS_LIMITS`, widest ≤ narrowest; each defaults to its bound)
534
- * narrow the range: `fNumber` must lie in it, and shutter priority and program choose apertures only in it. Default off.
550
+ * narrow the range: `fNumber` must lie in it, and shutter priority and program choose apertures only in it (program limits its
551
+ * line to a range that does not hold f/4–f/16, #583). Default off.
535
552
  */
536
553
  ideal?: { focalLengthMm: number; widestFNumber?: number; narrowestFNumber?: number };
537
554
  /**
@@ -738,6 +755,16 @@ export interface SimulateCaptureOptions {
738
755
  * (not the wide-gamut or perceptual PNG paths); not combinable with `region`. The DNG's RAW codes are not changed. Default off.
739
756
  */
740
757
  sharpening?: SimulateCaptureSharpening;
758
+ /**
759
+ * Opt-in (#570), with `lens.catalogue`: the lens's generic corrections (see {@link SimulateCaptureLensCorrections}) on the
760
+ * developed linear colour, after demosaic, colour, white balance and `diffractionCorrection`, before the tone curve; for the JPEG,
761
+ * PNG and samples. The RAW and the DNG are never corrected. To keep the output size with no missing pixels, the corrected image is
762
+ * magnified by the smallest factor 1/m ≥ 1 that keeps every pixel's source inside the frame (recorded in the
763
+ * `output.lensCorrections` assumption). Works with `region`: a region develops the frame pixels its corrected pixels read
764
+ * (`region.nativeRect`, `raw` and `plan` describe that rectangle) and its pixels are the whole render's. All off (or omitted):
765
+ * the development is unchanged.
766
+ */
767
+ lensCorrections?: SimulateCaptureLensCorrections;
741
768
  };
742
769
  /** Camera motion and shutter type. Default: still camera, global electronic shutter. */
743
770
  motion?: SimulateCaptureMotion;
@@ -776,7 +803,7 @@ export interface SimulateCaptureOptions {
776
803
  fast?: boolean;
777
804
  /** Aborting cancels the running task; the promise rejects with a domain error and returns no partial image. */
778
805
  signal?: AbortSignal;
779
- /** Called after each committed RAW, development or JPEG tile. Cannot change output. */
806
+ /** Called after each committed RAW, development, detail or JPEG tile (see {@link SimulateCaptureProgress}). Cannot change output. */
780
807
  onProgress?: (progress: SimulateCaptureProgress) => void;
781
808
  }
782
809
  ```
@@ -799,11 +826,15 @@ export type SimulateCapturePixelAperture = "point" | "box" | { kind: "box"; samp
799
826
 
800
827
  ## SimulateCaptureProgress
801
828
 
802
- The exact contract is declared below; use the domain guide for assumptions and staged integration.
829
+ One progress event of `onProgress`. Stages run in this order: `raw`, `white-balance` (with auto white balance), `development`
830
+ (rows), then, when requested, `diffraction-correction` and `sharpening` (#571: one event per output row of the whole image;
831
+ each stage's last event has `completedTileCount === totalTileCount`), then `jpeg`. `output.lensCorrections` (#570) runs between
832
+ `diffraction-correction` and `sharpening` and reports no stage of its own. {@link simulateCaptureFromParts }
833
+ reports no `raw` stage: its RAW comes from the parts.
803
834
 
804
835
  ```ts
805
836
  export interface SimulateCaptureProgress {
806
- stage: "raw" | "white-balance" | "development" | "jpeg";
837
+ stage: "raw" | "white-balance" | "development" | "diffraction-correction" | "sharpening" | "jpeg";
807
838
  completedTileCount: number;
808
839
  totalTileCount: number;
809
840
  }
@@ -0,0 +1,34 @@
1
+ # optics/generic-nd-filter-presets.ts public contracts
2
+
3
+ The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
4
+
5
+ ## GENERIC_ND_FILTER_PROFILES
6
+
7
+ Frozen generic ND filter profiles keyed by stops: `GENERIC_ND_FILTER_PROFILES[10]` transmits
8
+ exactly 1/1024. Spectrally flat over 380–780 nm (air), idealized (`scientificStatus:
9
+ "approximation"`), unbranded. 1/125 s with the 10-stop preset gives 1024/125 ≈ 8.19 s in
10
+ `calculateFilteredExposureTime`.
11
+
12
+ ```ts
13
+ GENERIC_ND_FILTER_PROFILES: Readonly<Record<GenericNdFilterStops, FrontOfLensFilterProfile>> = freezeOwnedData({
14
+ 3: idealNdProfile(3),
15
+ 6: idealNdProfile(6),
16
+ 10: idealNdProfile(10)
17
+ })
18
+ ```
19
+
20
+ ## GENERIC_ND_FILTER_STOPS
21
+
22
+ Attenuations, in stops, of the generic ND presets.
23
+
24
+ ```ts
25
+ GENERIC_ND_FILTER_STOPS = Object.freeze([3, 6, 10] as const)
26
+ ```
27
+
28
+ ## GenericNdFilterStops
29
+
30
+ One of the generic ND preset attenuations: 3, 6 or 10 stops.
31
+
32
+ ```ts
33
+ export type GenericNdFilterStops = (typeof GENERIC_ND_FILTER_STOPS)[number];
34
+ ```
@@ -200,7 +200,9 @@ Kernel interpolation between regular-grid nodes.
200
200
  - `rotationally-symmetric-radial-field-centroid-aligned`: for a rotationally symmetric lens. Nodes
201
201
  are declared only along the +X field axis (fieldYmm = [0], fieldXmm >= 0). A query at field
202
202
  (x, y) is interpolated, with centroid alignment, at radius hypot(x, y). The kernel is then
203
- rotated by atan2(y, x) about its centre. The sample pitch must be square.
203
+ rotated by atan2(y, x) about its centre, with the kernel row index increasing with image +Y (the
204
+ convention the environment PSF quadrature applies), so an asymmetric kernel points the same way
205
+ as in the 2-D grid modes. The sample pitch must be square.
204
206
  Shifts and rotations use bilinear resampling. This smooths the kernel slightly (by at most about
205
207
  one sample). Samples outside the kernel are zero, and the result is renormalised to unit energy.
206
208
 
@@ -0,0 +1,12 @@
1
+ # output/developed-lens-corrections.ts public contracts
2
+
3
+ The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
4
+
5
+ ## GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM
6
+
7
+ Effective wavelengths of the red, green and blue channels for the lateral-colour map, nm: a generic approximation of where
8
+ a camera's channel responses peak, not a measured camera.
9
+
10
+ ```ts
11
+ GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM: Readonly<Record<LensCorrectionChannel, number>> = Object.freeze({ red: 610, green: 540, blue: 465 })
12
+ ```
@@ -28,12 +28,3 @@ export interface CreateGenericLensCorrectionProfileInput {
28
28
  state: OpticalProfileState;
29
29
  }
30
30
  ```
31
-
32
- ## GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM
33
-
34
- Effective wavelengths of the red, green and blue channels for the lateral-colour map, nm: a generic approximation of where
35
- a camera's channel responses peak, not a measured camera.
36
-
37
- ```ts
38
- GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM: Readonly<Record<LensCorrectionChannel, number>> = Object.freeze({ red: 610, green: 540, blue: 465 })
39
- ```
package/llms.txt CHANGED
@@ -28,6 +28,7 @@
28
28
  - [calculateBlurShareOfFrame()](https://github.com/Photivra/photivra/blob/main/docs/api/camera-sensor-equivalence.md#calculateblurshareofframe): a blur circle as a share of the frame diagonal, or its size on a print
29
29
  - [calculateCenteredCrop()](https://github.com/Photivra/photivra/blob/main/docs/api/output-crop.md#calculatecenteredcrop): pixels left after a centred crop
30
30
  - [calculateProjectedMotionBlur()](https://github.com/Photivra/photivra/blob/main/docs/api/motion-projected-motion.md#calculateprojectedmotionblur): how far a moving subject smears on the sensor during the shutter time
31
+ - [calculateFilteredExposureTime()](https://github.com/Photivra/photivra/blob/main/docs/api/exposure-filtered-exposure-time.md#calculatefilteredexposuretime): shutter time that keeps the same exposure with an ND (or any front-of-lens) filter, and the nearest shutter-dial value; `GENERIC_ND_FILTER_PROFILES` gives idealized 3-, 6- and 10-stop filters (exactly 2^-N)
31
32
  - [calculateAiryDisk()](https://github.com/Photivra/photivra/blob/main/docs/api/optics-diffraction.md#calculateairydisk): diffraction spot size from f-number and wavelength
32
33
  - [calculateSignalToNoise()](https://github.com/Photivra/photivra/blob/main/docs/api/sensor-signal-noise.md#calculatesignaltonoise): expected SNR from signal and read noise in electrons
33
34
  - [calculateChromaticityFromCorrelatedColorTemperature()](https://github.com/Photivra/photivra/blob/main/docs/api/color-illuminants.md#calculatechromaticityfromcorrelatedcolortemperature): white point for a colour temperature in kelvin
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@photivra/engine",
3
- "version": "2.18.0",
3
+ "version": "2.19.0",
4
4
  "private": false,
5
5
  "description": "Scientific TypeScript engine for camera optics, exposure, focus, motion, sensor sampling, diffraction, and digital imaging calculations.",
6
6
  "type": "module",