@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
@@ -1 +1 @@
1
- {"version":3,"file":"capture-translation-parallax-temporal-quadrature.js","sourceRoot":"","sources":["../../src/motion/capture-translation-parallax-temporal-quadrature.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Parallax from camera movement (translation): nearer points shift more in the image than distant\n * ones. It is evaluated at deterministic midpoint time samples across each scene point's local\n * exposure window.\n *\n * The model works point by point, on purpose. Camera movement and optional subject movement combine\n * as relative linear motion; neither is recast as the other. Camera rotation, and visibility or\n * occlusion, are separate models.\n * @see docs/MOTION_AND_SIGNAL.md for the equations, units, coordinate conventions, known blockers\n * and support limits.\n */\n\nimport {\n approximationResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport {\n InvalidScientificInputError,\n requirePositiveFinite,\n requirePositiveInteger\n} from \"../core/validation.js\";\nimport {\n transformNativeRasterVectorToOriented,\n type CaptureOrientation,\n type RasterPoint\n} from \"../output/capture-geometry.js\";\nimport { calculateThinLensImageDistance } from \"../optics/thin-lens.js\";\nimport type { Vector3 } from \"../schema/scene.js\";\nimport type {\n ResolvedCaptureModeTiming\n} from \"../sensor/capture-mode-timing.js\";\nimport type {\n SensorImagingArea\n} from \"../sensor/sensor-geometry.js\";\nimport { requireInputObject, requireInputKeys } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\n\nexport interface CaptureTranslationParallaxSceneSample {\n sampleId: string;\n /**\n * Native destination point. Its local exposure window applies to this scene\n * sample.\n */\n destinationPointNative:\n RasterPoint;\n /**\n * Point position in metres, in the camera axes at the first opening-boundary\n * phase. +X right, +Y up, +Z forward (away from the camera).\n */\n positionCameraM: Vector3;\n /**\n * Optional constant velocity of the subject (world point), in the same\n * starting camera axes. If omitted, the world point is still.\n */\n subjectVelocityMps?: Vector3;\n}\n\nexport interface CalculateCaptureTranslationParallaxTemporalQuadratureInput {\n timing:\n ResolvedCaptureModeTiming;\n imagingArea:\n SensorImagingArea;\n focalLengthMm: number;\n focusDistanceM?: number;\n orientation:\n CaptureOrientation;\n /**\n * Constant movement of the camera centre, in camera axes at the capture\n * reference phase. Rotation is handled separately, on purpose.\n */\n cameraTranslationVelocityMps:\n Vector3;\n temporalSampleCount: number;\n sceneSamples:\n readonly CaptureTranslationParallaxSceneSample[];\n}\n\nexport interface CaptureTranslationParallaxTemporalNode {\n temporalSampleIndex: number;\n localExposurePhase: number;\n captureTimeSecondsFromReference:\n number;\n normalizedTimeWeight: number;\n timeMeasureSeconds: number;\n relativePositionCameraM:\n Vector3;\n mappedImagePointMm: {\n x: number;\n y: number;\n };\n deltaFromReferenceImagePlaneMm: {\n x: number;\n y: number;\n distance: number;\n };\n deltaNativeSamples: {\n x: number;\n y: number;\n distance: number;\n };\n deltaOrientedSamples: {\n x: number;\n y: number;\n distance: number;\n };\n}\n\nexport interface CaptureTranslationParallaxTemporalSample {\n sampleId: string;\n destinationPointNative:\n RasterPoint;\n referencePositionCameraM:\n Vector3;\n subjectVelocityMps: Vector3;\n localExposureWindow: {\n startSecondsFromCaptureReference:\n number;\n endSecondsFromCaptureReference:\n number;\n durationSeconds: number;\n };\n referenceImagePointMm: {\n x: number;\n y: number;\n };\n nodes:\n readonly CaptureTranslationParallaxTemporalNode[];\n}\n\nexport interface CaptureTranslationParallaxTemporalQuadrature {\n timeReference:\n \"first-opening-boundary-phase\";\n timingProfileIdentity: {\n profileId: string;\n profileVersion: string;\n captureModeId: string;\n };\n orientation:\n CaptureOrientation;\n projectionDistanceMm: number;\n temporalSampleCount: number;\n cameraTranslationVelocityMps:\n Vector3;\n samples:\n readonly CaptureTranslationParallaxTemporalSample[];\n geometryModel:\n \"metric-depth-dependent-constant-linear-translation\";\n relativeMotionComposition:\n \"subject-minus-camera-translation\";\n metricSceneDepthRequired: true;\n oneGlobalHomographyAuthorized: false;\n cameraRotationComposed: false;\n visibilityOcclusionModeled: false;\n sensorReadoutTimingUsedAsExposureTiming:\n false;\n}\n\nfunction requireFiniteVector(\n value: Vector3,\n path: string\n): Vector3 {\n requireInputObject(value, path);\n for (\n const axis of\n [\"x\", \"y\", \"z\"] as const\n ) {\n if (\n !Number.isFinite(\n value[axis]\n )\n ) {\n throw new InvalidScientificInputError(\n path +\n \".\" +\n axis +\n \" must be finite.\"\n );\n }\n }\n return {\n ...value\n };\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 +\n \" must be a non-empty string.\"\n );\n }\n return value.trim();\n}\n\nfunction samePoint(\n a: RasterPoint,\n b: RasterPoint\n): boolean {\n return (\n a.x === b.x &&\n a.y === b.y\n );\n}\n\nfunction projectPoint(\n positionM: Vector3,\n projectionDistanceMm: number,\n path: string\n): {\n x: number;\n y: number;\n} {\n if (\n positionM.z <= 0\n ) {\n throw new InvalidScientificInputError(\n path +\n \" must remain in front of the camera plane.\"\n );\n }\n const mapped = {\n x:\n projectionDistanceMm *\n (positionM.x /\n positionM.z),\n y:\n projectionDistanceMm *\n (positionM.y /\n positionM.z)\n };\n if (\n !Number.isFinite(mapped.x) ||\n !Number.isFinite(mapped.y)\n ) {\n throw new InvalidScientificInputError(\n path +\n \" projection must remain finite.\"\n );\n }\n return mapped;\n}\n\nfunction localExposureForPoint(\n timing:\n ResolvedCaptureModeTiming,\n point:\n RasterPoint\n): ResolvedCaptureModeTiming[\"exposureWindows\"][\"samples\"][number] {\n requireInputObject(point, \"destinationPointNative\");\n const matches =\n timing.exposureWindows.samples.filter(\n (sample) =>\n samePoint(\n sample.pointNative,\n point\n )\n );\n if (matches.length !== 1) {\n throw new InvalidScientificInputError(\n \"Each translation/parallax scene sample must match exactly one committed exposure-window sample point.\"\n );\n }\n return matches[0]!;\n}\n\n/**\n * Evaluates depth-dependent parallax from camera movement at deterministic\n * midpoint time samples across each scene point's local exposure window.\n *\n * The model works point by point, on purpose. Camera movement and optional\n * subject movement combine as relative linear motion; neither is recast as\n * the other. Camera rotation, and visibility or occlusion, are separate\n * models.\n */\nexport function calculateCaptureTranslationParallaxTemporalQuadrature(\n input:\n CalculateCaptureTranslationParallaxTemporalQuadratureInput\n): CalculationResult<CaptureTranslationParallaxTemporalQuadrature> {\n requireInputKeys(input, \"calculateCaptureTranslationParallaxTemporalQuadrature\", [\"timing\", \"imagingArea\", \"focalLengthMm\", \"focusDistanceM\", \"orientation\", \"cameraTranslationVelocityMps\", \"temporalSampleCount\", \"sceneSamples\"]);\n requireInputObject(input.imagingArea, \"imagingArea\");\n requireInputObject(input.timing, \"timing\");\n requireInputObject(input.timing.nativeRaster, \"timing.nativeRaster\");\n requireInputObject(input.timing.exposureWindows, \"timing.exposureWindows\");\n if (!Array.isArray(input.timing.exposureWindows.samples)) throw new InvalidConfigurationError(\"timing.exposureWindows.samples must be an array.\");\n input.timing.exposureWindows.samples.forEach((sample, index) => {\n requireInputObject(sample, \"timing.exposureWindows.samples[\" + index + \"]\");\n requireInputObject(sample.pointNative, \"timing.exposureWindows.samples[\" + index + \"].pointNative\");\n });\n requirePositiveFinite(\n \"imagingArea.widthMm\",\n input.imagingArea.widthMm\n );\n requirePositiveFinite(\n \"imagingArea.heightMm\",\n input.imagingArea.heightMm\n );\n requirePositiveFinite(\n \"focalLengthMm\",\n input.focalLengthMm\n );\n requirePositiveInteger(\n \"temporalSampleCount\",\n input.temporalSampleCount\n );\n\n if (\n !Array.isArray(\n input.sceneSamples\n ) ||\n input.sceneSamples.length === 0\n ) {\n throw new InvalidConfigurationError(\n \"sceneSamples must be a non-empty array.\"\n );\n }\n\n input.sceneSamples.forEach((sample, index) => requireInputObject(sample, \"sceneSamples[\" + index + \"]\"));\n const ids =\n input.sceneSamples.map(\n (sample) =>\n requireNonEmptyString(\n sample.sampleId,\n \"sceneSamples[].sampleId\"\n )\n );\n if (\n new Set(ids).size !==\n ids.length\n ) {\n throw new InvalidScientificInputError(\n \"sceneSamples must not contain duplicate sampleId values.\"\n );\n }\n\n const cameraVelocity =\n requireFiniteVector(\n input\n .cameraTranslationVelocityMps,\n \"cameraTranslationVelocityMps\"\n );\n\n const projectionDistanceMm =\n input.focusDistanceM ===\n undefined\n ? input.focalLengthMm\n : calculateThinLensImageDistance({\n focalLengthMm:\n input.focalLengthMm,\n objectDistanceM:\n input.focusDistanceM\n }).value.imageDistanceMm;\n\n const pitchX =\n input.imagingArea.widthMm /\n input.timing.nativeRaster\n .pixelWidth;\n const pitchY =\n input.imagingArea.heightMm /\n input.timing.nativeRaster\n .pixelHeight;\n\n if (\n !Number.isFinite(pitchX) ||\n !Number.isFinite(pitchY) ||\n pitchX <= 0 ||\n pitchY <= 0\n ) {\n throw new InvalidScientificInputError(\n \"Resolved translation/parallax sampling pitch must remain finite and positive.\"\n );\n }\n\n const samples =\n input.sceneSamples.map(\n (sceneSample, sampleIndex) => {\n const samplePath =\n \"sceneSamples[\" +\n sampleIndex +\n \"]\";\n const sampleId =\n requireNonEmptyString(\n sceneSample.sampleId,\n samplePath +\n \".sampleId\"\n );\n const referencePosition =\n requireFiniteVector(\n sceneSample\n .positionCameraM,\n samplePath +\n \".positionCameraM\"\n );\n if (\n referencePosition.z <= 0\n ) {\n throw new InvalidScientificInputError(\n samplePath +\n \".positionCameraM.z must place the scene point in front of the camera.\"\n );\n }\n const subjectVelocity =\n sceneSample\n .subjectVelocityMps ===\n undefined\n ? {\n x: 0,\n y: 0,\n z: 0\n }\n : requireFiniteVector(\n sceneSample\n .subjectVelocityMps,\n samplePath +\n \".subjectVelocityMps\"\n );\n\n const exposure =\n localExposureForPoint(\n input.timing,\n sceneSample\n .destinationPointNative\n );\n const duration =\n exposure\n .localExposureDurationSeconds;\n if (\n !Number.isFinite(\n duration\n ) ||\n duration <= 0\n ) {\n throw new InvalidScientificInputError(\n \"Translation/parallax requires a positive finite local exposure duration.\"\n );\n }\n\n const referenceImagePoint =\n projectPoint(\n referencePosition,\n projectionDistanceMm,\n samplePath +\n \".positionCameraM\"\n );\n\n const normalizedTimeWeight =\n 1 /\n input.temporalSampleCount;\n const timeMeasureSeconds =\n duration /\n input.temporalSampleCount;\n\n const nodes =\n Array.from(\n {\n length:\n input.temporalSampleCount\n },\n (\n _,\n temporalSampleIndex\n ) => {\n const localExposurePhase =\n (temporalSampleIndex +\n 0.5) /\n input.temporalSampleCount;\n const captureTime =\n exposure\n .startOffsetSecondsFromOpeningReference +\n localExposurePhase *\n duration;\n\n const relativePosition = {\n x:\n referencePosition.x +\n (subjectVelocity.x -\n cameraVelocity.x) *\n captureTime,\n y:\n referencePosition.y +\n (subjectVelocity.y -\n cameraVelocity.y) *\n captureTime,\n z:\n referencePosition.z +\n (subjectVelocity.z -\n cameraVelocity.z) *\n captureTime\n };\n\n const mapped =\n projectPoint(\n relativePosition,\n projectionDistanceMm,\n samplePath +\n \".relativePositionCameraM\"\n );\n const deltaMm = {\n x:\n mapped.x -\n referenceImagePoint.x,\n y:\n mapped.y -\n referenceImagePoint.y\n };\n const deltaNative = {\n x:\n deltaMm.x /\n pitchX,\n y:\n -deltaMm.y /\n pitchY\n };\n const oriented =\n transformNativeRasterVectorToOriented({\n vector:\n deltaNative,\n orientation:\n input.orientation\n });\n\n return {\n temporalSampleIndex,\n localExposurePhase,\n captureTimeSecondsFromReference:\n captureTime,\n normalizedTimeWeight,\n timeMeasureSeconds,\n relativePositionCameraM:\n relativePosition,\n mappedImagePointMm:\n mapped,\n deltaFromReferenceImagePlaneMm: {\n ...deltaMm,\n distance:\n Math.hypot(\n deltaMm.x,\n deltaMm.y\n )\n },\n deltaNativeSamples: {\n ...deltaNative,\n distance:\n Math.hypot(\n deltaNative.x,\n deltaNative.y\n )\n },\n deltaOrientedSamples: {\n ...oriented,\n distance:\n Math.hypot(\n oriented.x,\n oriented.y\n )\n }\n };\n }\n );\n\n return {\n sampleId,\n destinationPointNative: {\n ...sceneSample\n .destinationPointNative\n },\n referencePositionCameraM:\n referencePosition,\n subjectVelocityMps:\n subjectVelocity,\n localExposureWindow: {\n startSecondsFromCaptureReference:\n exposure\n .startOffsetSecondsFromOpeningReference,\n endSecondsFromCaptureReference:\n exposure\n .endOffsetSecondsFromOpeningReference,\n durationSeconds:\n duration\n },\n referenceImagePointMm:\n referenceImagePoint,\n nodes\n };\n }\n );\n\n return approximationResult(\n {\n timeReference:\n \"first-opening-boundary-phase\",\n timingProfileIdentity: {\n profileId:\n input.timing\n .timingProfileId,\n profileVersion:\n input.timing\n .timingProfileVersion,\n captureModeId:\n input.timing\n .captureModeId\n },\n orientation:\n input.orientation,\n projectionDistanceMm,\n temporalSampleCount:\n input.temporalSampleCount,\n cameraTranslationVelocityMps:\n cameraVelocity,\n samples,\n geometryModel:\n \"metric-depth-dependent-constant-linear-translation\",\n relativeMotionComposition:\n \"subject-minus-camera-translation\",\n metricSceneDepthRequired:\n true,\n oneGlobalHomographyAuthorized:\n false,\n cameraRotationComposed:\n false,\n visibilityOcclusionModeled:\n false,\n sensorReadoutTimingUsedAsExposureTiming:\n false\n },\n \"capture-translation-parallax-temporal-quadrature\",\n \"1.0.0\",\n [\n \"Translation is evaluated independently for each metric scene point because translational image motion depends on depth.\",\n \"Camera and subject translation coexist as explicit relative linear motion; subject motion is not reclassified as camera motion.\",\n \"The camera-axis basis is fixed at the first-opening-boundary phase; camera rotation remains a separate model.\",\n \"The destination native point selects the authoritative local exposure window; sensor data-readout timing is not substituted for exposure time.\",\n \"Oriented sample displacement is a coordinate transform of the same physical image-plane mapping and does not change the translation physics.\",\n \"No one global homography/warp is authorized for arbitrary 3D scenes.\",\n \"Visibility, occlusion, deformation, acceleration and rolling scene geometry remain outside this model.\"\n ]\n );\n}\n"]}
1
+ {"version":3,"file":"capture-translation-parallax-temporal-quadrature.js","sourceRoot":"","sources":["../../src/motion/capture-translation-parallax-temporal-quadrature.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC;;;;;;;;;;GAUG;AAEH,OAAO,EACL,mBAAmB,EAEpB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,qCAAqC,EAGtC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,8BAA8B,EAAE,MAAM,wBAAwB,CAAC;AAQxE,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,0BAA0B,IAAI,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACzI,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAE,QAAQ,IAAI,SAAS,EAAE,MAAM,2BAA2B,CAAC;AA0HlE,MAAM,UAAU,mBAAmB,CACjC,KAAc,EACd,IAAY;IAEZ,kBAAkB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAChC,KACE,MAAM,IAAI,IACV,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAU,EACxB,CAAC;QACD,IACE,CAAC,MAAM,CAAC,QAAQ,CACd,KAAK,CAAC,IAAI,CAAC,CACZ,EACD,CAAC;YACD,MAAM,IAAI,2BAA2B,CACnC,IAAI;gBACF,GAAG;gBACH,IAAI;gBACJ,kBAAkB,CACrB,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO;QACL,GAAG,KAAK;KACT,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CACnB,SAAkB,EAClB,oBAA4B,EAC5B,IAAY;IAKZ,IACE,SAAS,CAAC,CAAC,IAAI,CAAC,EAChB,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,IAAI;YACF,4CAA4C,CAC/C,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG;QACb,CAAC,EACC,oBAAoB;YACpB,CAAC,SAAS,CAAC,CAAC;gBACV,SAAS,CAAC,CAAC,CAAC;QAChB,CAAC,EACC,oBAAoB;YACpB,CAAC,SAAS,CAAC,CAAC;gBACV,SAAS,CAAC,CAAC,CAAC;KACjB,CAAC;IACF,IACE,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QAC1B,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAC1B,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,IAAI;YACF,iCAAiC,CACpC,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,qBAAqB,CAC5B,MAC2B,EAC3B,KACa;IAEb,kBAAkB,CAAC,KAAK,EAAE,wBAAwB,CAAC,CAAC;IACpD,MAAM,OAAO,GACX,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,MAAM,CACnC,CAAC,MAAM,EAAE,EAAE,CACT,SAAS,CACP,MAAM,CAAC,WAAW,EAClB,KAAK,CACN,CACJ,CAAC;IACJ,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,2BAA2B,CACnC,uGAAuG,CACxG,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC,CAAC,CAAE,CAAC;AACrB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,qDAAqD,CACnE,KAC4D;IAE5D,gBAAgB,CAAC,KAAK,EAAE,uDAAuD,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,eAAe,EAAE,gBAAgB,EAAE,aAAa,EAAE,8BAA8B,EAAE,qBAAqB,EAAE,cAAc,CAAC,CAAC,CAAC;IACrO,kBAAkB,CAAC,KAAK,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;IACrD,kBAAkB,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC3C,kBAAkB,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,EAAE,qBAAqB,CAAC,CAAC;IACrE,kBAAkB,CAAC,KAAK,CAAC,MAAM,CAAC,eAAe,EAAE,wBAAwB,CAAC,CAAC;IAC3E,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC;QAAE,MAAM,IAAI,yBAAyB,CAAC,kDAAkD,CAAC,CAAC;IAClJ,KAAK,CAAC,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;QAC7D,kBAAkB,CAAC,MAAM,EAAE,iCAAiC,GAAG,KAAK,GAAG,GAAG,CAAC,CAAC;QAC5E,kBAAkB,CAAC,MAAM,CAAC,WAAW,EAAE,iCAAiC,GAAG,KAAK,GAAG,eAAe,CAAC,CAAC;IACtG,CAAC,CAAC,CAAC;IACH,qBAAqB,CACnB,qBAAqB,EACrB,KAAK,CAAC,WAAW,CAAC,OAAO,CAC1B,CAAC;IACF,qBAAqB,CACnB,sBAAsB,EACtB,KAAK,CAAC,WAAW,CAAC,QAAQ,CAC3B,CAAC;IACF,qBAAqB,CACnB,eAAe,EACf,KAAK,CAAC,aAAa,CACpB,CAAC;IACF,sBAAsB,CACpB,qBAAqB,EACrB,KAAK,CAAC,mBAAmB,CAC1B,CAAC;IAEF,IACE,CAAC,KAAK,CAAC,OAAO,CACZ,KAAK,CAAC,YAAY,CACnB;QACD,KAAK,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC,EAC/B,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,yCAAyC,CAC1C,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,kBAAkB,CAAC,MAAM,EAAE,eAAe,GAAG,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC;IACzG,MAAM,GAAG,GACP,KAAK,CAAC,YAAY,CAAC,GAAG,CACpB,CAAC,MAAM,EAAE,EAAE,CACT,qBAAqB,CACnB,MAAM,CAAC,QAAQ,EACf,yBAAyB,CAC1B,CACJ,CAAC;IACJ,IACE,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI;QACjB,GAAG,CAAC,MAAM,EACV,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,0DAA0D,CAC3D,CAAC;IACJ,CAAC;IAED,MAAM,cAAc,GAClB,mBAAmB,CACjB,KAAK;SACF,4BAA4B,EAC/B,8BAA8B,CAC/B,CAAC;IAEJ,MAAM,oBAAoB,GACxB,KAAK,CAAC,cAAc;QACpB,SAAS;QACP,CAAC,CAAC,KAAK,CAAC,aAAa;QACrB,CAAC,CAAC,8BAA8B,CAAC;YAC7B,aAAa,EACX,KAAK,CAAC,aAAa;YACrB,eAAe,EACb,KAAK,CAAC,cAAc;SACvB,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC;IAE/B,MAAM,MAAM,GACV,KAAK,CAAC,WAAW,CAAC,OAAO;QACzB,KAAK,CAAC,MAAM,CAAC,YAAY;aACtB,UAAU,CAAC;IAChB,MAAM,MAAM,GACV,KAAK,CAAC,WAAW,CAAC,QAAQ;QAC1B,KAAK,CAAC,MAAM,CAAC,YAAY;aACtB,WAAW,CAAC;IAEjB,IACE,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;QACxB,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;QACxB,MAAM,IAAI,CAAC;QACX,MAAM,IAAI,CAAC,EACX,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,+EAA+E,CAChF,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GACX,KAAK,CAAC,YAAY,CAAC,GAAG,CACpB,CAAC,WAAW,EAAE,WAAW,EAAE,EAAE;QAC3B,MAAM,UAAU,GACd,eAAe;YACf,WAAW;YACX,GAAG,CAAC;QACN,MAAM,QAAQ,GACZ,qBAAqB,CACnB,WAAW,CAAC,QAAQ,EACpB,UAAU;YACR,WAAW,CACd,CAAC;QACJ,MAAM,iBAAiB,GACrB,mBAAmB,CACjB,WAAW;aACR,eAAe,EAClB,UAAU;YACR,kBAAkB,CACrB,CAAC;QACJ,IACE,iBAAiB,CAAC,CAAC,IAAI,CAAC,EACxB,CAAC;YACD,MAAM,IAAI,2BAA2B,CACnC,UAAU;gBACR,uEAAuE,CAC1E,CAAC;QACJ,CAAC;QACD,MAAM,eAAe,GACnB,WAAW;aACR,kBAAkB;YACrB,SAAS;YACP,CAAC,CAAC;gBACE,CAAC,EAAE,CAAC;gBACJ,CAAC,EAAE,CAAC;gBACJ,CAAC,EAAE,CAAC;aACL;YACH,CAAC,CAAC,mBAAmB,CACjB,WAAW;iBACR,kBAAkB,EACrB,UAAU;gBACR,qBAAqB,CACxB,CAAC;QAER,MAAM,QAAQ,GACZ,qBAAqB,CACnB,KAAK,CAAC,MAAM,EACZ,WAAW;aACR,sBAAsB,CAC1B,CAAC;QACJ,MAAM,QAAQ,GACZ,QAAQ;aACL,4BAA4B,CAAC;QAClC,IACE,CAAC,MAAM,CAAC,QAAQ,CACd,QAAQ,CACT;YACD,QAAQ,IAAI,CAAC,EACb,CAAC;YACD,MAAM,IAAI,2BAA2B,CACnC,0EAA0E,CAC3E,CAAC;QACJ,CAAC;QAED,MAAM,mBAAmB,GACvB,YAAY,CACV,iBAAiB,EACjB,oBAAoB,EACpB,UAAU;YACR,kBAAkB,CACrB,CAAC;QAEJ,MAAM,oBAAoB,GACxB,CAAC;YACD,KAAK,CAAC,mBAAmB,CAAC;QAC5B,MAAM,kBAAkB,GACtB,QAAQ;YACR,KAAK,CAAC,mBAAmB,CAAC;QAE5B,MAAM,KAAK,GACT,KAAK,CAAC,IAAI,CACR;YACE,MAAM,EACJ,KAAK,CAAC,mBAAmB;SAC5B,EACD,CACE,CAAC,EACD,mBAAmB,EACnB,EAAE;YACF,MAAM,kBAAkB,GACtB,CAAC,mBAAmB;gBAClB,GAAG,CAAC;gBACN,KAAK,CAAC,mBAAmB,CAAC;YAC5B,MAAM,WAAW,GACf,QAAQ;iBACL,sCAAsC;gBACzC,kBAAkB;oBAChB,QAAQ,CAAC;YAEb,MAAM,gBAAgB,GAAG;gBACvB,CAAC,EACC,iBAAiB,CAAC,CAAC;oBACnB,CAAC,eAAe,CAAC,CAAC;wBAChB,cAAc,CAAC,CAAC,CAAC;wBACjB,WAAW;gBACf,CAAC,EACC,iBAAiB,CAAC,CAAC;oBACnB,CAAC,eAAe,CAAC,CAAC;wBAChB,cAAc,CAAC,CAAC,CAAC;wBACjB,WAAW;gBACf,CAAC,EACC,iBAAiB,CAAC,CAAC;oBACnB,CAAC,eAAe,CAAC,CAAC;wBAChB,cAAc,CAAC,CAAC,CAAC;wBACjB,WAAW;aAChB,CAAC;YAEF,MAAM,MAAM,GACV,YAAY,CACV,gBAAgB,EAChB,oBAAoB,EACpB,UAAU;gBACR,0BAA0B,CAC7B,CAAC;YACJ,MAAM,OAAO,GAAG;gBACd,CAAC,EACC,MAAM,CAAC,CAAC;oBACR,mBAAmB,CAAC,CAAC;gBACvB,CAAC,EACC,MAAM,CAAC,CAAC;oBACR,mBAAmB,CAAC,CAAC;aACxB,CAAC;YACF,MAAM,WAAW,GAAG;gBAClB,CAAC,EACC,OAAO,CAAC,CAAC;oBACT,MAAM;gBACR,CAAC,EACC,CAAC,OAAO,CAAC,CAAC;oBACV,MAAM;aACT,CAAC;YACF,MAAM,QAAQ,GACZ,qCAAqC,CAAC;gBACpC,MAAM,EACJ,WAAW;gBACb,WAAW,EACT,KAAK,CAAC,WAAW;aACpB,CAAC,CAAC;YAEL,OAAO;gBACL,mBAAmB;gBACnB,kBAAkB;gBAClB,+BAA+B,EAC7B,WAAW;gBACb,oBAAoB;gBACpB,kBAAkB;gBAClB,uBAAuB,EACrB,gBAAgB;gBAClB,kBAAkB,EAChB,MAAM;gBACR,8BAA8B,EAAE;oBAC9B,GAAG,OAAO;oBACV,QAAQ,EACN,IAAI,CAAC,KAAK,CACR,OAAO,CAAC,CAAC,EACT,OAAO,CAAC,CAAC,CACV;iBACJ;gBACD,kBAAkB,EAAE;oBAClB,GAAG,WAAW;oBACd,QAAQ,EACN,IAAI,CAAC,KAAK,CACR,WAAW,CAAC,CAAC,EACb,WAAW,CAAC,CAAC,CACd;iBACJ;gBACD,oBAAoB,EAAE;oBACpB,GAAG,QAAQ;oBACX,QAAQ,EACN,IAAI,CAAC,KAAK,CACR,QAAQ,CAAC,CAAC,EACV,QAAQ,CAAC,CAAC,CACX;iBACJ;aACF,CAAC;QACJ,CAAC,CACF,CAAC;QAEJ,OAAO;YACL,QAAQ;YACR,sBAAsB,EAAE;gBACtB,GAAG,WAAW;qBACX,sBAAsB;aAC1B;YACD,wBAAwB,EACtB,iBAAiB;YACnB,kBAAkB,EAChB,eAAe;YACjB,mBAAmB,EAAE;gBACnB,gCAAgC,EAC9B,QAAQ;qBACL,sCAAsC;gBAC3C,8BAA8B,EAC5B,QAAQ;qBACL,oCAAoC;gBACzC,eAAe,EACb,QAAQ;aACX;YACD,qBAAqB,EACnB,mBAAmB;YACrB,KAAK;SACN,CAAC;IACJ,CAAC,CACF,CAAC;IAEJ,OAAO,mBAAmB,CACxB;QACE,aAAa,EACX,8BAA8B;QAChC,qBAAqB,EAAE;YACrB,SAAS,EACP,KAAK,CAAC,MAAM;iBACT,eAAe;YACpB,cAAc,EACZ,KAAK,CAAC,MAAM;iBACT,oBAAoB;YACzB,aAAa,EACX,KAAK,CAAC,MAAM;iBACT,aAAa;SACnB;QACD,WAAW,EACT,KAAK,CAAC,WAAW;QACnB,oBAAoB;QACpB,mBAAmB,EACjB,KAAK,CAAC,mBAAmB;QAC3B,4BAA4B,EAC1B,cAAc;QAChB,OAAO;QACP,aAAa,EACX,oDAAoD;QACtD,yBAAyB,EACvB,kCAAkC;QACpC,wBAAwB,EACtB,IAAI;QACN,6BAA6B,EAC3B,KAAK;QACP,sBAAsB,EACpB,KAAK;QACP,0BAA0B,EACxB,KAAK;QACP,uCAAuC,EACrC,KAAK;KACR,EACD,kDAAkD,EAClD,OAAO,EACP;QACE,yHAAyH;QACzH,iIAAiI;QACjI,+GAA+G;QAC/G,gJAAgJ;QAChJ,8IAA8I;QAC9I,sEAAsE;QACtE,wGAAwG;KACzG,CACF,CAAC;AACJ,CAAC","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Parallax from camera movement (translation): nearer points shift more in the image than distant\n * ones. It is evaluated at deterministic midpoint time samples across each scene point's local\n * exposure window.\n *\n * The model works point by point, on purpose. Camera movement and optional subject movement combine\n * as relative linear motion; neither is recast as the other. Camera rotation, and visibility or\n * occlusion, are separate models.\n * @see docs/MOTION_AND_SIGNAL.md for the equations, units, coordinate conventions, known blockers\n * and support limits.\n */\n\nimport {\n approximationResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport {\n InvalidScientificInputError,\n requirePositiveFinite,\n requirePositiveInteger\n} from \"../core/validation.js\";\nimport {\n transformNativeRasterVectorToOriented,\n type CaptureOrientation,\n type RasterPoint\n} from \"../output/capture-geometry.js\";\nimport { calculateThinLensImageDistance } from \"../optics/thin-lens.js\";\nimport type { Vector3 } from \"../schema/scene.js\";\nimport type {\n ResolvedCaptureModeTiming\n} from \"../sensor/capture-mode-timing.js\";\nimport type {\n SensorImagingArea\n} from \"../sensor/sensor-geometry.js\";\nimport { requireInputObject, requireInputKeys, requireTrimmedConfigString as requireNonEmptyString } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { sameSite as samePoint } from \"../core/shared-helpers.js\";\n\nexport interface CaptureTranslationParallaxSceneSample {\n sampleId: string;\n /**\n * Native destination point. Its local exposure window applies to this scene\n * sample.\n */\n destinationPointNative:\n RasterPoint;\n /**\n * Point position in metres, in the camera axes at the first opening-boundary\n * phase. +X right, +Y up, +Z forward (away from the camera).\n */\n positionCameraM: Vector3;\n /**\n * Optional constant velocity of the subject (world point), in the same\n * starting camera axes. If omitted, the world point is still.\n */\n subjectVelocityMps?: Vector3;\n}\n\nexport interface CalculateCaptureTranslationParallaxTemporalQuadratureInput {\n timing:\n ResolvedCaptureModeTiming;\n imagingArea:\n SensorImagingArea;\n focalLengthMm: number;\n focusDistanceM?: number;\n orientation:\n CaptureOrientation;\n /**\n * Constant movement of the camera centre, in camera axes at the capture\n * reference phase. Rotation is handled separately, on purpose.\n */\n cameraTranslationVelocityMps:\n Vector3;\n temporalSampleCount: number;\n sceneSamples:\n readonly CaptureTranslationParallaxSceneSample[];\n}\n\nexport interface CaptureTranslationParallaxTemporalNode {\n temporalSampleIndex: number;\n localExposurePhase: number;\n captureTimeSecondsFromReference:\n number;\n normalizedTimeWeight: number;\n timeMeasureSeconds: number;\n relativePositionCameraM:\n Vector3;\n mappedImagePointMm: {\n x: number;\n y: number;\n };\n deltaFromReferenceImagePlaneMm: {\n x: number;\n y: number;\n distance: number;\n };\n deltaNativeSamples: {\n x: number;\n y: number;\n distance: number;\n };\n deltaOrientedSamples: {\n x: number;\n y: number;\n distance: number;\n };\n}\n\nexport interface CaptureTranslationParallaxTemporalSample {\n sampleId: string;\n destinationPointNative:\n RasterPoint;\n referencePositionCameraM:\n Vector3;\n subjectVelocityMps: Vector3;\n localExposureWindow: {\n startSecondsFromCaptureReference:\n number;\n endSecondsFromCaptureReference:\n number;\n durationSeconds: number;\n };\n referenceImagePointMm: {\n x: number;\n y: number;\n };\n nodes:\n readonly CaptureTranslationParallaxTemporalNode[];\n}\n\nexport interface CaptureTranslationParallaxTemporalQuadrature {\n timeReference:\n \"first-opening-boundary-phase\";\n timingProfileIdentity: {\n profileId: string;\n profileVersion: string;\n captureModeId: string;\n };\n orientation:\n CaptureOrientation;\n projectionDistanceMm: number;\n temporalSampleCount: number;\n cameraTranslationVelocityMps:\n Vector3;\n samples:\n readonly CaptureTranslationParallaxTemporalSample[];\n geometryModel:\n \"metric-depth-dependent-constant-linear-translation\";\n relativeMotionComposition:\n \"subject-minus-camera-translation\";\n metricSceneDepthRequired: true;\n oneGlobalHomographyAuthorized: false;\n cameraRotationComposed: false;\n visibilityOcclusionModeled: false;\n sensorReadoutTimingUsedAsExposureTiming:\n false;\n}\n\nexport function requireFiniteVector(\n value: Vector3,\n path: string\n): Vector3 {\n requireInputObject(value, path);\n for (\n const axis of\n [\"x\", \"y\", \"z\"] as const\n ) {\n if (\n !Number.isFinite(\n value[axis]\n )\n ) {\n throw new InvalidScientificInputError(\n path +\n \".\" +\n axis +\n \" must be finite.\"\n );\n }\n }\n return {\n ...value\n };\n}\n\nfunction projectPoint(\n positionM: Vector3,\n projectionDistanceMm: number,\n path: string\n): {\n x: number;\n y: number;\n} {\n if (\n positionM.z <= 0\n ) {\n throw new InvalidScientificInputError(\n path +\n \" must remain in front of the camera plane.\"\n );\n }\n const mapped = {\n x:\n projectionDistanceMm *\n (positionM.x /\n positionM.z),\n y:\n projectionDistanceMm *\n (positionM.y /\n positionM.z)\n };\n if (\n !Number.isFinite(mapped.x) ||\n !Number.isFinite(mapped.y)\n ) {\n throw new InvalidScientificInputError(\n path +\n \" projection must remain finite.\"\n );\n }\n return mapped;\n}\n\nfunction localExposureForPoint(\n timing:\n ResolvedCaptureModeTiming,\n point:\n RasterPoint\n): ResolvedCaptureModeTiming[\"exposureWindows\"][\"samples\"][number] {\n requireInputObject(point, \"destinationPointNative\");\n const matches =\n timing.exposureWindows.samples.filter(\n (sample) =>\n samePoint(\n sample.pointNative,\n point\n )\n );\n if (matches.length !== 1) {\n throw new InvalidScientificInputError(\n \"Each translation/parallax scene sample must match exactly one committed exposure-window sample point.\"\n );\n }\n return matches[0]!;\n}\n\n/**\n * Evaluates depth-dependent parallax from camera movement at deterministic\n * midpoint time samples across each scene point's local exposure window.\n *\n * The model works point by point, on purpose. Camera movement and optional\n * subject movement combine as relative linear motion; neither is recast as\n * the other. Camera rotation, and visibility or occlusion, are separate\n * models.\n */\nexport function calculateCaptureTranslationParallaxTemporalQuadrature(\n input:\n CalculateCaptureTranslationParallaxTemporalQuadratureInput\n): CalculationResult<CaptureTranslationParallaxTemporalQuadrature> {\n requireInputKeys(input, \"calculateCaptureTranslationParallaxTemporalQuadrature\", [\"timing\", \"imagingArea\", \"focalLengthMm\", \"focusDistanceM\", \"orientation\", \"cameraTranslationVelocityMps\", \"temporalSampleCount\", \"sceneSamples\"]);\n requireInputObject(input.imagingArea, \"imagingArea\");\n requireInputObject(input.timing, \"timing\");\n requireInputObject(input.timing.nativeRaster, \"timing.nativeRaster\");\n requireInputObject(input.timing.exposureWindows, \"timing.exposureWindows\");\n if (!Array.isArray(input.timing.exposureWindows.samples)) throw new InvalidConfigurationError(\"timing.exposureWindows.samples must be an array.\");\n input.timing.exposureWindows.samples.forEach((sample, index) => {\n requireInputObject(sample, \"timing.exposureWindows.samples[\" + index + \"]\");\n requireInputObject(sample.pointNative, \"timing.exposureWindows.samples[\" + index + \"].pointNative\");\n });\n requirePositiveFinite(\n \"imagingArea.widthMm\",\n input.imagingArea.widthMm\n );\n requirePositiveFinite(\n \"imagingArea.heightMm\",\n input.imagingArea.heightMm\n );\n requirePositiveFinite(\n \"focalLengthMm\",\n input.focalLengthMm\n );\n requirePositiveInteger(\n \"temporalSampleCount\",\n input.temporalSampleCount\n );\n\n if (\n !Array.isArray(\n input.sceneSamples\n ) ||\n input.sceneSamples.length === 0\n ) {\n throw new InvalidConfigurationError(\n \"sceneSamples must be a non-empty array.\"\n );\n }\n\n input.sceneSamples.forEach((sample, index) => requireInputObject(sample, \"sceneSamples[\" + index + \"]\"));\n const ids =\n input.sceneSamples.map(\n (sample) =>\n requireNonEmptyString(\n sample.sampleId,\n \"sceneSamples[].sampleId\"\n )\n );\n if (\n new Set(ids).size !==\n ids.length\n ) {\n throw new InvalidScientificInputError(\n \"sceneSamples must not contain duplicate sampleId values.\"\n );\n }\n\n const cameraVelocity =\n requireFiniteVector(\n input\n .cameraTranslationVelocityMps,\n \"cameraTranslationVelocityMps\"\n );\n\n const projectionDistanceMm =\n input.focusDistanceM ===\n undefined\n ? input.focalLengthMm\n : calculateThinLensImageDistance({\n focalLengthMm:\n input.focalLengthMm,\n objectDistanceM:\n input.focusDistanceM\n }).value.imageDistanceMm;\n\n const pitchX =\n input.imagingArea.widthMm /\n input.timing.nativeRaster\n .pixelWidth;\n const pitchY =\n input.imagingArea.heightMm /\n input.timing.nativeRaster\n .pixelHeight;\n\n if (\n !Number.isFinite(pitchX) ||\n !Number.isFinite(pitchY) ||\n pitchX <= 0 ||\n pitchY <= 0\n ) {\n throw new InvalidScientificInputError(\n \"Resolved translation/parallax sampling pitch must remain finite and positive.\"\n );\n }\n\n const samples =\n input.sceneSamples.map(\n (sceneSample, sampleIndex) => {\n const samplePath =\n \"sceneSamples[\" +\n sampleIndex +\n \"]\";\n const sampleId =\n requireNonEmptyString(\n sceneSample.sampleId,\n samplePath +\n \".sampleId\"\n );\n const referencePosition =\n requireFiniteVector(\n sceneSample\n .positionCameraM,\n samplePath +\n \".positionCameraM\"\n );\n if (\n referencePosition.z <= 0\n ) {\n throw new InvalidScientificInputError(\n samplePath +\n \".positionCameraM.z must place the scene point in front of the camera.\"\n );\n }\n const subjectVelocity =\n sceneSample\n .subjectVelocityMps ===\n undefined\n ? {\n x: 0,\n y: 0,\n z: 0\n }\n : requireFiniteVector(\n sceneSample\n .subjectVelocityMps,\n samplePath +\n \".subjectVelocityMps\"\n );\n\n const exposure =\n localExposureForPoint(\n input.timing,\n sceneSample\n .destinationPointNative\n );\n const duration =\n exposure\n .localExposureDurationSeconds;\n if (\n !Number.isFinite(\n duration\n ) ||\n duration <= 0\n ) {\n throw new InvalidScientificInputError(\n \"Translation/parallax requires a positive finite local exposure duration.\"\n );\n }\n\n const referenceImagePoint =\n projectPoint(\n referencePosition,\n projectionDistanceMm,\n samplePath +\n \".positionCameraM\"\n );\n\n const normalizedTimeWeight =\n 1 /\n input.temporalSampleCount;\n const timeMeasureSeconds =\n duration /\n input.temporalSampleCount;\n\n const nodes =\n Array.from(\n {\n length:\n input.temporalSampleCount\n },\n (\n _,\n temporalSampleIndex\n ) => {\n const localExposurePhase =\n (temporalSampleIndex +\n 0.5) /\n input.temporalSampleCount;\n const captureTime =\n exposure\n .startOffsetSecondsFromOpeningReference +\n localExposurePhase *\n duration;\n\n const relativePosition = {\n x:\n referencePosition.x +\n (subjectVelocity.x -\n cameraVelocity.x) *\n captureTime,\n y:\n referencePosition.y +\n (subjectVelocity.y -\n cameraVelocity.y) *\n captureTime,\n z:\n referencePosition.z +\n (subjectVelocity.z -\n cameraVelocity.z) *\n captureTime\n };\n\n const mapped =\n projectPoint(\n relativePosition,\n projectionDistanceMm,\n samplePath +\n \".relativePositionCameraM\"\n );\n const deltaMm = {\n x:\n mapped.x -\n referenceImagePoint.x,\n y:\n mapped.y -\n referenceImagePoint.y\n };\n const deltaNative = {\n x:\n deltaMm.x /\n pitchX,\n y:\n -deltaMm.y /\n pitchY\n };\n const oriented =\n transformNativeRasterVectorToOriented({\n vector:\n deltaNative,\n orientation:\n input.orientation\n });\n\n return {\n temporalSampleIndex,\n localExposurePhase,\n captureTimeSecondsFromReference:\n captureTime,\n normalizedTimeWeight,\n timeMeasureSeconds,\n relativePositionCameraM:\n relativePosition,\n mappedImagePointMm:\n mapped,\n deltaFromReferenceImagePlaneMm: {\n ...deltaMm,\n distance:\n Math.hypot(\n deltaMm.x,\n deltaMm.y\n )\n },\n deltaNativeSamples: {\n ...deltaNative,\n distance:\n Math.hypot(\n deltaNative.x,\n deltaNative.y\n )\n },\n deltaOrientedSamples: {\n ...oriented,\n distance:\n Math.hypot(\n oriented.x,\n oriented.y\n )\n }\n };\n }\n );\n\n return {\n sampleId,\n destinationPointNative: {\n ...sceneSample\n .destinationPointNative\n },\n referencePositionCameraM:\n referencePosition,\n subjectVelocityMps:\n subjectVelocity,\n localExposureWindow: {\n startSecondsFromCaptureReference:\n exposure\n .startOffsetSecondsFromOpeningReference,\n endSecondsFromCaptureReference:\n exposure\n .endOffsetSecondsFromOpeningReference,\n durationSeconds:\n duration\n },\n referenceImagePointMm:\n referenceImagePoint,\n nodes\n };\n }\n );\n\n return approximationResult(\n {\n timeReference:\n \"first-opening-boundary-phase\",\n timingProfileIdentity: {\n profileId:\n input.timing\n .timingProfileId,\n profileVersion:\n input.timing\n .timingProfileVersion,\n captureModeId:\n input.timing\n .captureModeId\n },\n orientation:\n input.orientation,\n projectionDistanceMm,\n temporalSampleCount:\n input.temporalSampleCount,\n cameraTranslationVelocityMps:\n cameraVelocity,\n samples,\n geometryModel:\n \"metric-depth-dependent-constant-linear-translation\",\n relativeMotionComposition:\n \"subject-minus-camera-translation\",\n metricSceneDepthRequired:\n true,\n oneGlobalHomographyAuthorized:\n false,\n cameraRotationComposed:\n false,\n visibilityOcclusionModeled:\n false,\n sensorReadoutTimingUsedAsExposureTiming:\n false\n },\n \"capture-translation-parallax-temporal-quadrature\",\n \"1.0.0\",\n [\n \"Translation is evaluated independently for each metric scene point because translational image motion depends on depth.\",\n \"Camera and subject translation coexist as explicit relative linear motion; subject motion is not reclassified as camera motion.\",\n \"The camera-axis basis is fixed at the first-opening-boundary phase; camera rotation remains a separate model.\",\n \"The destination native point selects the authoritative local exposure window; sensor data-readout timing is not substituted for exposure time.\",\n \"Oriented sample displacement is a coordinate transform of the same physical image-plane mapping and does not change the translation physics.\",\n \"No one global homography/warp is authorized for arbitrary 3D scenes.\",\n \"Visibility, occlusion, deformation, acceleration and rolling scene geometry remain outside this model.\"\n ]\n );\n}\n"]}
@@ -14,43 +14,12 @@
14
14
  import { approximationResult } from "../core/calculation-result.js";
15
15
  import { InvalidScientificInputError, requirePositiveFinite, requirePositiveInteger } from "../core/validation.js";
16
16
  import { calculateThinLensImageDistance } from "../optics/thin-lens.js";
17
- import { requireInputKeys, requireInputObject } from "../core/record-validation.js";
17
+ import { requireInputKeys, requireInputObject, requireTrimmedConfigString as requireNonEmptyString } from "../core/record-validation.js";
18
18
  import { InvalidConfigurationError } from "../core/configuration-error.js";
19
- function requireNonEmptyString(value, path) {
20
- if (typeof value !== "string" ||
21
- value.trim().length === 0) {
22
- throw new InvalidConfigurationError(path +
23
- " must be a non-empty string.");
24
- }
25
- return value.trim();
26
- }
27
- function requireFiniteVector(value, path) {
28
- requireInputObject(value, path);
29
- for (const axis of ["x", "y", "z"]) {
30
- if (!Number.isFinite(value[axis])) {
31
- throw new InvalidScientificInputError(path +
32
- "." +
33
- axis +
34
- " must be finite.");
35
- }
36
- }
37
- return {
38
- ...value
39
- };
40
- }
41
- function requireFiniteTime(value, path) {
42
- if (!Number.isFinite(value)) {
43
- throw new InvalidScientificInputError(path + " must be finite.");
44
- }
45
- return value;
46
- }
47
- function approximatelyEqual(a, b) {
48
- const scale = Math.max(1, Math.abs(a), Math.abs(b));
49
- return (Math.abs(a - b) <=
50
- Number.EPSILON *
51
- 32 *
52
- scale);
53
- }
19
+ import { sameSite as samePoint } from "../core/shared-helpers.js";
20
+ import { requireFiniteVector } from "./capture-translation-parallax-temporal-quadrature.js";
21
+ import { requireScientificFinite as requireFiniteTime } from "../color/white-balance.js";
22
+ import { settingEquals as approximatelyEqual } from "../composition/image-formation-plan.js";
54
23
  function resolveProjectionDistanceMm(focalLengthMm, focusDistanceM) {
55
24
  requirePositiveFinite("focalLengthMm", focalLengthMm);
56
25
  return focusDistanceM ===
@@ -285,10 +254,6 @@ export function calculateExtendedObjectProjectionTrajectory(input) {
285
254
  "When relative Z translation is nonzero, downstream defocus/PSF state may also vary with time."
286
255
  ]);
287
256
  }
288
- function samePoint(a, b) {
289
- return (a.x === b.x &&
290
- a.y === b.y);
291
- }
292
257
  function localExposureForPoint(timing, point) {
293
258
  requireInputObject(timing, "timing");
294
259
  requireInputObject(timing.exposureWindows, "timing.exposureWindows");
@@ -1 +1 @@
1
- {"version":3,"file":"extended-object-projection.js","sourceRoot":"","sources":["../../src/motion/extended-object-projection.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Projection of an extended object: several explicit points (in metres) on one rigid object that\n * moves in a straight line.\n *\n * The points are evaluated at deterministic physical times, or at deterministic midpoint time\n * samples inside each point's local exposure window (from the capture exposure schedule).\n *\n * This is geometry only. It does not build a blur kernel, decide visibility, integrate radiance,\n * deform the object, rotate the camera or apply a universal scale.\n * @see docs/MOTION_AND_SIGNAL.md for the equations, units, coordinate conventions, known blockers\n * and support limits.\n */\n\nimport {\n approximationResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport {\n InvalidScientificInputError,\n requirePositiveFinite,\n requirePositiveInteger\n} from \"../core/validation.js\";\nimport type {\n RasterPoint\n} from \"../output/capture-geometry.js\";\nimport {\n calculateThinLensImageDistance\n} from \"../optics/thin-lens.js\";\nimport type {\n Vector3\n} from \"../schema/scene.js\";\nimport type {\n ResolvedCaptureModeTiming\n} from \"../sensor/capture-mode-timing.js\";\nimport { requireInputKeys, requireInputObject } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\n\nexport interface ExtendedObjectMetricPoint {\n pointId: string;\n /**\n * Point in camera space, in metres, at the shared reference phase.\n *\n * +X right, +Y up, +Z forward/away from the camera.\n */\n positionCameraM: Vector3;\n}\n\nexport interface ExtendedObjectFrontoparallelPlaneDeclaration {\n kind: \"fronto-parallel-planar-patch\";\n referenceDepthM: number;\n}\n\nexport interface CalculateExtendedObjectProjectionTrajectoryInput {\n objectId: string;\n focalLengthMm: number;\n focusDistanceM?: number;\n objectTranslationVelocityMps:\n Vector3;\n cameraTranslationVelocityMps:\n Vector3;\n sampleTimesSecondsFromReference:\n readonly number[];\n points:\n readonly ExtendedObjectMetricPoint[];\n frontoparallelPlane?:\n ExtendedObjectFrontoparallelPlaneDeclaration;\n}\n\nexport interface ExtendedObjectProjectionNode {\n sampleIndex: number;\n timeSecondsFromReference: number;\n relativePositionCameraM:\n Vector3;\n mappedImagePointMm: {\n x: number;\n y: number;\n };\n deltaFromReferenceImagePlaneMm: {\n x: number;\n y: number;\n distance: number;\n };\n depthM: number;\n planarMagnificationScaleFromReference:\n number | null;\n}\n\nexport interface ExtendedObjectPointProjectionTrajectory {\n pointId: string;\n referencePositionCameraM:\n Vector3;\n referenceImagePointMm: {\n x: number;\n y: number;\n };\n nodes:\n readonly ExtendedObjectProjectionNode[];\n}\n\nexport interface ExtendedObjectPlanarMagnificationDiagnostic {\n geometry:\n \"declared-fronto-parallel-planar-patch\";\n referenceDepthM: number;\n uniformScaleAuthorizedWithinDeclaredPatch:\n true;\n scaleByTime:\n readonly {\n sampleIndex: number;\n timeSecondsFromReference:\n number;\n scaleFromReference: number;\n }[];\n lateralRigidTranslationDoesNotChangePatchScale:\n true;\n notAuthorizedForArbitrary3dGeometry:\n true;\n}\n\nexport interface ExtendedObjectProjectionTrajectory {\n objectId: string;\n timeReference:\n \"caller-declared-common-reference-phase\";\n projectionDistanceMm: number;\n objectTranslationVelocityMps:\n Vector3;\n cameraTranslationVelocityMps:\n Vector3;\n relativeTranslationVelocityMps:\n Vector3;\n rigidTranslationOnly: true;\n pointTrajectories:\n readonly ExtendedObjectPointProjectionTrajectory[];\n planarMagnificationDiagnostic:\n ExtendedObjectPlanarMagnificationDiagnostic | null;\n metricSceneDepthRequired: true;\n oneGlobalHomographyAuthorized:\n false;\n oneGlobalScaleAuthorizedForArbitrary3dGeometry:\n false;\n cameraRotationComposed: false;\n visibilityOcclusionModeled: false;\n deformationModeled: false;\n accelerationModeled: false;\n blurKernelCalculated: false;\n radianceIntegrated: false;\n timeVaryingDefocusMayBeRequired:\n boolean;\n}\n\nexport interface CaptureExtendedObjectMetricPoint\n extends ExtendedObjectMetricPoint {\n /**\n * Native destination point. It selects the local exposure window (from the\n * capture exposure schedule) for this object point.\n */\n destinationPointNative:\n RasterPoint;\n}\n\nexport interface CalculateCaptureExtendedObjectTemporalProjectionInput {\n objectId: string;\n timing:\n ResolvedCaptureModeTiming;\n focalLengthMm: number;\n focusDistanceM?: number;\n objectTranslationVelocityMps:\n Vector3;\n cameraTranslationVelocityMps:\n Vector3;\n temporalSampleCount: number;\n points:\n readonly CaptureExtendedObjectMetricPoint[];\n frontoparallelPlane?:\n ExtendedObjectFrontoparallelPlaneDeclaration;\n}\n\nexport interface CaptureExtendedObjectTemporalNode\n extends ExtendedObjectProjectionNode {\n localExposurePhase: number;\n normalizedTimeWeight: number;\n timeMeasureSeconds: number;\n}\n\nexport interface CaptureExtendedObjectPointTrajectory {\n pointId: string;\n destinationPointNative:\n RasterPoint;\n referencePositionCameraM:\n Vector3;\n referenceImagePointMm: {\n x: number;\n y: number;\n };\n localExposureWindow: {\n startSecondsFromCaptureReference:\n number;\n endSecondsFromCaptureReference:\n number;\n durationSeconds: number;\n };\n nodes:\n readonly CaptureExtendedObjectTemporalNode[];\n}\n\nexport interface CaptureExtendedObjectTemporalProjection {\n objectId: string;\n timeReference:\n \"first-opening-boundary-phase\";\n timingProfileIdentity: {\n profileId: string;\n profileVersion: string;\n captureModeId: string;\n };\n projectionDistanceMm: number;\n temporalSampleCount: number;\n objectTranslationVelocityMps:\n Vector3;\n cameraTranslationVelocityMps:\n Vector3;\n relativeTranslationVelocityMps:\n Vector3;\n rigidTranslationOnly: true;\n pointTrajectories:\n readonly CaptureExtendedObjectPointTrajectory[];\n planarMagnificationDiagnosticAvailable:\n boolean;\n metricSceneDepthRequired: true;\n oneGlobalHomographyAuthorized:\n false;\n oneGlobalScaleAuthorizedForArbitrary3dGeometry:\n false;\n cameraRotationComposed: false;\n visibilityOcclusionModeled: false;\n deformationModeled: false;\n accelerationModeled: false;\n blurKernelCalculated: false;\n radianceIntegrated: false;\n sensorReadoutTimingUsedAsExposureTiming:\n false;\n timeVaryingDefocusMayBeRequired:\n boolean;\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 +\n \" must be a non-empty string.\"\n );\n }\n return value.trim();\n}\n\nfunction requireFiniteVector(\n value: Vector3,\n path: string\n): Vector3 {\n requireInputObject(value, path);\n for (\n const axis of\n [\"x\", \"y\", \"z\"] as const\n ) {\n if (\n !Number.isFinite(\n value[axis]\n )\n ) {\n throw new InvalidScientificInputError(\n path +\n \".\" +\n axis +\n \" must be finite.\"\n );\n }\n }\n return {\n ...value\n };\n}\n\nfunction requireFiniteTime(\n value: number,\n path: string\n): number {\n if (!Number.isFinite(value)) {\n throw new InvalidScientificInputError(\n path + \" must be finite.\"\n );\n }\n return value;\n}\n\nfunction approximatelyEqual(\n a: number,\n b: number\n): boolean {\n const scale =\n Math.max(\n 1,\n Math.abs(a),\n Math.abs(b)\n );\n return (\n Math.abs(a - b) <=\n Number.EPSILON *\n 32 *\n scale\n );\n}\n\nfunction resolveProjectionDistanceMm(\n focalLengthMm: number,\n focusDistanceM:\n number | undefined\n): number {\n requirePositiveFinite(\n \"focalLengthMm\",\n focalLengthMm\n );\n return focusDistanceM ===\n undefined\n ? focalLengthMm\n : calculateThinLensImageDistance({\n focalLengthMm,\n objectDistanceM:\n focusDistanceM\n }).value.imageDistanceMm;\n}\n\nfunction projectMetricPoint(\n positionCameraM:\n Vector3,\n projectionDistanceMm: number,\n path: string\n): {\n x: number;\n y: number;\n} {\n if (\n !Number.isFinite(\n positionCameraM.z\n ) ||\n positionCameraM.z <= 0\n ) {\n throw new InvalidScientificInputError(\n path +\n \" must remain in front of the camera plane.\"\n );\n }\n\n const mapped = {\n x:\n projectionDistanceMm *\n (\n positionCameraM.x /\n positionCameraM.z\n ),\n y:\n projectionDistanceMm *\n (\n positionCameraM.y /\n positionCameraM.z\n )\n };\n if (\n !Number.isFinite(mapped.x) ||\n !Number.isFinite(mapped.y)\n ) {\n throw new InvalidScientificInputError(\n path +\n \" projection must remain finite.\"\n );\n }\n return mapped;\n}\n\nfunction validateUniquePoints<T extends ExtendedObjectMetricPoint>(\n points: readonly T[]\n): readonly {\n point:\n T;\n pointId: string;\n referencePosition:\n Vector3;\n}[] {\n if (\n !Array.isArray(points) ||\n points.length === 0\n ) {\n throw new InvalidConfigurationError(\n \"points must be a non-empty array.\"\n );\n }\n\n const parsed =\n points.map(\n (point, index) => {\n const path =\n \"points[\" +\n index +\n \"]\";\n requireInputObject(point, path);\n const pointId =\n requireNonEmptyString(\n point.pointId,\n path + \".pointId\"\n );\n const referencePosition =\n requireFiniteVector(\n point.positionCameraM,\n path +\n \".positionCameraM\"\n );\n if (\n referencePosition.z <= 0\n ) {\n throw new InvalidScientificInputError(\n path +\n \".positionCameraM.z must place the point in front of the camera.\"\n );\n }\n return {\n point,\n pointId,\n referencePosition\n };\n }\n );\n\n const ids =\n parsed.map(\n (entry) =>\n entry.pointId\n );\n if (\n new Set(ids).size !==\n ids.length\n ) {\n throw new InvalidScientificInputError(\n \"points must not contain duplicate pointId values.\"\n );\n }\n return parsed;\n}\n\nfunction validateSampleTimes(\n times:\n readonly number[]\n): readonly number[] {\n if (\n !Array.isArray(times) ||\n times.length === 0\n ) {\n throw new InvalidConfigurationError(\n \"sampleTimesSecondsFromReference must be a non-empty array.\"\n );\n }\n let prior =\n Number.NEGATIVE_INFINITY;\n return times.map(\n (value, index) => {\n const time =\n requireFiniteTime(\n value,\n \"sampleTimesSecondsFromReference[\" +\n index +\n \"]\"\n );\n if (time <= prior) {\n throw new InvalidScientificInputError(\n \"sampleTimesSecondsFromReference must be strictly increasing.\"\n );\n }\n prior = time;\n return time;\n }\n );\n}\n\nfunction relativeVelocity(\n objectVelocity:\n Vector3,\n cameraVelocity:\n Vector3\n): Vector3 {\n return {\n x:\n objectVelocity.x -\n cameraVelocity.x,\n y:\n objectVelocity.y -\n cameraVelocity.y,\n z:\n objectVelocity.z -\n cameraVelocity.z\n };\n}\n\nfunction positionAtTime(\n reference:\n Vector3,\n velocity:\n Vector3,\n timeSeconds: number\n): Vector3 {\n return {\n x:\n reference.x +\n velocity.x *\n timeSeconds,\n y:\n reference.y +\n velocity.y *\n timeSeconds,\n z:\n reference.z +\n velocity.z *\n timeSeconds\n };\n}\n\nfunction validatePlanarDeclaration(\n declaration:\n ExtendedObjectFrontoparallelPlaneDeclaration |\n undefined,\n points:\n readonly {\n referencePosition:\n Vector3;\n }[]\n): number | null {\n if (\n declaration === undefined\n ) {\n return null;\n }\n requireInputObject(declaration, \"frontoparallelPlane\");\n if (\n declaration.kind !==\n \"fronto-parallel-planar-patch\"\n ) {\n throw new InvalidConfigurationError(\n \"frontoparallelPlane.kind is invalid.\"\n );\n }\n requirePositiveFinite(\n \"frontoparallelPlane.referenceDepthM\",\n declaration.referenceDepthM\n );\n for (\n const entry of points\n ) {\n if (\n !approximatelyEqual(\n entry.referencePosition.z,\n declaration.referenceDepthM\n )\n ) {\n throw new InvalidScientificInputError(\n \"Every declared fronto-parallel point must lie at frontoparallelPlane.referenceDepthM.\"\n );\n }\n }\n return declaration\n .referenceDepthM;\n}\n\nfunction createPointTrajectory(\n pointId: string,\n referencePosition:\n Vector3,\n relativeTranslationVelocityMps:\n Vector3,\n sampleTimes:\n readonly number[],\n projectionDistanceMm:\n number,\n planarReferenceDepthM:\n number | null,\n path: string\n): ExtendedObjectPointProjectionTrajectory {\n const referenceImagePoint =\n projectMetricPoint(\n referencePosition,\n projectionDistanceMm,\n path +\n \".positionCameraM\"\n );\n\n const nodes =\n sampleTimes.map(\n (\n timeSeconds,\n sampleIndex\n ) => {\n const position =\n positionAtTime(\n referencePosition,\n relativeTranslationVelocityMps,\n timeSeconds\n );\n const mapped =\n projectMetricPoint(\n position,\n projectionDistanceMm,\n path +\n \".positionAtTime\"\n );\n const deltaX =\n mapped.x -\n referenceImagePoint.x;\n const deltaY =\n mapped.y -\n referenceImagePoint.y;\n\n return {\n sampleIndex,\n timeSecondsFromReference:\n timeSeconds,\n relativePositionCameraM:\n position,\n mappedImagePointMm:\n mapped,\n deltaFromReferenceImagePlaneMm: {\n x: deltaX,\n y: deltaY,\n distance:\n Math.hypot(\n deltaX,\n deltaY\n )\n },\n depthM:\n position.z,\n planarMagnificationScaleFromReference:\n planarReferenceDepthM ===\n null\n ? null\n : planarReferenceDepthM /\n position.z\n };\n }\n );\n\n return {\n pointId,\n referencePositionCameraM:\n referencePosition,\n referenceImagePointMm:\n referenceImagePoint,\n nodes\n };\n}\n\n/**\n * Evaluates multiple explicit metric points on one rigidly translating object\n * at deterministic physical times.\n *\n * This is geometry only. It does not build a blur kernel, decide visibility,\n * integrate radiance, deform the object, rotate the camera or apply a\n * universal scale.\n */\nexport function calculateExtendedObjectProjectionTrajectory(\n input:\n CalculateExtendedObjectProjectionTrajectoryInput\n): CalculationResult<ExtendedObjectProjectionTrajectory> {\n requireInputKeys(input, \"calculateExtendedObjectProjectionTrajectory\", [\"objectId\", \"focalLengthMm\", \"focusDistanceM\", \"objectTranslationVelocityMps\", \"cameraTranslationVelocityMps\", \"sampleTimesSecondsFromReference\", \"points\", \"frontoparallelPlane\"]);\n const objectId =\n requireNonEmptyString(\n input.objectId,\n \"objectId\"\n );\n const projectionDistanceMm =\n resolveProjectionDistanceMm(\n input.focalLengthMm,\n input.focusDistanceM\n );\n const objectVelocity =\n requireFiniteVector(\n input\n .objectTranslationVelocityMps,\n \"objectTranslationVelocityMps\"\n );\n const cameraVelocity =\n requireFiniteVector(\n input\n .cameraTranslationVelocityMps,\n \"cameraTranslationVelocityMps\"\n );\n const relative =\n relativeVelocity(\n objectVelocity,\n cameraVelocity\n );\n const sampleTimes =\n validateSampleTimes(\n input\n .sampleTimesSecondsFromReference\n );\n const points =\n validateUniquePoints(\n input.points\n );\n const planarDepth =\n validatePlanarDeclaration(\n input.frontoparallelPlane,\n points\n );\n\n const pointTrajectories =\n points.map(\n (\n entry,\n pointIndex\n ) =>\n createPointTrajectory(\n entry.pointId,\n entry.referencePosition,\n relative,\n sampleTimes,\n projectionDistanceMm,\n planarDepth,\n \"points[\" +\n pointIndex +\n \"]\"\n )\n );\n\n const scaleByTime =\n planarDepth === null\n ? null\n : sampleTimes.map(\n (\n timeSeconds,\n sampleIndex\n ) => {\n const currentDepth =\n planarDepth +\n relative.z *\n timeSeconds;\n if (\n currentDepth <= 0\n ) {\n throw new InvalidScientificInputError(\n \"Declared fronto-parallel patch reaches or crosses the camera plane.\"\n );\n }\n return {\n sampleIndex,\n timeSecondsFromReference:\n timeSeconds,\n scaleFromReference:\n planarDepth /\n currentDepth\n };\n }\n );\n\n return approximationResult(\n {\n objectId,\n timeReference:\n \"caller-declared-common-reference-phase\",\n projectionDistanceMm,\n objectTranslationVelocityMps:\n objectVelocity,\n cameraTranslationVelocityMps:\n cameraVelocity,\n relativeTranslationVelocityMps:\n relative,\n rigidTranslationOnly: true,\n pointTrajectories,\n planarMagnificationDiagnostic:\n scaleByTime === null\n ? null\n : {\n geometry:\n \"declared-fronto-parallel-planar-patch\",\n referenceDepthM:\n planarDepth!,\n uniformScaleAuthorizedWithinDeclaredPatch:\n true,\n scaleByTime,\n lateralRigidTranslationDoesNotChangePatchScale:\n true,\n notAuthorizedForArbitrary3dGeometry:\n true\n },\n metricSceneDepthRequired:\n true,\n oneGlobalHomographyAuthorized:\n false,\n oneGlobalScaleAuthorizedForArbitrary3dGeometry:\n false,\n cameraRotationComposed:\n false,\n visibilityOcclusionModeled:\n false,\n deformationModeled: false,\n accelerationModeled: false,\n blurKernelCalculated:\n false,\n radianceIntegrated: false,\n timeVaryingDefocusMayBeRequired:\n relative.z !== 0\n },\n \"extended-object-time-varying-projection\",\n \"1.0.0\",\n [\n \"Every object point is projected independently from explicit metric camera-space depth.\",\n \"Object and camera translation remain separate inputs and compose only as object-minus-camera relative translation.\",\n \"One global homography or one global scale is not authorized for arbitrary 3D geometry.\",\n \"A uniform magnification diagnostic is emitted only for an explicitly declared and validated fronto-parallel planar patch under the shared rigid-translation model.\",\n \"Constant rigid translation is the only object motion modeled; acceleration, rotation, articulation and deformation are excluded.\",\n \"Visibility, occlusion/disocclusion, frame-boundary coverage, radiance integration and blur-kernel synthesis remain downstream.\",\n \"When relative Z translation is nonzero, downstream defocus/PSF state may also vary with time.\"\n ]\n );\n}\n\nfunction samePoint(\n a: RasterPoint,\n b: RasterPoint\n): boolean {\n return (\n a.x === b.x &&\n a.y === b.y\n );\n}\n\nfunction localExposureForPoint(\n timing:\n ResolvedCaptureModeTiming,\n point:\n RasterPoint\n): ResolvedCaptureModeTiming[\"exposureWindows\"][\"samples\"][number] {\n requireInputObject(timing, \"timing\");\n requireInputObject(timing.exposureWindows, \"timing.exposureWindows\");\n if (!Array.isArray(timing.exposureWindows.samples)) throw new InvalidConfigurationError(\"timing.exposureWindows.samples must be an array.\");\n timing.exposureWindows.samples.forEach((sample, index) => {\n requireInputObject(sample, \"timing.exposureWindows.samples[\" + index + \"]\");\n requireInputObject(sample.pointNative, \"timing.exposureWindows.samples[\" + index + \"].pointNative\");\n });\n requireInputObject(point, \"destinationPointNative\");\n const matches =\n timing.exposureWindows.samples.filter(\n (sample) =>\n samePoint(\n sample.pointNative,\n point\n )\n );\n if (matches.length !== 1) {\n throw new InvalidScientificInputError(\n \"Each extended-object capture point must match exactly one committed exposure-window sample point.\"\n );\n }\n return matches[0]!;\n}\n\n/**\n * Evaluates extended-object projection at deterministic midpoint time samples\n * inside each point's local exposure window (from the capture exposure\n * schedule).\n */\nexport function calculateCaptureExtendedObjectTemporalProjection(\n input:\n CalculateCaptureExtendedObjectTemporalProjectionInput\n): CalculationResult<CaptureExtendedObjectTemporalProjection> {\n requireInputKeys(input, \"calculateCaptureExtendedObjectTemporalProjection\", [\"objectId\", \"timing\", \"focalLengthMm\", \"focusDistanceM\", \"objectTranslationVelocityMps\", \"cameraTranslationVelocityMps\", \"temporalSampleCount\", \"points\", \"frontoparallelPlane\"]);\n requirePositiveInteger(\n \"temporalSampleCount\",\n input.temporalSampleCount\n );\n const objectId =\n requireNonEmptyString(\n input.objectId,\n \"objectId\"\n );\n const projectionDistanceMm =\n resolveProjectionDistanceMm(\n input.focalLengthMm,\n input.focusDistanceM\n );\n const objectVelocity =\n requireFiniteVector(\n input\n .objectTranslationVelocityMps,\n \"objectTranslationVelocityMps\"\n );\n const cameraVelocity =\n requireFiniteVector(\n input\n .cameraTranslationVelocityMps,\n \"cameraTranslationVelocityMps\"\n );\n const relative =\n relativeVelocity(\n objectVelocity,\n cameraVelocity\n );\n const points =\n validateUniquePoints(\n input.points\n );\n const planarDepth =\n validatePlanarDeclaration(\n input.frontoparallelPlane,\n points\n );\n\n const totalNodeCount =\n input.temporalSampleCount *\n points.length;\n if (\n !Number.isSafeInteger(\n totalNodeCount\n )\n ) {\n throw new InvalidScientificInputError(\n \"temporalSampleCount × points.length must remain a safe integer.\"\n );\n }\n\n const pointTrajectories =\n points.map(\n (\n entry,\n pointIndex\n ) => {\n const capturePoint =\n entry.point as\n CaptureExtendedObjectMetricPoint;\n const exposure =\n localExposureForPoint(\n input.timing,\n capturePoint\n .destinationPointNative\n );\n const duration =\n exposure\n .localExposureDurationSeconds;\n if (\n !Number.isFinite(\n duration\n ) ||\n duration <= 0\n ) {\n throw new InvalidScientificInputError(\n \"Extended-object capture projection requires a positive finite local exposure duration.\"\n );\n }\n\n const times =\n Array.from(\n {\n length:\n input.temporalSampleCount\n },\n (\n _,\n temporalSampleIndex\n ) =>\n exposure\n .startOffsetSecondsFromOpeningReference +\n (\n temporalSampleIndex +\n 0.5\n ) /\n input.temporalSampleCount *\n duration\n );\n const trajectory =\n createPointTrajectory(\n entry.pointId,\n entry.referencePosition,\n relative,\n times,\n projectionDistanceMm,\n planarDepth,\n \"points[\" +\n pointIndex +\n \"]\"\n );\n const normalizedTimeWeight =\n 1 /\n input.temporalSampleCount;\n const timeMeasureSeconds =\n duration /\n input.temporalSampleCount;\n\n return {\n pointId:\n entry.pointId,\n destinationPointNative: {\n ...capturePoint\n .destinationPointNative\n },\n referencePositionCameraM:\n trajectory\n .referencePositionCameraM,\n referenceImagePointMm:\n trajectory\n .referenceImagePointMm,\n localExposureWindow: {\n startSecondsFromCaptureReference:\n exposure\n .startOffsetSecondsFromOpeningReference,\n endSecondsFromCaptureReference:\n exposure\n .endOffsetSecondsFromOpeningReference,\n durationSeconds:\n duration\n },\n nodes:\n trajectory.nodes.map(\n (\n node,\n temporalSampleIndex\n ) => ({\n ...node,\n localExposurePhase:\n (\n temporalSampleIndex +\n 0.5\n ) /\n input\n .temporalSampleCount,\n normalizedTimeWeight,\n timeMeasureSeconds\n })\n )\n };\n }\n );\n\n return approximationResult(\n {\n objectId,\n timeReference:\n \"first-opening-boundary-phase\",\n timingProfileIdentity: {\n profileId:\n input.timing\n .timingProfileId,\n profileVersion:\n input.timing\n .timingProfileVersion,\n captureModeId:\n input.timing\n .captureModeId\n },\n projectionDistanceMm,\n temporalSampleCount:\n input.temporalSampleCount,\n objectTranslationVelocityMps:\n objectVelocity,\n cameraTranslationVelocityMps:\n cameraVelocity,\n relativeTranslationVelocityMps:\n relative,\n rigidTranslationOnly: true,\n pointTrajectories,\n planarMagnificationDiagnosticAvailable:\n planarDepth !== null,\n metricSceneDepthRequired:\n true,\n oneGlobalHomographyAuthorized:\n false,\n oneGlobalScaleAuthorizedForArbitrary3dGeometry:\n false,\n cameraRotationComposed:\n false,\n visibilityOcclusionModeled:\n false,\n deformationModeled: false,\n accelerationModeled: false,\n blurKernelCalculated:\n false,\n radianceIntegrated: false,\n sensorReadoutTimingUsedAsExposureTiming:\n false,\n timeVaryingDefocusMayBeRequired:\n relative.z !== 0\n },\n \"capture-local-extended-object-time-varying-projection\",\n \"1.0.0\",\n [\n \"Every point is evaluated at deterministic midpoint nodes in its own authoritative local exposure interval of the exposure-window model.\",\n \"Sensor data-readout timing is not substituted for exposure time.\",\n \"Rolling/local exposure may cause different native destinations to integrate different portions of the same rigid object trajectory.\",\n \"The result remains geometry only and does not authorize endpoint interpolation as a complete visibility/radiance blur solution.\",\n \"Camera rotation and subject deformation remain separate; object and camera translation compose only through explicit relative translation.\",\n \"When relative depth changes, downstream defocus/PSF evaluation may need to vary at the same physical-time nodes.\"\n ]\n );\n}\n"]}
1
+ {"version":3,"file":"extended-object-projection.js","sourceRoot":"","sources":["../../src/motion/extended-object-projection.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Projection of an extended object: several explicit points (in metres) on one rigid object that\n * moves in a straight line.\n *\n * The points are evaluated at deterministic physical times, or at deterministic midpoint time\n * samples inside each point's local exposure window (from the capture exposure schedule).\n *\n * This is geometry only. It does not build a blur kernel, decide visibility, integrate radiance,\n * deform the object, rotate the camera or apply a universal scale.\n * @see docs/MOTION_AND_SIGNAL.md for the equations, units, coordinate conventions, known blockers\n * and support limits.\n */\n\nimport {\n approximationResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport {\n InvalidScientificInputError,\n requirePositiveFinite,\n requirePositiveInteger\n} from \"../core/validation.js\";\nimport type {\n RasterPoint\n} from \"../output/capture-geometry.js\";\nimport {\n calculateThinLensImageDistance\n} from \"../optics/thin-lens.js\";\nimport type {\n Vector3\n} from \"../schema/scene.js\";\nimport type {\n ResolvedCaptureModeTiming\n} from \"../sensor/capture-mode-timing.js\";\nimport { requireInputKeys, requireInputObject, requireTrimmedConfigString as requireNonEmptyString } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { sameSite as samePoint } from \"../core/shared-helpers.js\";\nimport { requireFiniteVector } from \"./capture-translation-parallax-temporal-quadrature.js\";\nimport { requireScientificFinite as requireFiniteTime } from \"../color/white-balance.js\";\nimport { settingEquals as approximatelyEqual } from \"../composition/image-formation-plan.js\";\n\nexport interface ExtendedObjectMetricPoint {\n pointId: string;\n /**\n * Point in camera space, in metres, at the shared reference phase.\n *\n * +X right, +Y up, +Z forward/away from the camera.\n */\n positionCameraM: Vector3;\n}\n\nexport interface ExtendedObjectFrontoparallelPlaneDeclaration {\n kind: \"fronto-parallel-planar-patch\";\n referenceDepthM: number;\n}\n\nexport interface CalculateExtendedObjectProjectionTrajectoryInput {\n objectId: string;\n focalLengthMm: number;\n focusDistanceM?: number;\n objectTranslationVelocityMps:\n Vector3;\n cameraTranslationVelocityMps:\n Vector3;\n sampleTimesSecondsFromReference:\n readonly number[];\n points:\n readonly ExtendedObjectMetricPoint[];\n frontoparallelPlane?:\n ExtendedObjectFrontoparallelPlaneDeclaration;\n}\n\nexport interface ExtendedObjectProjectionNode {\n sampleIndex: number;\n timeSecondsFromReference: number;\n relativePositionCameraM:\n Vector3;\n mappedImagePointMm: {\n x: number;\n y: number;\n };\n deltaFromReferenceImagePlaneMm: {\n x: number;\n y: number;\n distance: number;\n };\n depthM: number;\n planarMagnificationScaleFromReference:\n number | null;\n}\n\nexport interface ExtendedObjectPointProjectionTrajectory {\n pointId: string;\n referencePositionCameraM:\n Vector3;\n referenceImagePointMm: {\n x: number;\n y: number;\n };\n nodes:\n readonly ExtendedObjectProjectionNode[];\n}\n\nexport interface ExtendedObjectPlanarMagnificationDiagnostic {\n geometry:\n \"declared-fronto-parallel-planar-patch\";\n referenceDepthM: number;\n uniformScaleAuthorizedWithinDeclaredPatch:\n true;\n scaleByTime:\n readonly {\n sampleIndex: number;\n timeSecondsFromReference:\n number;\n scaleFromReference: number;\n }[];\n lateralRigidTranslationDoesNotChangePatchScale:\n true;\n notAuthorizedForArbitrary3dGeometry:\n true;\n}\n\nexport interface ExtendedObjectProjectionTrajectory {\n objectId: string;\n timeReference:\n \"caller-declared-common-reference-phase\";\n projectionDistanceMm: number;\n objectTranslationVelocityMps:\n Vector3;\n cameraTranslationVelocityMps:\n Vector3;\n relativeTranslationVelocityMps:\n Vector3;\n rigidTranslationOnly: true;\n pointTrajectories:\n readonly ExtendedObjectPointProjectionTrajectory[];\n planarMagnificationDiagnostic:\n ExtendedObjectPlanarMagnificationDiagnostic | null;\n metricSceneDepthRequired: true;\n oneGlobalHomographyAuthorized:\n false;\n oneGlobalScaleAuthorizedForArbitrary3dGeometry:\n false;\n cameraRotationComposed: false;\n visibilityOcclusionModeled: false;\n deformationModeled: false;\n accelerationModeled: false;\n blurKernelCalculated: false;\n radianceIntegrated: false;\n timeVaryingDefocusMayBeRequired:\n boolean;\n}\n\nexport interface CaptureExtendedObjectMetricPoint\n extends ExtendedObjectMetricPoint {\n /**\n * Native destination point. It selects the local exposure window (from the\n * capture exposure schedule) for this object point.\n */\n destinationPointNative:\n RasterPoint;\n}\n\nexport interface CalculateCaptureExtendedObjectTemporalProjectionInput {\n objectId: string;\n timing:\n ResolvedCaptureModeTiming;\n focalLengthMm: number;\n focusDistanceM?: number;\n objectTranslationVelocityMps:\n Vector3;\n cameraTranslationVelocityMps:\n Vector3;\n temporalSampleCount: number;\n points:\n readonly CaptureExtendedObjectMetricPoint[];\n frontoparallelPlane?:\n ExtendedObjectFrontoparallelPlaneDeclaration;\n}\n\nexport interface CaptureExtendedObjectTemporalNode\n extends ExtendedObjectProjectionNode {\n localExposurePhase: number;\n normalizedTimeWeight: number;\n timeMeasureSeconds: number;\n}\n\nexport interface CaptureExtendedObjectPointTrajectory {\n pointId: string;\n destinationPointNative:\n RasterPoint;\n referencePositionCameraM:\n Vector3;\n referenceImagePointMm: {\n x: number;\n y: number;\n };\n localExposureWindow: {\n startSecondsFromCaptureReference:\n number;\n endSecondsFromCaptureReference:\n number;\n durationSeconds: number;\n };\n nodes:\n readonly CaptureExtendedObjectTemporalNode[];\n}\n\nexport interface CaptureExtendedObjectTemporalProjection {\n objectId: string;\n timeReference:\n \"first-opening-boundary-phase\";\n timingProfileIdentity: {\n profileId: string;\n profileVersion: string;\n captureModeId: string;\n };\n projectionDistanceMm: number;\n temporalSampleCount: number;\n objectTranslationVelocityMps:\n Vector3;\n cameraTranslationVelocityMps:\n Vector3;\n relativeTranslationVelocityMps:\n Vector3;\n rigidTranslationOnly: true;\n pointTrajectories:\n readonly CaptureExtendedObjectPointTrajectory[];\n planarMagnificationDiagnosticAvailable:\n boolean;\n metricSceneDepthRequired: true;\n oneGlobalHomographyAuthorized:\n false;\n oneGlobalScaleAuthorizedForArbitrary3dGeometry:\n false;\n cameraRotationComposed: false;\n visibilityOcclusionModeled: false;\n deformationModeled: false;\n accelerationModeled: false;\n blurKernelCalculated: false;\n radianceIntegrated: false;\n sensorReadoutTimingUsedAsExposureTiming:\n false;\n timeVaryingDefocusMayBeRequired:\n boolean;\n}\n\nfunction resolveProjectionDistanceMm(\n focalLengthMm: number,\n focusDistanceM:\n number | undefined\n): number {\n requirePositiveFinite(\n \"focalLengthMm\",\n focalLengthMm\n );\n return focusDistanceM ===\n undefined\n ? focalLengthMm\n : calculateThinLensImageDistance({\n focalLengthMm,\n objectDistanceM:\n focusDistanceM\n }).value.imageDistanceMm;\n}\n\nfunction projectMetricPoint(\n positionCameraM:\n Vector3,\n projectionDistanceMm: number,\n path: string\n): {\n x: number;\n y: number;\n} {\n if (\n !Number.isFinite(\n positionCameraM.z\n ) ||\n positionCameraM.z <= 0\n ) {\n throw new InvalidScientificInputError(\n path +\n \" must remain in front of the camera plane.\"\n );\n }\n\n const mapped = {\n x:\n projectionDistanceMm *\n (\n positionCameraM.x /\n positionCameraM.z\n ),\n y:\n projectionDistanceMm *\n (\n positionCameraM.y /\n positionCameraM.z\n )\n };\n if (\n !Number.isFinite(mapped.x) ||\n !Number.isFinite(mapped.y)\n ) {\n throw new InvalidScientificInputError(\n path +\n \" projection must remain finite.\"\n );\n }\n return mapped;\n}\n\nfunction validateUniquePoints<T extends ExtendedObjectMetricPoint>(\n points: readonly T[]\n): readonly {\n point:\n T;\n pointId: string;\n referencePosition:\n Vector3;\n}[] {\n if (\n !Array.isArray(points) ||\n points.length === 0\n ) {\n throw new InvalidConfigurationError(\n \"points must be a non-empty array.\"\n );\n }\n\n const parsed =\n points.map(\n (point, index) => {\n const path =\n \"points[\" +\n index +\n \"]\";\n requireInputObject(point, path);\n const pointId =\n requireNonEmptyString(\n point.pointId,\n path + \".pointId\"\n );\n const referencePosition =\n requireFiniteVector(\n point.positionCameraM,\n path +\n \".positionCameraM\"\n );\n if (\n referencePosition.z <= 0\n ) {\n throw new InvalidScientificInputError(\n path +\n \".positionCameraM.z must place the point in front of the camera.\"\n );\n }\n return {\n point,\n pointId,\n referencePosition\n };\n }\n );\n\n const ids =\n parsed.map(\n (entry) =>\n entry.pointId\n );\n if (\n new Set(ids).size !==\n ids.length\n ) {\n throw new InvalidScientificInputError(\n \"points must not contain duplicate pointId values.\"\n );\n }\n return parsed;\n}\n\nfunction validateSampleTimes(\n times:\n readonly number[]\n): readonly number[] {\n if (\n !Array.isArray(times) ||\n times.length === 0\n ) {\n throw new InvalidConfigurationError(\n \"sampleTimesSecondsFromReference must be a non-empty array.\"\n );\n }\n let prior =\n Number.NEGATIVE_INFINITY;\n return times.map(\n (value, index) => {\n const time =\n requireFiniteTime(\n value,\n \"sampleTimesSecondsFromReference[\" +\n index +\n \"]\"\n );\n if (time <= prior) {\n throw new InvalidScientificInputError(\n \"sampleTimesSecondsFromReference must be strictly increasing.\"\n );\n }\n prior = time;\n return time;\n }\n );\n}\n\nfunction relativeVelocity(\n objectVelocity:\n Vector3,\n cameraVelocity:\n Vector3\n): Vector3 {\n return {\n x:\n objectVelocity.x -\n cameraVelocity.x,\n y:\n objectVelocity.y -\n cameraVelocity.y,\n z:\n objectVelocity.z -\n cameraVelocity.z\n };\n}\n\nfunction positionAtTime(\n reference:\n Vector3,\n velocity:\n Vector3,\n timeSeconds: number\n): Vector3 {\n return {\n x:\n reference.x +\n velocity.x *\n timeSeconds,\n y:\n reference.y +\n velocity.y *\n timeSeconds,\n z:\n reference.z +\n velocity.z *\n timeSeconds\n };\n}\n\nfunction validatePlanarDeclaration(\n declaration:\n ExtendedObjectFrontoparallelPlaneDeclaration |\n undefined,\n points:\n readonly {\n referencePosition:\n Vector3;\n }[]\n): number | null {\n if (\n declaration === undefined\n ) {\n return null;\n }\n requireInputObject(declaration, \"frontoparallelPlane\");\n if (\n declaration.kind !==\n \"fronto-parallel-planar-patch\"\n ) {\n throw new InvalidConfigurationError(\n \"frontoparallelPlane.kind is invalid.\"\n );\n }\n requirePositiveFinite(\n \"frontoparallelPlane.referenceDepthM\",\n declaration.referenceDepthM\n );\n for (\n const entry of points\n ) {\n if (\n !approximatelyEqual(\n entry.referencePosition.z,\n declaration.referenceDepthM\n )\n ) {\n throw new InvalidScientificInputError(\n \"Every declared fronto-parallel point must lie at frontoparallelPlane.referenceDepthM.\"\n );\n }\n }\n return declaration\n .referenceDepthM;\n}\n\nfunction createPointTrajectory(\n pointId: string,\n referencePosition:\n Vector3,\n relativeTranslationVelocityMps:\n Vector3,\n sampleTimes:\n readonly number[],\n projectionDistanceMm:\n number,\n planarReferenceDepthM:\n number | null,\n path: string\n): ExtendedObjectPointProjectionTrajectory {\n const referenceImagePoint =\n projectMetricPoint(\n referencePosition,\n projectionDistanceMm,\n path +\n \".positionCameraM\"\n );\n\n const nodes =\n sampleTimes.map(\n (\n timeSeconds,\n sampleIndex\n ) => {\n const position =\n positionAtTime(\n referencePosition,\n relativeTranslationVelocityMps,\n timeSeconds\n );\n const mapped =\n projectMetricPoint(\n position,\n projectionDistanceMm,\n path +\n \".positionAtTime\"\n );\n const deltaX =\n mapped.x -\n referenceImagePoint.x;\n const deltaY =\n mapped.y -\n referenceImagePoint.y;\n\n return {\n sampleIndex,\n timeSecondsFromReference:\n timeSeconds,\n relativePositionCameraM:\n position,\n mappedImagePointMm:\n mapped,\n deltaFromReferenceImagePlaneMm: {\n x: deltaX,\n y: deltaY,\n distance:\n Math.hypot(\n deltaX,\n deltaY\n )\n },\n depthM:\n position.z,\n planarMagnificationScaleFromReference:\n planarReferenceDepthM ===\n null\n ? null\n : planarReferenceDepthM /\n position.z\n };\n }\n );\n\n return {\n pointId,\n referencePositionCameraM:\n referencePosition,\n referenceImagePointMm:\n referenceImagePoint,\n nodes\n };\n}\n\n/**\n * Evaluates multiple explicit metric points on one rigidly translating object\n * at deterministic physical times.\n *\n * This is geometry only. It does not build a blur kernel, decide visibility,\n * integrate radiance, deform the object, rotate the camera or apply a\n * universal scale.\n */\nexport function calculateExtendedObjectProjectionTrajectory(\n input:\n CalculateExtendedObjectProjectionTrajectoryInput\n): CalculationResult<ExtendedObjectProjectionTrajectory> {\n requireInputKeys(input, \"calculateExtendedObjectProjectionTrajectory\", [\"objectId\", \"focalLengthMm\", \"focusDistanceM\", \"objectTranslationVelocityMps\", \"cameraTranslationVelocityMps\", \"sampleTimesSecondsFromReference\", \"points\", \"frontoparallelPlane\"]);\n const objectId =\n requireNonEmptyString(\n input.objectId,\n \"objectId\"\n );\n const projectionDistanceMm =\n resolveProjectionDistanceMm(\n input.focalLengthMm,\n input.focusDistanceM\n );\n const objectVelocity =\n requireFiniteVector(\n input\n .objectTranslationVelocityMps,\n \"objectTranslationVelocityMps\"\n );\n const cameraVelocity =\n requireFiniteVector(\n input\n .cameraTranslationVelocityMps,\n \"cameraTranslationVelocityMps\"\n );\n const relative =\n relativeVelocity(\n objectVelocity,\n cameraVelocity\n );\n const sampleTimes =\n validateSampleTimes(\n input\n .sampleTimesSecondsFromReference\n );\n const points =\n validateUniquePoints(\n input.points\n );\n const planarDepth =\n validatePlanarDeclaration(\n input.frontoparallelPlane,\n points\n );\n\n const pointTrajectories =\n points.map(\n (\n entry,\n pointIndex\n ) =>\n createPointTrajectory(\n entry.pointId,\n entry.referencePosition,\n relative,\n sampleTimes,\n projectionDistanceMm,\n planarDepth,\n \"points[\" +\n pointIndex +\n \"]\"\n )\n );\n\n const scaleByTime =\n planarDepth === null\n ? null\n : sampleTimes.map(\n (\n timeSeconds,\n sampleIndex\n ) => {\n const currentDepth =\n planarDepth +\n relative.z *\n timeSeconds;\n if (\n currentDepth <= 0\n ) {\n throw new InvalidScientificInputError(\n \"Declared fronto-parallel patch reaches or crosses the camera plane.\"\n );\n }\n return {\n sampleIndex,\n timeSecondsFromReference:\n timeSeconds,\n scaleFromReference:\n planarDepth /\n currentDepth\n };\n }\n );\n\n return approximationResult(\n {\n objectId,\n timeReference:\n \"caller-declared-common-reference-phase\",\n projectionDistanceMm,\n objectTranslationVelocityMps:\n objectVelocity,\n cameraTranslationVelocityMps:\n cameraVelocity,\n relativeTranslationVelocityMps:\n relative,\n rigidTranslationOnly: true,\n pointTrajectories,\n planarMagnificationDiagnostic:\n scaleByTime === null\n ? null\n : {\n geometry:\n \"declared-fronto-parallel-planar-patch\",\n referenceDepthM:\n planarDepth!,\n uniformScaleAuthorizedWithinDeclaredPatch:\n true,\n scaleByTime,\n lateralRigidTranslationDoesNotChangePatchScale:\n true,\n notAuthorizedForArbitrary3dGeometry:\n true\n },\n metricSceneDepthRequired:\n true,\n oneGlobalHomographyAuthorized:\n false,\n oneGlobalScaleAuthorizedForArbitrary3dGeometry:\n false,\n cameraRotationComposed:\n false,\n visibilityOcclusionModeled:\n false,\n deformationModeled: false,\n accelerationModeled: false,\n blurKernelCalculated:\n false,\n radianceIntegrated: false,\n timeVaryingDefocusMayBeRequired:\n relative.z !== 0\n },\n \"extended-object-time-varying-projection\",\n \"1.0.0\",\n [\n \"Every object point is projected independently from explicit metric camera-space depth.\",\n \"Object and camera translation remain separate inputs and compose only as object-minus-camera relative translation.\",\n \"One global homography or one global scale is not authorized for arbitrary 3D geometry.\",\n \"A uniform magnification diagnostic is emitted only for an explicitly declared and validated fronto-parallel planar patch under the shared rigid-translation model.\",\n \"Constant rigid translation is the only object motion modeled; acceleration, rotation, articulation and deformation are excluded.\",\n \"Visibility, occlusion/disocclusion, frame-boundary coverage, radiance integration and blur-kernel synthesis remain downstream.\",\n \"When relative Z translation is nonzero, downstream defocus/PSF state may also vary with time.\"\n ]\n );\n}\n\nfunction localExposureForPoint(\n timing:\n ResolvedCaptureModeTiming,\n point:\n RasterPoint\n): ResolvedCaptureModeTiming[\"exposureWindows\"][\"samples\"][number] {\n requireInputObject(timing, \"timing\");\n requireInputObject(timing.exposureWindows, \"timing.exposureWindows\");\n if (!Array.isArray(timing.exposureWindows.samples)) throw new InvalidConfigurationError(\"timing.exposureWindows.samples must be an array.\");\n timing.exposureWindows.samples.forEach((sample, index) => {\n requireInputObject(sample, \"timing.exposureWindows.samples[\" + index + \"]\");\n requireInputObject(sample.pointNative, \"timing.exposureWindows.samples[\" + index + \"].pointNative\");\n });\n requireInputObject(point, \"destinationPointNative\");\n const matches =\n timing.exposureWindows.samples.filter(\n (sample) =>\n samePoint(\n sample.pointNative,\n point\n )\n );\n if (matches.length !== 1) {\n throw new InvalidScientificInputError(\n \"Each extended-object capture point must match exactly one committed exposure-window sample point.\"\n );\n }\n return matches[0]!;\n}\n\n/**\n * Evaluates extended-object projection at deterministic midpoint time samples\n * inside each point's local exposure window (from the capture exposure\n * schedule).\n */\nexport function calculateCaptureExtendedObjectTemporalProjection(\n input:\n CalculateCaptureExtendedObjectTemporalProjectionInput\n): CalculationResult<CaptureExtendedObjectTemporalProjection> {\n requireInputKeys(input, \"calculateCaptureExtendedObjectTemporalProjection\", [\"objectId\", \"timing\", \"focalLengthMm\", \"focusDistanceM\", \"objectTranslationVelocityMps\", \"cameraTranslationVelocityMps\", \"temporalSampleCount\", \"points\", \"frontoparallelPlane\"]);\n requirePositiveInteger(\n \"temporalSampleCount\",\n input.temporalSampleCount\n );\n const objectId =\n requireNonEmptyString(\n input.objectId,\n \"objectId\"\n );\n const projectionDistanceMm =\n resolveProjectionDistanceMm(\n input.focalLengthMm,\n input.focusDistanceM\n );\n const objectVelocity =\n requireFiniteVector(\n input\n .objectTranslationVelocityMps,\n \"objectTranslationVelocityMps\"\n );\n const cameraVelocity =\n requireFiniteVector(\n input\n .cameraTranslationVelocityMps,\n \"cameraTranslationVelocityMps\"\n );\n const relative =\n relativeVelocity(\n objectVelocity,\n cameraVelocity\n );\n const points =\n validateUniquePoints(\n input.points\n );\n const planarDepth =\n validatePlanarDeclaration(\n input.frontoparallelPlane,\n points\n );\n\n const totalNodeCount =\n input.temporalSampleCount *\n points.length;\n if (\n !Number.isSafeInteger(\n totalNodeCount\n )\n ) {\n throw new InvalidScientificInputError(\n \"temporalSampleCount × points.length must remain a safe integer.\"\n );\n }\n\n const pointTrajectories =\n points.map(\n (\n entry,\n pointIndex\n ) => {\n const capturePoint =\n entry.point as\n CaptureExtendedObjectMetricPoint;\n const exposure =\n localExposureForPoint(\n input.timing,\n capturePoint\n .destinationPointNative\n );\n const duration =\n exposure\n .localExposureDurationSeconds;\n if (\n !Number.isFinite(\n duration\n ) ||\n duration <= 0\n ) {\n throw new InvalidScientificInputError(\n \"Extended-object capture projection requires a positive finite local exposure duration.\"\n );\n }\n\n const times =\n Array.from(\n {\n length:\n input.temporalSampleCount\n },\n (\n _,\n temporalSampleIndex\n ) =>\n exposure\n .startOffsetSecondsFromOpeningReference +\n (\n temporalSampleIndex +\n 0.5\n ) /\n input.temporalSampleCount *\n duration\n );\n const trajectory =\n createPointTrajectory(\n entry.pointId,\n entry.referencePosition,\n relative,\n times,\n projectionDistanceMm,\n planarDepth,\n \"points[\" +\n pointIndex +\n \"]\"\n );\n const normalizedTimeWeight =\n 1 /\n input.temporalSampleCount;\n const timeMeasureSeconds =\n duration /\n input.temporalSampleCount;\n\n return {\n pointId:\n entry.pointId,\n destinationPointNative: {\n ...capturePoint\n .destinationPointNative\n },\n referencePositionCameraM:\n trajectory\n .referencePositionCameraM,\n referenceImagePointMm:\n trajectory\n .referenceImagePointMm,\n localExposureWindow: {\n startSecondsFromCaptureReference:\n exposure\n .startOffsetSecondsFromOpeningReference,\n endSecondsFromCaptureReference:\n exposure\n .endOffsetSecondsFromOpeningReference,\n durationSeconds:\n duration\n },\n nodes:\n trajectory.nodes.map(\n (\n node,\n temporalSampleIndex\n ) => ({\n ...node,\n localExposurePhase:\n (\n temporalSampleIndex +\n 0.5\n ) /\n input\n .temporalSampleCount,\n normalizedTimeWeight,\n timeMeasureSeconds\n })\n )\n };\n }\n );\n\n return approximationResult(\n {\n objectId,\n timeReference:\n \"first-opening-boundary-phase\",\n timingProfileIdentity: {\n profileId:\n input.timing\n .timingProfileId,\n profileVersion:\n input.timing\n .timingProfileVersion,\n captureModeId:\n input.timing\n .captureModeId\n },\n projectionDistanceMm,\n temporalSampleCount:\n input.temporalSampleCount,\n objectTranslationVelocityMps:\n objectVelocity,\n cameraTranslationVelocityMps:\n cameraVelocity,\n relativeTranslationVelocityMps:\n relative,\n rigidTranslationOnly: true,\n pointTrajectories,\n planarMagnificationDiagnosticAvailable:\n planarDepth !== null,\n metricSceneDepthRequired:\n true,\n oneGlobalHomographyAuthorized:\n false,\n oneGlobalScaleAuthorizedForArbitrary3dGeometry:\n false,\n cameraRotationComposed:\n false,\n visibilityOcclusionModeled:\n false,\n deformationModeled: false,\n accelerationModeled: false,\n blurKernelCalculated:\n false,\n radianceIntegrated: false,\n sensorReadoutTimingUsedAsExposureTiming:\n false,\n timeVaryingDefocusMayBeRequired:\n relative.z !== 0\n },\n \"capture-local-extended-object-time-varying-projection\",\n \"1.0.0\",\n [\n \"Every point is evaluated at deterministic midpoint nodes in its own authoritative local exposure interval of the exposure-window model.\",\n \"Sensor data-readout timing is not substituted for exposure time.\",\n \"Rolling/local exposure may cause different native destinations to integrate different portions of the same rigid object trajectory.\",\n \"The result remains geometry only and does not authorize endpoint interpolation as a complete visibility/radiance blur solution.\",\n \"Camera rotation and subject deformation remain separate; object and camera translation compose only through explicit relative translation.\",\n \"When relative depth changes, downstream defocus/PSF evaluation may need to vary at the same physical-time nodes.\"\n ]\n );\n}\n"]}
@@ -23,7 +23,7 @@
23
23
  */
24
24
  import { approximationResult, calculatedResult } from "../core/calculation-result.js";
25
25
  import { InvalidScientificInputError, requirePositiveFinite } from "../core/validation.js";
26
- import { rejectUnknownFields, requireInputKeys } from "../core/record-validation.js";
26
+ import { rejectUnknownFields, requireInputKeys, requireNamedFinite as requireFinite } from "../core/record-validation.js";
27
27
  import { InvalidConfigurationError } from "../core/configuration-error.js";
28
28
  function requireNonNegativeFinite(name, value) {
29
29
  if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
@@ -31,12 +31,6 @@ function requireNonNegativeFinite(name, value) {
31
31
  }
32
32
  return value;
33
33
  }
34
- function requireFinite(name, value) {
35
- if (typeof value !== "number" || !Number.isFinite(value)) {
36
- throw new InvalidScientificInputError(`${name} must be finite.`);
37
- }
38
- return value;
39
- }
40
34
  /**
41
35
  * Koschmieder haze at one wavelength: attenuated scene radiance plus airlight, with Rayleigh and
42
36
  * Angstrom-law aerosol extinction.
@@ -1 +1 @@
1
- {"version":3,"file":"atmospheric-haze.js","sourceRoot":"","sources":["../../src/optics/atmospheric-haze.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Atmospheric haze as a scene-radiance modifier along a horizontal, homogeneous line of sight\n * (Koschmieder 1924, Beitr. Phys. freien Atmos. 12:33; Middleton, Vision Through the Atmosphere,\n * 1952; McCartney, Optics of the Atmosphere, 1976, ch. 1):\n * L(d, lambda) = L0(lambda) t + L_air(lambda) (1 - t), t = exp(-beta(lambda) d)\n * with extinction coefficient beta = beta_Rayleigh + beta_aerosol:\n * beta_Rayleigh(lambda) = beta_R,ref (lambda / lambda_ref)^(-rayleighExponent) (Rayleigh: 4;\n * Bucholtz 1995, Appl. Opt. 34:2765, gives about 4.0-4.1 for air in the visible),\n * beta_aerosol(lambda) = beta_A,ref (lambda / lambda_ref)^(-alpha) (Angstrom 1929, Geogr. Ann.\n * 11:156; alpha typically about 0-2.5).\n * L_air is the caller-supplied horizon (airlight) radiance at the same wavelength.\n *\n * This is an approximation. It assumes a uniform atmosphere and a single line of sight. It ignores\n * the angular structure of multiple scattering and any gradient in sky luminance. The caller\n * supplies the reference extinction values; none are built in.\n *\n * Meteorological visibility: Koschmieder's relation V = -ln(epsilon) / beta for a black target at\n * contrast threshold epsilon (0.02 gives 3.912 / beta; the WMO meteorological optical range uses\n * 0.05, about 3.0 / beta; WMO-No. 8, 2018, vol. I ch. 9).\n * @see docs/PHYSICS_FOUNDATION.md for the equations, units, coordinate conventions, known blockers\n * and support limits.\n */\n\nimport {\n approximationResult,\n calculatedResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport {\n InvalidScientificInputError,\n requirePositiveFinite\n} from \"../core/validation.js\";\nimport { rejectUnknownFields, requireInputKeys } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\n\nexport interface AtmosphericExtinctionModel {\n /** Wavelength at which the reference coefficients apply, nm. */\n referenceWavelengthNm: number;\n /** Molecular (Rayleigh) extinction coefficient at the reference wavelength, 1/m (>= 0). */\n rayleighExtinctionPerMeter: number;\n /** Aerosol extinction coefficient at the reference wavelength, 1/m (>= 0). */\n aerosolExtinctionPerMeter: number;\n /** Angstrom exponent alpha for the aerosol term (finite, typically 0-2.5). */\n angstromExponent: number;\n /** Rayleigh wavelength exponent; defaults to 4. */\n rayleighExponent?: number;\n}\n\nexport interface CalculateAtmosphericHazeInput {\n /** Line-of-sight distance through the homogeneous haze, metres (>= 0). */\n pathLengthM: number;\n /** Wavelength, nm (same basis as the extinction data). */\n wavelengthNm: number;\n /** Scene (object) radiance at zero distance, any linear radiance unit (>= 0). */\n sceneRadiance: number;\n /** Horizon/airlight radiance in the same unit (>= 0). */\n airlightRadiance: number;\n extinction: AtmosphericExtinctionModel;\n}\n\nexport interface AtmosphericHaze {\n wavelengthNm: number;\n pathLengthM: number;\n rayleighExtinctionPerMeter: number;\n aerosolExtinctionPerMeter: number;\n extinctionPerMeter: number;\n opticalDepth: number;\n /** Direct transmittance t = exp(-beta d). */\n transmittance: number;\n /** L0 t. */\n attenuatedSceneRadiance: number;\n /** L_air (1 - t). */\n airlightContribution: number;\n /** L0 t + L_air (1 - t), in the input radiance unit. */\n hazedRadiance: number;\n /** Apparent contrast against the horizon divided by the intrinsic contrast: (L - L_air)/(L0 - L_air) = t. */\n contrastTransmittance: number;\n}\n\nexport interface CalculateExtinctionFromVisibilityInput {\n /** Meteorological visibility, metres. */\n visibilityM: number;\n /** Contrast threshold epsilon in (0, 1); Koschmieder 0.02 by default, WMO MOR uses 0.05. */\n contrastThreshold?: number;\n}\n\nfunction requireNonNegativeFinite(name: string, value: unknown): number {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value < 0) {\n throw new InvalidScientificInputError(`${name} must be finite and non-negative.`);\n }\n return value;\n}\n\nfunction requireFinite(name: string, value: unknown): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n throw new InvalidScientificInputError(`${name} must be finite.`);\n }\n return value;\n}\n\n/**\n * Koschmieder haze at one wavelength: attenuated scene radiance plus airlight, with Rayleigh and\n * Angstrom-law aerosol extinction.\n */\nexport function calculateAtmosphericHaze(\n input: CalculateAtmosphericHazeInput\n): CalculationResult<AtmosphericHaze> {\n requireInputKeys(input, \"calculateAtmosphericHaze\", [\"pathLengthM\", \"wavelengthNm\", \"sceneRadiance\", \"airlightRadiance\", \"extinction\"]);\n const d = requireNonNegativeFinite(\"pathLengthM\", input.pathLengthM);\n requirePositiveFinite(\"wavelengthNm\", input.wavelengthNm);\n const l0 = requireNonNegativeFinite(\"sceneRadiance\", input.sceneRadiance);\n const lAir = requireNonNegativeFinite(\"airlightRadiance\", input.airlightRadiance);\n const model = input.extinction;\n if (typeof model !== \"object\" || model === null) {\n throw new InvalidConfigurationError(\"extinction must be an object.\");\n }\n // A misspelt optional field (for example rayleighExponet) must not silently fall back to the default.\n rejectUnknownFields(model, [\"referenceWavelengthNm\", \"rayleighExtinctionPerMeter\", \"aerosolExtinctionPerMeter\", \"angstromExponent\", \"rayleighExponent\"], \"extinction\");\n requirePositiveFinite(\"extinction.referenceWavelengthNm\", model.referenceWavelengthNm);\n const betaR = requireNonNegativeFinite(\n \"extinction.rayleighExtinctionPerMeter\",\n model.rayleighExtinctionPerMeter\n );\n const betaA = requireNonNegativeFinite(\n \"extinction.aerosolExtinctionPerMeter\",\n model.aerosolExtinctionPerMeter\n );\n const alpha = requireFinite(\"extinction.angstromExponent\", model.angstromExponent);\n const rayleighExponent =\n model.rayleighExponent === undefined\n ? 4\n : requireFinite(\"extinction.rayleighExponent\", model.rayleighExponent);\n if (alpha < -1 || alpha > 4 || rayleighExponent < 3.5 || rayleighExponent > 4.5) {\n throw new InvalidScientificInputError(\n \"extinction.angstromExponent must lie in [-1, 4] and rayleighExponent in [3.5, 4.5].\"\n );\n }\n\n const ratio = input.wavelengthNm / model.referenceWavelengthNm;\n const rayleigh = betaR * ratio ** -rayleighExponent;\n const aerosol = betaA * ratio ** -alpha;\n const beta = rayleigh + aerosol;\n const opticalDepth = beta * d;\n const transmittance = Math.exp(-opticalDepth);\n const attenuated = l0 * transmittance;\n // -expm1(-tau) = 1 - t without cancellation for thin haze.\n const airlight = lAir * -Math.expm1(-opticalDepth);\n return approximationResult(\n {\n wavelengthNm: input.wavelengthNm,\n pathLengthM: d,\n rayleighExtinctionPerMeter: rayleigh,\n aerosolExtinctionPerMeter: aerosol,\n extinctionPerMeter: beta,\n opticalDepth,\n transmittance,\n attenuatedSceneRadiance: attenuated,\n airlightContribution: airlight,\n hazedRadiance: attenuated + airlight,\n contrastTransmittance: transmittance\n },\n \"koschmieder-homogeneous-haze\",\n \"1.0.0\",\n [\n \"Koschmieder model: L = L0 exp(-beta d) + L_air (1 - exp(-beta d)) along one homogeneous horizontal line of sight\",\n `Extinction = Rayleigh (lambda^-${rayleighExponent}) + aerosol Angstrom law (lambda^-${alpha}) scaled from caller reference coefficients`,\n \"Airlight radiance is caller-supplied; sky gradients, multiple-scattering angular structure, absorption bands and slant-path density changes are not modeled\",\n \"Radiance unit is whatever the caller supplies; the model is linear in it\"\n ]\n );\n}\n\n/**\n * Extinction coefficient from meteorological visibility with Koschmieder's relation\n * beta = -ln(epsilon) / V.\n */\nexport function calculateExtinctionFromVisibility(\n input: CalculateExtinctionFromVisibilityInput\n): CalculationResult<{ extinctionPerMeter: number; contrastThreshold: number }> {\n requireInputKeys(input, \"calculateExtinctionFromVisibility\", [\"visibilityM\", \"contrastThreshold\"]);\n requirePositiveFinite(\"visibilityM\", input.visibilityM);\n const epsilon = input.contrastThreshold ?? 0.02;\n if (typeof epsilon !== \"number\" || !(epsilon > 0) || !(epsilon < 1)) {\n throw new InvalidScientificInputError(\"contrastThreshold must lie in (0, 1).\");\n }\n return calculatedResult(\n {\n extinctionPerMeter: -Math.log(epsilon) / input.visibilityM,\n contrastThreshold: epsilon\n },\n \"koschmieder-visibility-extinction\",\n \"1.0.0\",\n [\n \"Koschmieder relation for a black target against the horizon in a homogeneous atmosphere\",\n \"Threshold 0.02 is Koschmieder's; WMO meteorological optical range uses 0.05\"\n ]\n );\n}\n"]}
1
+ {"version":3,"file":"atmospheric-haze.js","sourceRoot":"","sources":["../../src/optics/atmospheric-haze.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAEjB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,EACtB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,kBAAkB,IAAI,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC1H,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAqD3E,SAAS,wBAAwB,CAAC,IAAY,EAAE,KAAc;IAC5D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACtE,MAAM,IAAI,2BAA2B,CAAC,GAAG,IAAI,mCAAmC,CAAC,CAAC;IACpF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CACtC,KAAoC;IAEpC,gBAAgB,CAAC,KAAK,EAAE,0BAA0B,EAAE,CAAC,aAAa,EAAE,cAAc,EAAE,eAAe,EAAE,kBAAkB,EAAE,YAAY,CAAC,CAAC,CAAC;IACxI,MAAM,CAAC,GAAG,wBAAwB,CAAC,aAAa,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;IACrE,qBAAqB,CAAC,cAAc,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IAC1D,MAAM,EAAE,GAAG,wBAAwB,CAAC,eAAe,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;IAC1E,MAAM,IAAI,GAAG,wBAAwB,CAAC,kBAAkB,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAClF,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,CAAC;IAC/B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,MAAM,IAAI,yBAAyB,CAAC,+BAA+B,CAAC,CAAC;IACvE,CAAC;IACD,sGAAsG;IACtG,mBAAmB,CAAC,KAAK,EAAE,CAAC,uBAAuB,EAAE,4BAA4B,EAAE,2BAA2B,EAAE,kBAAkB,EAAE,kBAAkB,CAAC,EAAE,YAAY,CAAC,CAAC;IACvK,qBAAqB,CAAC,kCAAkC,EAAE,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACvF,MAAM,KAAK,GAAG,wBAAwB,CACpC,uCAAuC,EACvC,KAAK,CAAC,0BAA0B,CACjC,CAAC;IACF,MAAM,KAAK,GAAG,wBAAwB,CACpC,sCAAsC,EACtC,KAAK,CAAC,yBAAyB,CAChC,CAAC;IACF,MAAM,KAAK,GAAG,aAAa,CAAC,6BAA6B,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAC;IACnF,MAAM,gBAAgB,GACpB,KAAK,CAAC,gBAAgB,KAAK,SAAS;QAClC,CAAC,CAAC,CAAC;QACH,CAAC,CAAC,aAAa,CAAC,6BAA6B,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAC3E,IAAI,KAAK,GAAG,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,gBAAgB,GAAG,GAAG,IAAI,gBAAgB,GAAG,GAAG,EAAE,CAAC;QAChF,MAAM,IAAI,2BAA2B,CACnC,qFAAqF,CACtF,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC,qBAAqB,CAAC;IAC/D,MAAM,QAAQ,GAAG,KAAK,GAAG,KAAK,IAAI,CAAC,gBAAgB,CAAC;IACpD,MAAM,OAAO,GAAG,KAAK,GAAG,KAAK,IAAI,CAAC,KAAK,CAAC;IACxC,MAAM,IAAI,GAAG,QAAQ,GAAG,OAAO,CAAC;IAChC,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC;IAC9B,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC;IAC9C,MAAM,UAAU,GAAG,EAAE,GAAG,aAAa,CAAC;IACtC,2DAA2D;IAC3D,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC;IACnD,OAAO,mBAAmB,CACxB;QACE,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,WAAW,EAAE,CAAC;QACd,0BAA0B,EAAE,QAAQ;QACpC,yBAAyB,EAAE,OAAO;QAClC,kBAAkB,EAAE,IAAI;QACxB,YAAY;QACZ,aAAa;QACb,uBAAuB,EAAE,UAAU;QACnC,oBAAoB,EAAE,QAAQ;QAC9B,aAAa,EAAE,UAAU,GAAG,QAAQ;QACpC,qBAAqB,EAAE,aAAa;KACrC,EACD,8BAA8B,EAC9B,OAAO,EACP;QACE,kHAAkH;QAClH,kCAAkC,gBAAgB,qCAAqC,KAAK,6CAA6C;QACzI,6JAA6J;QAC7J,0EAA0E;KAC3E,CACF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iCAAiC,CAC/C,KAA6C;IAE7C,gBAAgB,CAAC,KAAK,EAAE,mCAAmC,EAAE,CAAC,aAAa,EAAE,mBAAmB,CAAC,CAAC,CAAC;IACnG,qBAAqB,CAAC,aAAa,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,KAAK,CAAC,iBAAiB,IAAI,IAAI,CAAC;IAChD,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,2BAA2B,CAAC,uCAAuC,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,gBAAgB,CACrB;QACE,kBAAkB,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC,WAAW;QAC1D,iBAAiB,EAAE,OAAO;KAC3B,EACD,mCAAmC,EACnC,OAAO,EACP;QACE,yFAAyF;QACzF,6EAA6E;KAC9E,CACF,CAAC;AACJ,CAAC","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Atmospheric haze as a scene-radiance modifier along a horizontal, homogeneous line of sight\n * (Koschmieder 1924, Beitr. Phys. freien Atmos. 12:33; Middleton, Vision Through the Atmosphere,\n * 1952; McCartney, Optics of the Atmosphere, 1976, ch. 1):\n * L(d, lambda) = L0(lambda) t + L_air(lambda) (1 - t), t = exp(-beta(lambda) d)\n * with extinction coefficient beta = beta_Rayleigh + beta_aerosol:\n * beta_Rayleigh(lambda) = beta_R,ref (lambda / lambda_ref)^(-rayleighExponent) (Rayleigh: 4;\n * Bucholtz 1995, Appl. Opt. 34:2765, gives about 4.0-4.1 for air in the visible),\n * beta_aerosol(lambda) = beta_A,ref (lambda / lambda_ref)^(-alpha) (Angstrom 1929, Geogr. Ann.\n * 11:156; alpha typically about 0-2.5).\n * L_air is the caller-supplied horizon (airlight) radiance at the same wavelength.\n *\n * This is an approximation. It assumes a uniform atmosphere and a single line of sight. It ignores\n * the angular structure of multiple scattering and any gradient in sky luminance. The caller\n * supplies the reference extinction values; none are built in.\n *\n * Meteorological visibility: Koschmieder's relation V = -ln(epsilon) / beta for a black target at\n * contrast threshold epsilon (0.02 gives 3.912 / beta; the WMO meteorological optical range uses\n * 0.05, about 3.0 / beta; WMO-No. 8, 2018, vol. I ch. 9).\n * @see docs/PHYSICS_FOUNDATION.md for the equations, units, coordinate conventions, known blockers\n * and support limits.\n */\n\nimport {\n approximationResult,\n calculatedResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport {\n InvalidScientificInputError,\n requirePositiveFinite\n} from \"../core/validation.js\";\nimport { rejectUnknownFields, requireInputKeys, requireNamedFinite as requireFinite } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\n\nexport interface AtmosphericExtinctionModel {\n /** Wavelength at which the reference coefficients apply, nm. */\n referenceWavelengthNm: number;\n /** Molecular (Rayleigh) extinction coefficient at the reference wavelength, 1/m (>= 0). */\n rayleighExtinctionPerMeter: number;\n /** Aerosol extinction coefficient at the reference wavelength, 1/m (>= 0). */\n aerosolExtinctionPerMeter: number;\n /** Angstrom exponent alpha for the aerosol term (finite, typically 0-2.5). */\n angstromExponent: number;\n /** Rayleigh wavelength exponent; defaults to 4. */\n rayleighExponent?: number;\n}\n\nexport interface CalculateAtmosphericHazeInput {\n /** Line-of-sight distance through the homogeneous haze, metres (>= 0). */\n pathLengthM: number;\n /** Wavelength, nm (same basis as the extinction data). */\n wavelengthNm: number;\n /** Scene (object) radiance at zero distance, any linear radiance unit (>= 0). */\n sceneRadiance: number;\n /** Horizon/airlight radiance in the same unit (>= 0). */\n airlightRadiance: number;\n extinction: AtmosphericExtinctionModel;\n}\n\nexport interface AtmosphericHaze {\n wavelengthNm: number;\n pathLengthM: number;\n rayleighExtinctionPerMeter: number;\n aerosolExtinctionPerMeter: number;\n extinctionPerMeter: number;\n opticalDepth: number;\n /** Direct transmittance t = exp(-beta d). */\n transmittance: number;\n /** L0 t. */\n attenuatedSceneRadiance: number;\n /** L_air (1 - t). */\n airlightContribution: number;\n /** L0 t + L_air (1 - t), in the input radiance unit. */\n hazedRadiance: number;\n /** Apparent contrast against the horizon divided by the intrinsic contrast: (L - L_air)/(L0 - L_air) = t. */\n contrastTransmittance: number;\n}\n\nexport interface CalculateExtinctionFromVisibilityInput {\n /** Meteorological visibility, metres. */\n visibilityM: number;\n /** Contrast threshold epsilon in (0, 1); Koschmieder 0.02 by default, WMO MOR uses 0.05. */\n contrastThreshold?: number;\n}\n\nfunction requireNonNegativeFinite(name: string, value: unknown): number {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value < 0) {\n throw new InvalidScientificInputError(`${name} must be finite and non-negative.`);\n }\n return value;\n}\n\n/**\n * Koschmieder haze at one wavelength: attenuated scene radiance plus airlight, with Rayleigh and\n * Angstrom-law aerosol extinction.\n */\nexport function calculateAtmosphericHaze(\n input: CalculateAtmosphericHazeInput\n): CalculationResult<AtmosphericHaze> {\n requireInputKeys(input, \"calculateAtmosphericHaze\", [\"pathLengthM\", \"wavelengthNm\", \"sceneRadiance\", \"airlightRadiance\", \"extinction\"]);\n const d = requireNonNegativeFinite(\"pathLengthM\", input.pathLengthM);\n requirePositiveFinite(\"wavelengthNm\", input.wavelengthNm);\n const l0 = requireNonNegativeFinite(\"sceneRadiance\", input.sceneRadiance);\n const lAir = requireNonNegativeFinite(\"airlightRadiance\", input.airlightRadiance);\n const model = input.extinction;\n if (typeof model !== \"object\" || model === null) {\n throw new InvalidConfigurationError(\"extinction must be an object.\");\n }\n // A misspelt optional field (for example rayleighExponet) must not silently fall back to the default.\n rejectUnknownFields(model, [\"referenceWavelengthNm\", \"rayleighExtinctionPerMeter\", \"aerosolExtinctionPerMeter\", \"angstromExponent\", \"rayleighExponent\"], \"extinction\");\n requirePositiveFinite(\"extinction.referenceWavelengthNm\", model.referenceWavelengthNm);\n const betaR = requireNonNegativeFinite(\n \"extinction.rayleighExtinctionPerMeter\",\n model.rayleighExtinctionPerMeter\n );\n const betaA = requireNonNegativeFinite(\n \"extinction.aerosolExtinctionPerMeter\",\n model.aerosolExtinctionPerMeter\n );\n const alpha = requireFinite(\"extinction.angstromExponent\", model.angstromExponent);\n const rayleighExponent =\n model.rayleighExponent === undefined\n ? 4\n : requireFinite(\"extinction.rayleighExponent\", model.rayleighExponent);\n if (alpha < -1 || alpha > 4 || rayleighExponent < 3.5 || rayleighExponent > 4.5) {\n throw new InvalidScientificInputError(\n \"extinction.angstromExponent must lie in [-1, 4] and rayleighExponent in [3.5, 4.5].\"\n );\n }\n\n const ratio = input.wavelengthNm / model.referenceWavelengthNm;\n const rayleigh = betaR * ratio ** -rayleighExponent;\n const aerosol = betaA * ratio ** -alpha;\n const beta = rayleigh + aerosol;\n const opticalDepth = beta * d;\n const transmittance = Math.exp(-opticalDepth);\n const attenuated = l0 * transmittance;\n // -expm1(-tau) = 1 - t without cancellation for thin haze.\n const airlight = lAir * -Math.expm1(-opticalDepth);\n return approximationResult(\n {\n wavelengthNm: input.wavelengthNm,\n pathLengthM: d,\n rayleighExtinctionPerMeter: rayleigh,\n aerosolExtinctionPerMeter: aerosol,\n extinctionPerMeter: beta,\n opticalDepth,\n transmittance,\n attenuatedSceneRadiance: attenuated,\n airlightContribution: airlight,\n hazedRadiance: attenuated + airlight,\n contrastTransmittance: transmittance\n },\n \"koschmieder-homogeneous-haze\",\n \"1.0.0\",\n [\n \"Koschmieder model: L = L0 exp(-beta d) + L_air (1 - exp(-beta d)) along one homogeneous horizontal line of sight\",\n `Extinction = Rayleigh (lambda^-${rayleighExponent}) + aerosol Angstrom law (lambda^-${alpha}) scaled from caller reference coefficients`,\n \"Airlight radiance is caller-supplied; sky gradients, multiple-scattering angular structure, absorption bands and slant-path density changes are not modeled\",\n \"Radiance unit is whatever the caller supplies; the model is linear in it\"\n ]\n );\n}\n\n/**\n * Extinction coefficient from meteorological visibility with Koschmieder's relation\n * beta = -ln(epsilon) / V.\n */\nexport function calculateExtinctionFromVisibility(\n input: CalculateExtinctionFromVisibilityInput\n): CalculationResult<{ extinctionPerMeter: number; contrastThreshold: number }> {\n requireInputKeys(input, \"calculateExtinctionFromVisibility\", [\"visibilityM\", \"contrastThreshold\"]);\n requirePositiveFinite(\"visibilityM\", input.visibilityM);\n const epsilon = input.contrastThreshold ?? 0.02;\n if (typeof epsilon !== \"number\" || !(epsilon > 0) || !(epsilon < 1)) {\n throw new InvalidScientificInputError(\"contrastThreshold must lie in (0, 1).\");\n }\n return calculatedResult(\n {\n extinctionPerMeter: -Math.log(epsilon) / input.visibilityM,\n contrastThreshold: epsilon\n },\n \"koschmieder-visibility-extinction\",\n \"1.0.0\",\n [\n \"Koschmieder relation for a black target against the horizon in a homogeneous atmosphere\",\n \"Threshold 0.02 is Koschmieder's; WMO meteorological optical range uses 0.05\"\n ]\n );\n}\n"]}
@@ -31,17 +31,11 @@
31
31
  import { approximationResult } from "../core/calculation-result.js";
32
32
  import { InvalidScientificInputError } from "../core/validation.js";
33
33
  import { calculateInverseRadialSourcePointValue, validateRadialDistortionProfile } from "./radial-distortion.js";
34
- import { requireInputKeys } from "../core/record-validation.js";
34
+ import { requireInputKeys, requireNamedFinite as requireFinite } from "../core/record-validation.js";
35
35
  import { InvalidConfigurationError } from "../core/configuration-error.js";
36
36
  const VALIDATION_RINGS = 64;
37
37
  const VALIDATION_AZIMUTHS = 256;
38
38
  const MAXIMUM_NEWTON_ITERATIONS = 100;
39
- function requireFinite(name, value) {
40
- if (typeof value !== "number" || !Number.isFinite(value)) {
41
- throw new InvalidScientificInputError(`${name} must be finite.`);
42
- }
43
- return value;
44
- }
45
39
  function requirePoint(name, point) {
46
40
  if (typeof point !== "object" || point === null) {
47
41
  throw new InvalidConfigurationError(`${name} must be an object with finite numeric x and y coordinates.`);