@photivra/engine 2.13.0 → 2.14.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 (125) hide show
  1. package/CHANGELOG.md +68 -1
  2. package/CITATION.cff +1 -1
  3. package/README.md +7 -7
  4. package/SECURITY.md +1 -1
  5. package/THIRD_PARTY.md +2 -0
  6. package/dist/camera/lens-projection.js +2 -1
  7. package/dist/camera/lens-projection.js.map +1 -1
  8. package/dist/capture/dng-import.js +1 -1
  9. package/dist/capture/dng-import.js.map +1 -1
  10. package/dist/capture/host-yield-cadence.js +1 -1
  11. package/dist/capture/host-yield-cadence.js.map +1 -1
  12. package/dist/capture/native-environment-optical-stage.js +1 -1
  13. package/dist/capture/native-environment-optical-stage.js.map +1 -1
  14. package/dist/capture/native-raw.js +1 -1
  15. package/dist/capture/native-raw.js.map +1 -1
  16. package/dist/capture/packed-sensor-raw.js +3 -3
  17. package/dist/capture/packed-sensor-raw.js.map +1 -1
  18. package/dist/capture/sensor-raw-producer.js +1 -1
  19. package/dist/capture/sensor-raw-producer.js.map +1 -1
  20. package/dist/color/capture-color.js +3 -3
  21. package/dist/color/capture-color.js.map +1 -1
  22. package/dist/color/chromatic-adaptation.js +2 -2
  23. package/dist/color/chromatic-adaptation.js.map +1 -1
  24. package/dist/color/color-matching-functions.js +1 -1
  25. package/dist/color/color-matching-functions.js.map +1 -1
  26. package/dist/color/colorimetry-math.js +2 -2
  27. package/dist/color/colorimetry-math.js.map +1 -1
  28. package/dist/color/illuminants.js +3 -3
  29. package/dist/color/illuminants.js.map +1 -1
  30. package/dist/color/rgb-color-spaces.js +1 -1
  31. package/dist/color/rgb-color-spaces.js.map +1 -1
  32. package/dist/color/sensor-color-development.js +1 -1
  33. package/dist/color/sensor-color-development.js.map +1 -1
  34. package/dist/core/record-validation.js +3 -3
  35. package/dist/core/record-validation.js.map +1 -1
  36. package/dist/core/version.d.ts +1 -1
  37. package/dist/core/version.js +1 -1
  38. package/dist/core/version.js.map +1 -1
  39. package/dist/equipment/generic-prime-lenses.js +3 -3
  40. package/dist/equipment/generic-prime-lenses.js.map +1 -1
  41. package/dist/equipment/generic-tier-assets.d.ts +23 -1
  42. package/dist/equipment/generic-tier-assets.js +58 -30
  43. package/dist/equipment/generic-tier-assets.js.map +1 -1
  44. package/dist/equipment/generic-tier-presets.d.ts +6 -3
  45. package/dist/equipment/generic-tier-presets.js +26 -13
  46. package/dist/equipment/generic-tier-presets.js.map +1 -1
  47. package/dist/exposure/auto-iso-slowest-shutter.js +2 -1
  48. package/dist/exposure/auto-iso-slowest-shutter.js.map +1 -1
  49. package/dist/exposure/flash-ttl.js +1 -1
  50. package/dist/exposure/flash-ttl.js.map +1 -1
  51. package/dist/exposure/flash.js +1 -1
  52. package/dist/exposure/flash.js.map +1 -1
  53. package/dist/facade/chart-material-layers.js +2 -2
  54. package/dist/facade/chart-material-layers.js.map +1 -1
  55. package/dist/facade/chart-radiance-batch.js +1 -1
  56. package/dist/facade/chart-radiance-batch.js.map +1 -1
  57. package/dist/facade/chart-scenes.d.ts +45 -8
  58. package/dist/facade/chart-scenes.js +101 -44
  59. package/dist/facade/chart-scenes.js.map +1 -1
  60. package/dist/facade/color-patches.d.ts +129 -0
  61. package/dist/facade/color-patches.js +209 -0
  62. package/dist/facade/color-patches.js.map +1 -0
  63. package/dist/facade/display-view-gpu.js +1 -1
  64. package/dist/facade/display-view-gpu.js.map +1 -1
  65. package/dist/facade/scene-radiance.js +2 -2
  66. package/dist/facade/scene-radiance.js.map +1 -1
  67. package/dist/facade/simulate-capture.d.ts +47 -3
  68. package/dist/facade/simulate-capture.js +71 -22
  69. package/dist/facade/simulate-capture.js.map +1 -1
  70. package/dist/facade/simulate-display-view.js +2 -2
  71. package/dist/facade/simulate-display-view.js.map +1 -1
  72. package/dist/gpu/chart-composite-gpu.js +2 -2
  73. package/dist/gpu/chart-composite-gpu.js.map +1 -1
  74. package/dist/gpu/display-view-composite-material-module.js +1 -1
  75. package/dist/gpu/display-view-composite-material-module.js.map +1 -1
  76. package/dist/gpu/image-kernel-execution.js +2 -2
  77. package/dist/gpu/image-kernel-execution.js.map +1 -1
  78. package/dist/gpu/temporal-sum-gpu.js +23 -23
  79. package/dist/gpu/webgpu-runtime.js +1 -1
  80. package/dist/gpu/webgpu-runtime.js.map +1 -1
  81. package/dist/gpu/wgsl-chart-composite-kernel.js +318 -318
  82. package/dist/gpu/wgsl-chart-composite-kernel.js.map +1 -1
  83. package/dist/gpu/wgsl-chart-kernel.js +117 -117
  84. package/dist/gpu/wgsl-chart-kernel.js.map +1 -1
  85. package/dist/gpu/wgsl-display-view-quality-kernel.js +505 -505
  86. package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
  87. package/dist/gpu/wgsl-display-view-speed-kernel.js +248 -248
  88. package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
  89. package/dist/gpu/wgsl-kernels.js +203 -203
  90. package/dist/gpu/wgsl-kernels.js.map +1 -1
  91. package/dist/gpu/wgsl-sensor-kernel.js +302 -302
  92. package/dist/gpu/wgsl-sensor-kernel.js.map +1 -1
  93. package/dist/index.d.ts +5 -3
  94. package/dist/index.js +4 -2
  95. package/dist/index.js.map +1 -1
  96. package/dist/motion/temporal-exposure-sum.js +1 -1
  97. package/dist/motion/temporal-exposure-sum.js.map +1 -1
  98. package/dist/optics/diffraction-psf-profile.js +4 -4
  99. package/dist/optics/diffraction-psf-profile.js.map +1 -1
  100. package/dist/optics/front-of-lens-filter.js +1 -1
  101. package/dist/optics/front-of-lens-filter.js.map +1 -1
  102. package/dist/optics/irradiance-convolution.js +1 -1
  103. package/dist/optics/irradiance-convolution.js.map +1 -1
  104. package/dist/output/native-capture-sdr.js +1 -1
  105. package/dist/output/native-capture-sdr.js.map +1 -1
  106. package/dist/output/png-encoder.js +2 -2
  107. package/dist/output/png-encoder.js.map +1 -1
  108. package/dist/output/print-jpeg.js +1 -1
  109. package/dist/output/print-jpeg.js.map +1 -1
  110. package/dist/sensor/fixed-pattern-noise.js +1 -1
  111. package/dist/sensor/fixed-pattern-noise.js.map +1 -1
  112. package/dist/sensor/readout-exposure-linkage.js +1 -1
  113. package/dist/sensor/readout-exposure-linkage.js.map +1 -1
  114. package/docs/API_REFERENCE.md +29 -1
  115. package/docs/QUICK_START.md +40 -5
  116. package/docs/USAGE.md +35 -1
  117. package/docs/api/core-version.md +1 -1
  118. package/docs/api/equipment-generic-tier-assets.md +41 -1
  119. package/docs/api/equipment-generic-tier-presets.md +11 -3
  120. package/docs/api/exports.json +107 -2
  121. package/docs/api/facade-chart-scenes.md +36 -5
  122. package/docs/api/facade-color-patches.md +150 -0
  123. package/docs/api/facade-simulate-capture.md +52 -2
  124. package/llms.txt +1 -0
  125. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"generic-tier-presets.js","sourceRoot":"","sources":["../../src/equipment/generic-tier-presets.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Generic tier presets: a catalog of exact preset versions for bodies and lenses.\n *\n * There is no hidden \"latest\" alias and no default sensor size. By default the lens tier matches the\n * body tier, but mixing tiers is a normal, supported choice.\n * @see docs/GENERIC_TIER_PRESETS.md for the equations, units, coordinate conventions, known blockers\n * and support limits.\n */\n\nimport { freezeOwnedData } from \"../core/owned-data.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { choice, record, parseOpticalProfileState, type OpticalProfileState, type GenericOpticalEvidence } from \"../optics/profile-contract.js\";\nimport { GENERIC_TIER_ASSETS, type GenericEquipmentTier, type GenericEquipmentTierPreset } from \"./generic-tier-assets.js\";\nimport { resolveGenericEquipmentExposureCapabilities } from \"./exposure-capabilities.js\";\nimport { parseGenericLensCorrectionProfile, type GenericLensCorrectionProfile } from \"../output/lens-corrections.js\";\nimport { parseLensStrayLightProfile, type LensStrayLightProfile } from \"../optics/stray-light.js\";\nimport { parseLensComplexPupilProfile, type LensComplexPupilProfile } from \"../optics/complex-pupil-psf.js\";\nimport { parseSceneToSensorIrradianceProfile, type SceneToSensorIrradianceProfile } from \"../optics/scene-to-sensor-irradiance.js\";\nimport type { RadialDistortionProfile } from \"../optics/radial-distortion.js\";\nimport type { LateralChromaticAberrationProfile } from \"../optics/lateral-chromatic-aberration.js\";\nimport type { IlluminationVignettingProfile } from \"../optics/illumination-vignetting.js\";\n\nexport const GENERIC_EQUIPMENT_TIER_PRESET_VERSION = \"2.0.0\" as const;\nexport const GENERIC_EQUIPMENT_TIERS = Object.freeze([\"consumer\", \"prosumer\", \"professional\"] as const);\nexport interface GenericEquipmentTierProfileReference { profileId: string; profileVersion: string }\nexport interface GenericEquipmentTierSelection {\n schemaVersion: \"0.1.0\"; presetVersion: typeof GENERIC_EQUIPMENT_TIER_PRESET_VERSION;\n bodyTier: GenericEquipmentTier; lensTier: GenericEquipmentTier;\n bodyPresetId: string; lensPresetId: string;\n profileReferences: readonly GenericEquipmentTierProfileReference[];\n sensorFormatSelectedByTier: false; scientificTierMultiplierApplied: false;\n}\nexport interface ResolvedGenericTierLensProfiles {\n selection: GenericEquipmentTierSelection;\n state: OpticalProfileState;\n radial: RadialDistortionProfile;\n lateralCa: LateralChromaticAberrationProfile;\n vignetting: IlluminationVignettingProfile;\n breathingProjectionScale: number;\n transmission: SceneToSensorIrradianceProfile;\n pupils: readonly LensComplexPupilProfile[];\n stray: LensStrayLightProfile;\n correction: GenericLensCorrectionProfile;\n scientificStatus: \"approximation\";\n}\nconst catalog = freezeOwnedData(GENERIC_TIER_ASSETS);\nfunction preset(tier: GenericEquipmentTier): GenericEquipmentTierPreset {\n return catalog.find((p) => p.tier === tier)!;\n}\n/** Catalog of exact versions. No hidden \"latest\" alias and no default sensor size. */\nexport function resolveGenericEquipmentTierCatalog(input: { presetVersion: string }): readonly GenericEquipmentTierPreset[] {\n const r = record(input, [\"presetVersion\"]);\n if (r.presetVersion !== GENERIC_EQUIPMENT_TIER_PRESET_VERSION) throw new InvalidConfigurationError(\"Unknown equipment preset version; historical definitions may not silently fall back.\");\n return catalog;\n}\nfunction bodyReferences(p: GenericEquipmentTierPreset): GenericEquipmentTierProfileReference[] {\n const b = p.body;\n return [b.exposure, b.iso, b.signalChain, b.release, b.focus, b.stabilization, b.whiteBalance, b.meteringCapabilities, b.flashSync]\n .map((profile) => ({ profileId: profile.profileId, profileVersion: profile.profileVersion }))\n .concat([...b.readout, b.readoutTiming, b.rollingReadoutTiming, b.fixedPatternNoise, ...b.metering]\n .map((a) => ({ profileId: a.assetId, profileVersion: a.assetVersion })));\n}\n/** The lens tier matches the body tier by default. Mixing tiers is a normal, supported choice. */\nexport function createGenericEquipmentTierSelection(input: {\n presetVersion: string; bodyTier: GenericEquipmentTier; lensTier?: GenericEquipmentTier;\n}): GenericEquipmentTierSelection {\n const r = record(input, [\"presetVersion\", \"bodyTier\", \"lensTier\"]);\n resolveGenericEquipmentTierCatalog({ presetVersion: r.presetVersion as string });\n const bodyTier = choice(r.bodyTier, GENERIC_EQUIPMENT_TIERS), lensTier = choice(r.lensTier === undefined ? bodyTier : r.lensTier, GENERIC_EQUIPMENT_TIERS);\n const body = preset(bodyTier), lens = preset(lensTier);\n return freezeOwnedData({ schemaVersion: \"0.1.0\", presetVersion: GENERIC_EQUIPMENT_TIER_PRESET_VERSION, bodyTier, lensTier,\n bodyPresetId: body.presetId+\":body\", lensPresetId: lens.presetId+\":lens\",\n profileReferences: [...bodyReferences(body), { profileId: lens.lens.exposure.profileId, profileVersion: lens.lens.exposure.profileVersion }, ...lens.lens.profileReferences],\n sensorFormatSelectedByTier: false, scientificTierMultiplierApplied: false });\n}\n/** On a saved simulation, rejects profile versions that are stale, missing or retuned, instead of picking today's defaults. */\nexport function parseGenericEquipmentTierSelection(value: unknown): GenericEquipmentTierSelection {\n const r = record(value, [\"schemaVersion\", \"presetVersion\", \"bodyTier\", \"lensTier\", \"bodyPresetId\", \"lensPresetId\", \"profileReferences\",\n \"sensorFormatSelectedByTier\", \"scientificTierMultiplierApplied\"]);\n const expected = createGenericEquipmentTierSelection({ presetVersion: r.presetVersion as string,\n bodyTier: r.bodyTier as GenericEquipmentTier, lensTier: choice(r.lensTier, GENERIC_EQUIPMENT_TIERS) });\n if (r.schemaVersion !== expected.schemaVersion || r.bodyPresetId !== expected.bodyPresetId || r.lensPresetId !== expected.lensPresetId ||\n r.sensorFormatSelectedByTier !== false || r.scientificTierMultiplierApplied !== false || !Array.isArray(r.profileReferences) ||\n r.profileReferences.length !== expected.profileReferences.length) throw new InvalidConfigurationError(\"Saved equipment selection differs from its exact preset manifest.\");\n for (let i = 0; i < expected.profileReferences.length; i++) {\n const ref = record(r.profileReferences[i], [\"profileId\", \"profileVersion\"]), actual = expected.profileReferences[i]!;\n if (ref.profileId !== actual.profileId || ref.profileVersion !== actual.profileVersion) throw new InvalidConfigurationError(\"Saved equipment profile reference drift.\");\n }\n return expected;\n}\n/** Ties the finite matched-reference optical data to one exact generic selected state. Never extrapolates. */\nexport function resolveGenericEquipmentTierLensProfiles(input: { selection: GenericEquipmentTierSelection; state: OpticalProfileState }): ResolvedGenericTierLensProfiles {\n const request = record(input, [\"selection\", \"state\"]), selection = parseGenericEquipmentTierSelection(request.selection), state = parseOpticalProfileState(request.state);\n const b = preset(selection.bodyTier), l = preset(selection.lensTier), a = l.lens.matchedReference;\n if (state.bodyId !== b.body.exposure.profileId.replace(/:exposure$/, \"\") || state.bodyVersion !== selection.presetVersion ||\n state.lensId !== l.lens.exposure.profileId.replace(/:exposure$/, \"\") || state.lensVersion !== selection.presetVersion ||\n state.focalLengthMm !== a.focalLengthMm || state.aperture !== a.aperture || state.focusDistanceM !== a.focusDistanceM ||\n state.captureMode !== \"still\" || state.stabilizationMode !== \"off\") throw new InvalidConfigurationError(\"No optical reference slice for this exact generic system/acquisition state.\");\n resolveGenericEquipmentExposureCapabilities({ bodyProfile: b.body.exposure, lensProfile: l.lens.exposure, selectedFocalLengthMm: state.focalLengthMm });\n const id = state.lensId, version = state.lensVersion, evidence = l.evidence;\n const opticalEvidence: GenericOpticalEvidence = { kind: \"generic-parametric\", basis: \"Photivra-owned educational tier design values, not measured calibration.\",\n residualNote: \"Finite matched-reference approximation; no branded signature or aggregate physical error bound.\", sources: evidence };\n const coeff = (k1: number): RadialDistortionProfile => ({ normalizationRadiusMm: a.normalizationRadiusMm, maximumNormalizedRadius: 1.1,\n coefficients: { k1, k2: 0, k3: 0 } });\n const radial = coeff(a.distortionK1), lateralCa: LateralChromaticAberrationProfile = { normalizationRadiusMm: a.normalizationRadiusMm,\n maximumNormalizedRadius: 1.1, baseDistortionCoefficients: radial.coefficients,\n redCoefficientOffset: { k1: a.lateralCaK1Offset, k2: 0, k3: 0 }, blueCoefficientOffset: { k1: -a.lateralCaK1Offset, k2: 0, k3: 0 } };\n const vignetting: IlluminationVignettingProfile = { normalizationRadiusMm: a.normalizationRadiusMm, maximumNormalizedRadius: 1.1,\n coefficients: { r2: a.vignettingR2, r4: 0, r6: 0 } };\n const identity = { version, domain: \"reconstructed-linear\" as const, availability: \"toggle\" as const, defaultEnabled: true,\n dependencies: [], requiredByStabilizationModes: [], residualNote: opticalEvidence.residualNote };\n const correction = parseGenericLensCorrectionProfile({ schemaVersion: \"0.1.0\", id: id+\":correction\", version, state, evidence: opticalEvidence,\n components: [{ ...identity, id: \"geometry\", kind: \"geometry\", transform: { id: id+\":radial-map\", version,\n domain: \"reconstructed-linear\", purpose: \"distortion\", kind: \"radial\", profile: radial } },\n { ...identity, id: \"lateral-ca\", kind: \"lateral-ca\", transforms: Object.fromEntries([\"red\", \"green\", \"blue\"].map((channel, i) => [channel, {\n id: id+\":ca-map:\"+channel, version, domain: \"reconstructed-linear\", purpose: \"lateral-ca\", kind: \"radial\",\n profile: coeff([a.lateralCaK1Offset*a.correctionCaFraction, 0, -a.lateralCaK1Offset*a.correctionCaFraction][i]!) }])) },\n { ...identity, id: \"gain\", kind: \"peripheral-illumination\", profile: vignetting, strength: a.correctionGainStrength }] });\n const stray = parseLensStrayLightProfile({ schemaVersion: \"0.1.0\", id: id+\":stray\", version, state, evidence: opticalEvidence,\n interaction: \"lens-reflections\", wavelengthNm: a.wavelengthNm, wavelengthBasis: \"vacuum\", maximumSourceAngleDegrees: 60,\n referenceEntranceAreaMm2: Math.PI*(50/4/2)**2, responses: [\n { id: \"ghost\", kind: \"ghost\", centroidMmPerDegree: [-.02,0,0,-.02], offsetMm: { x: 1, y: 0 }, sigmaMm: .5, axisPowerFraction: a.ghostPowerFraction, angularSlope: 1 },\n { id: \"veil\", kind: \"veil\", centroidMmPerDegree: [0,0,0,0], offsetMm: { x: 0, y: 0 }, sigmaMm: 10, axisPowerFraction: a.veilPowerFraction, angularSlope: 0 }] });\n const transmission = parseSceneToSensorIrradianceProfile({ schemaVersion: \"0.1.0\", profileId: id+\":transmission\", profileVersion: version, lensProfileId: id,\n scientificStatus: \"approximation\", applicability: { focalLengthMm: { minimum: 50, maximum: 50 }, nominalFNumber: { minimum: 4, maximum: 4 },\n focus: { kind: \"finite-distance-range\", objectDistanceM: { minimum: 5, maximum: 5 } } }, transmission: { kind: \"spectral-transmission\", scientificStatus: \"approximation\",\n wavelengthBasis: \"vacuum\", samples: { value: [{ wavelengthNanometers: 380, linearTransmissionFactor: a.transmission },\n { wavelengthNanometers: 780, linearTransmissionFactor: a.transmission }], evidence }, uncertainty: { kind: \"not-quantified\", limitation: \"Synthetic spectrally flat reference over 380-780 nm.\" } },\n distortionAreaMappingOwnership: \"not-applied-by-bridge\", psfRedistributionOwnership: \"downstream-normalized-energy-redistribution\", sensorOpticalStackIncluded: false,\n strayLightIncluded: false, polarizationModeled: false, wavelengthChangingBehaviorModeled: false, volumetricScatteringModeled: false, evidence, limitations: l.lens.limitations });\n const pupils = [0,1,2].flatMap((field) => [-20,0,20].map((defocus, index) => parseLensComplexPupilProfile({ schemaVersion: \"0.1.0\",\n profileId: id+\":pupil:\"+field+\":\"+index, profileVersion: version, scientificStatus: \"approximation\", opticalDomain: \"lens-primary-optical-path-only\",\n representation: \"complex-pupil-amplitude-plus-opd\", pupilCoordinateSystem: \"pupil-plane-metric-aligned-to-image-plane\", imageFieldAxes: \"+X right, +Y up\",\n amplitudeMeaning: \"relative-complex-pupil-amplitude-shape\", wavefrontMeaning: \"optical-path-difference-micrometers\",\n throughputOwnership: \"separate-relative-pupil-throughput-factor\", kernelEnergyNormalization: \"unit-energy-shape\", propagationModel: \"scalar-fraunhofer-discrete-reference\",\n context: { focalLengthMm: 50, focus: { kind: \"finite\", distanceM: 5 }, apertureFNumber: 4, fieldPointMm: [{x:0,y:0},{x:10,y:6},{x:18,y:12}][field],\n wavelengthNm: 550, signedDefocusImagePlaneMicrometers: defocus }, responseIncludes: { diffraction: true, aberration: true, defocus: true, pupilClippingShape: true },\n relativePupilThroughputFactor: a.pupilThroughputByField[field], grid: { widthSamples: 5, heightSamples: 5, pupilSamplePitchMmX: 3.125, pupilSamplePitchMmY: 3.125,\n centerSampleX: 2, centerSampleY: 2, relativeAmplitude: a.pupilAmplitudeByField[field], opticalPathDifferenceMicrometers: a.pupilOpdMicrometersByDefocus[index] },\n sensorOpticalStackIncluded: false, sensorSamplingIncluded: false, reconstructionIncluded: false, strayLightIncluded: false, evidence,\n uncertainty: { kind: \"not-quantified\", limitation: \"Coarse independently designed pupil-grid reference only.\" }, limitations: l.lens.limitations })));\n return freezeOwnedData({ selection, state, radial, lateralCa, vignetting, breathingProjectionScale: a.breathingProjectionScale, transmission, pupils, stray, correction,\n scientificStatus: \"approximation\" });\n}\n"]}
1
+ {"version":3,"file":"generic-tier-presets.js","sourceRoot":"","sources":["../../src/equipment/generic-tier-presets.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Generic tier presets: a catalog of exact preset versions for bodies and lenses.\n *\n * There is no hidden \"latest\" alias and no default sensor size. By default the lens tier matches the\n * body tier, but mixing tiers is a normal, supported choice.\n * @see docs/GENERIC_TIER_PRESETS.md for the equations, units, coordinate conventions, known blockers\n * and support limits.\n */\n\nimport { freezeOwnedData } from \"../core/owned-data.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { choice, record, parseOpticalProfileState, type OpticalProfileState, type GenericOpticalEvidence } from \"../optics/profile-contract.js\";\nimport { genericTierAssets, type GenericEquipmentTier, type GenericEquipmentTierPreset, type GenericTierPresetVersion } from \"./generic-tier-assets.js\";\nimport { resolveGenericEquipmentExposureCapabilities } from \"./exposure-capabilities.js\";\nimport { parseGenericLensCorrectionProfile, type GenericLensCorrectionProfile } from \"../output/lens-corrections.js\";\nimport { parseLensStrayLightProfile, type LensStrayLightProfile } from \"../optics/stray-light.js\";\nimport { parseLensComplexPupilProfile, type LensComplexPupilProfile } from \"../optics/complex-pupil-psf.js\";\nimport { parseSceneToSensorIrradianceProfile, type SceneToSensorIrradianceProfile } from \"../optics/scene-to-sensor-irradiance.js\";\nimport type { RadialDistortionProfile } from \"../optics/radial-distortion.js\";\nimport type { LateralChromaticAberrationProfile } from \"../optics/lateral-chromatic-aberration.js\";\nimport type { IlluminationVignettingProfile } from \"../optics/illumination-vignetting.js\";\n\n/** The current preset version (2.14: 2.1.0, third-stop ISO settings). */\nexport const GENERIC_EQUIPMENT_TIER_PRESET_VERSION = \"2.1.0\" as const;\n/** Every exact preset version that resolves, oldest first. A version is never retuned in place (`test/generic-tier-presets.test.ts`). */\nexport const GENERIC_EQUIPMENT_TIER_PRESET_VERSIONS = /* @__PURE__ */ Object.freeze([\"2.0.0\", \"2.1.0\"] as const);\nexport const GENERIC_EQUIPMENT_TIERS = /* @__PURE__ */ Object.freeze([\"consumer\", \"prosumer\", \"professional\"] as const);\nexport interface GenericEquipmentTierProfileReference { profileId: string; profileVersion: string }\nexport interface GenericEquipmentTierSelection {\n schemaVersion: \"0.1.0\"; presetVersion: GenericTierPresetVersion;\n bodyTier: GenericEquipmentTier; lensTier: GenericEquipmentTier;\n bodyPresetId: string; lensPresetId: string;\n profileReferences: readonly GenericEquipmentTierProfileReference[];\n sensorFormatSelectedByTier: false; scientificTierMultiplierApplied: false;\n}\nexport interface ResolvedGenericTierLensProfiles {\n selection: GenericEquipmentTierSelection;\n state: OpticalProfileState;\n radial: RadialDistortionProfile;\n lateralCa: LateralChromaticAberrationProfile;\n vignetting: IlluminationVignettingProfile;\n breathingProjectionScale: number;\n transmission: SceneToSensorIrradianceProfile;\n pupils: readonly LensComplexPupilProfile[];\n stray: LensStrayLightProfile;\n correction: GenericLensCorrectionProfile;\n scientificStatus: \"approximation\";\n}\nconst catalogs = new Map<GenericTierPresetVersion, readonly GenericEquipmentTierPreset[]>();\nfunction catalogOf(version: GenericTierPresetVersion): readonly GenericEquipmentTierPreset[] {\n let catalog = catalogs.get(version);\n if (!catalog) catalogs.set(version, catalog = freezeOwnedData(genericTierAssets(version)));\n return catalog;\n}\nfunction preset(version: GenericTierPresetVersion, tier: GenericEquipmentTier): GenericEquipmentTierPreset {\n return catalogOf(version).find((p) => p.tier === tier)!;\n}\nfunction exactVersion(value: unknown): GenericTierPresetVersion {\n if (!(GENERIC_EQUIPMENT_TIER_PRESET_VERSIONS as readonly unknown[]).includes(value)) {\n throw new InvalidConfigurationError(\"Unknown equipment preset version; historical definitions may not silently fall back.\");\n }\n return value as GenericTierPresetVersion;\n}\n/** Catalog of exact versions. No hidden \"latest\" alias and no default sensor size. */\nexport function resolveGenericEquipmentTierCatalog(input: { presetVersion: string }): readonly GenericEquipmentTierPreset[] {\n const r = record(input, [\"presetVersion\"]);\n return catalogOf(exactVersion(r.presetVersion));\n}\nfunction bodyReferences(p: GenericEquipmentTierPreset): GenericEquipmentTierProfileReference[] {\n const b = p.body;\n return [b.exposure, b.iso, b.signalChain, b.release, b.focus, b.stabilization, b.whiteBalance, b.meteringCapabilities, b.flashSync]\n .map((profile) => ({ profileId: profile.profileId, profileVersion: profile.profileVersion }))\n .concat([...b.readout, b.readoutTiming, b.rollingReadoutTiming, b.fixedPatternNoise, ...b.metering]\n .map((a) => ({ profileId: a.assetId, profileVersion: a.assetVersion })));\n}\n/** The lens tier matches the body tier by default. Mixing tiers is a normal, supported choice. */\nexport function createGenericEquipmentTierSelection(input: {\n presetVersion: string; bodyTier: GenericEquipmentTier; lensTier?: GenericEquipmentTier;\n}): GenericEquipmentTierSelection {\n const r = record(input, [\"presetVersion\", \"bodyTier\", \"lensTier\"]);\n const presetVersion = exactVersion(r.presetVersion);\n const bodyTier = choice(r.bodyTier, GENERIC_EQUIPMENT_TIERS), lensTier = choice(r.lensTier === undefined ? bodyTier : r.lensTier, GENERIC_EQUIPMENT_TIERS);\n const body = preset(presetVersion, bodyTier), lens = preset(presetVersion, lensTier);\n return freezeOwnedData({ schemaVersion: \"0.1.0\", presetVersion, bodyTier, lensTier,\n bodyPresetId: body.presetId+\":body\", lensPresetId: lens.presetId+\":lens\",\n profileReferences: [...bodyReferences(body), { profileId: lens.lens.exposure.profileId, profileVersion: lens.lens.exposure.profileVersion }, ...lens.lens.profileReferences],\n sensorFormatSelectedByTier: false, scientificTierMultiplierApplied: false });\n}\n/** On a saved simulation, rejects profile versions that are stale, missing or retuned, instead of picking today's defaults. */\nexport function parseGenericEquipmentTierSelection(value: unknown): GenericEquipmentTierSelection {\n const r = record(value, [\"schemaVersion\", \"presetVersion\", \"bodyTier\", \"lensTier\", \"bodyPresetId\", \"lensPresetId\", \"profileReferences\",\n \"sensorFormatSelectedByTier\", \"scientificTierMultiplierApplied\"]);\n const expected = createGenericEquipmentTierSelection({ presetVersion: r.presetVersion as string,\n bodyTier: r.bodyTier as GenericEquipmentTier, lensTier: choice(r.lensTier, GENERIC_EQUIPMENT_TIERS) });\n if (r.schemaVersion !== expected.schemaVersion || r.bodyPresetId !== expected.bodyPresetId || r.lensPresetId !== expected.lensPresetId ||\n r.sensorFormatSelectedByTier !== false || r.scientificTierMultiplierApplied !== false || !Array.isArray(r.profileReferences) ||\n r.profileReferences.length !== expected.profileReferences.length) throw new InvalidConfigurationError(\"Saved equipment selection differs from its exact preset manifest.\");\n for (let i = 0; i < expected.profileReferences.length; i++) {\n const ref = record(r.profileReferences[i], [\"profileId\", \"profileVersion\"]), actual = expected.profileReferences[i]!;\n if (ref.profileId !== actual.profileId || ref.profileVersion !== actual.profileVersion) throw new InvalidConfigurationError(\"Saved equipment profile reference drift.\");\n }\n return expected;\n}\n/** Ties the finite matched-reference optical data to one exact generic selected state. Never extrapolates. */\nexport function resolveGenericEquipmentTierLensProfiles(input: { selection: GenericEquipmentTierSelection; state: OpticalProfileState }): ResolvedGenericTierLensProfiles {\n const request = record(input, [\"selection\", \"state\"]), selection = parseGenericEquipmentTierSelection(request.selection), state = parseOpticalProfileState(request.state);\n const b = preset(selection.presetVersion, selection.bodyTier), l = preset(selection.presetVersion, selection.lensTier), a = l.lens.matchedReference;\n if (state.bodyId !== b.body.exposure.profileId.replace(/:exposure$/, \"\") || state.bodyVersion !== selection.presetVersion ||\n state.lensId !== l.lens.exposure.profileId.replace(/:exposure$/, \"\") || state.lensVersion !== selection.presetVersion ||\n state.focalLengthMm !== a.focalLengthMm || state.aperture !== a.aperture || state.focusDistanceM !== a.focusDistanceM ||\n state.captureMode !== \"still\" || state.stabilizationMode !== \"off\") throw new InvalidConfigurationError(\"No optical reference slice for this exact generic system/acquisition state.\");\n resolveGenericEquipmentExposureCapabilities({ bodyProfile: b.body.exposure, lensProfile: l.lens.exposure, selectedFocalLengthMm: state.focalLengthMm });\n const id = state.lensId, version = state.lensVersion, evidence = l.evidence;\n const opticalEvidence: GenericOpticalEvidence = { kind: \"generic-parametric\", basis: \"Photivra-owned educational tier design values, not measured calibration.\",\n residualNote: \"Finite matched-reference approximation; no branded signature or aggregate physical error bound.\", sources: evidence };\n const coeff = (k1: number): RadialDistortionProfile => ({ normalizationRadiusMm: a.normalizationRadiusMm, maximumNormalizedRadius: 1.1,\n coefficients: { k1, k2: 0, k3: 0 } });\n const radial = coeff(a.distortionK1), lateralCa: LateralChromaticAberrationProfile = { normalizationRadiusMm: a.normalizationRadiusMm,\n maximumNormalizedRadius: 1.1, baseDistortionCoefficients: radial.coefficients,\n redCoefficientOffset: { k1: a.lateralCaK1Offset, k2: 0, k3: 0 }, blueCoefficientOffset: { k1: -a.lateralCaK1Offset, k2: 0, k3: 0 } };\n const vignetting: IlluminationVignettingProfile = { normalizationRadiusMm: a.normalizationRadiusMm, maximumNormalizedRadius: 1.1,\n coefficients: { r2: a.vignettingR2, r4: 0, r6: 0 } };\n const identity = { version, domain: \"reconstructed-linear\" as const, availability: \"toggle\" as const, defaultEnabled: true,\n dependencies: [], requiredByStabilizationModes: [], residualNote: opticalEvidence.residualNote };\n const correction = parseGenericLensCorrectionProfile({ schemaVersion: \"0.1.0\", id: id+\":correction\", version, state, evidence: opticalEvidence,\n components: [{ ...identity, id: \"geometry\", kind: \"geometry\", transform: { id: id+\":radial-map\", version,\n domain: \"reconstructed-linear\", purpose: \"distortion\", kind: \"radial\", profile: radial } },\n { ...identity, id: \"lateral-ca\", kind: \"lateral-ca\", transforms: Object.fromEntries([\"red\", \"green\", \"blue\"].map((channel, i) => [channel, {\n id: id+\":ca-map:\"+channel, version, domain: \"reconstructed-linear\", purpose: \"lateral-ca\", kind: \"radial\",\n profile: coeff([a.lateralCaK1Offset*a.correctionCaFraction, 0, -a.lateralCaK1Offset*a.correctionCaFraction][i]!) }])) },\n { ...identity, id: \"gain\", kind: \"peripheral-illumination\", profile: vignetting, strength: a.correctionGainStrength }] });\n const stray = parseLensStrayLightProfile({ schemaVersion: \"0.1.0\", id: id+\":stray\", version, state, evidence: opticalEvidence,\n interaction: \"lens-reflections\", wavelengthNm: a.wavelengthNm, wavelengthBasis: \"vacuum\", maximumSourceAngleDegrees: 60,\n referenceEntranceAreaMm2: Math.PI*(50/4/2)**2, responses: [\n { id: \"ghost\", kind: \"ghost\", centroidMmPerDegree: [-.02,0,0,-.02], offsetMm: { x: 1, y: 0 }, sigmaMm: .5, axisPowerFraction: a.ghostPowerFraction, angularSlope: 1 },\n { id: \"veil\", kind: \"veil\", centroidMmPerDegree: [0,0,0,0], offsetMm: { x: 0, y: 0 }, sigmaMm: 10, axisPowerFraction: a.veilPowerFraction, angularSlope: 0 }] });\n const transmission = parseSceneToSensorIrradianceProfile({ schemaVersion: \"0.1.0\", profileId: id+\":transmission\", profileVersion: version, lensProfileId: id,\n scientificStatus: \"approximation\", applicability: { focalLengthMm: { minimum: 50, maximum: 50 }, nominalFNumber: { minimum: 4, maximum: 4 },\n focus: { kind: \"finite-distance-range\", objectDistanceM: { minimum: 5, maximum: 5 } } }, transmission: { kind: \"spectral-transmission\", scientificStatus: \"approximation\",\n wavelengthBasis: \"vacuum\", samples: { value: [{ wavelengthNanometers: 380, linearTransmissionFactor: a.transmission },\n { wavelengthNanometers: 780, linearTransmissionFactor: a.transmission }], evidence }, uncertainty: { kind: \"not-quantified\", limitation: \"Synthetic spectrally flat reference over 380-780 nm.\" } },\n distortionAreaMappingOwnership: \"not-applied-by-bridge\", psfRedistributionOwnership: \"downstream-normalized-energy-redistribution\", sensorOpticalStackIncluded: false,\n strayLightIncluded: false, polarizationModeled: false, wavelengthChangingBehaviorModeled: false, volumetricScatteringModeled: false, evidence, limitations: l.lens.limitations });\n const pupils = [0,1,2].flatMap((field) => [-20,0,20].map((defocus, index) => parseLensComplexPupilProfile({ schemaVersion: \"0.1.0\",\n profileId: id+\":pupil:\"+field+\":\"+index, profileVersion: version, scientificStatus: \"approximation\", opticalDomain: \"lens-primary-optical-path-only\",\n representation: \"complex-pupil-amplitude-plus-opd\", pupilCoordinateSystem: \"pupil-plane-metric-aligned-to-image-plane\", imageFieldAxes: \"+X right, +Y up\",\n amplitudeMeaning: \"relative-complex-pupil-amplitude-shape\", wavefrontMeaning: \"optical-path-difference-micrometers\",\n throughputOwnership: \"separate-relative-pupil-throughput-factor\", kernelEnergyNormalization: \"unit-energy-shape\", propagationModel: \"scalar-fraunhofer-discrete-reference\",\n context: { focalLengthMm: 50, focus: { kind: \"finite\", distanceM: 5 }, apertureFNumber: 4, fieldPointMm: [{x:0,y:0},{x:10,y:6},{x:18,y:12}][field],\n wavelengthNm: 550, signedDefocusImagePlaneMicrometers: defocus }, responseIncludes: { diffraction: true, aberration: true, defocus: true, pupilClippingShape: true },\n relativePupilThroughputFactor: a.pupilThroughputByField[field], grid: { widthSamples: 5, heightSamples: 5, pupilSamplePitchMmX: 3.125, pupilSamplePitchMmY: 3.125,\n centerSampleX: 2, centerSampleY: 2, relativeAmplitude: a.pupilAmplitudeByField[field], opticalPathDifferenceMicrometers: a.pupilOpdMicrometersByDefocus[index] },\n sensorOpticalStackIncluded: false, sensorSamplingIncluded: false, reconstructionIncluded: false, strayLightIncluded: false, evidence,\n uncertainty: { kind: \"not-quantified\", limitation: \"Coarse independently designed pupil-grid reference only.\" }, limitations: l.lens.limitations })));\n return freezeOwnedData({ selection, state, radial, lateralCa, vignetting, breathingProjectionScale: a.breathingProjectionScale, transmission, pupils, stray, correction,\n scientificStatus: \"approximation\" });\n}\n"]}
@@ -36,7 +36,8 @@ export const AUTO_ISO_SLOWEST_SHUTTER_MODEL_VERSION = "1.0.0";
36
36
  * placed on the yaw axis (for small angles the blur length does not depend on the direction). A
37
37
  * heavier camera, another grip or another photographer moves differently: pass your own profile.
38
38
  */
39
- export const GENERIC_HANDHELD_ANGULAR_VELOCITY_RAD_PER_SEC = Object.freeze({ yaw: (4.39 * Math.PI) / 180, pitch: 0 });
39
+ export const GENERIC_HANDHELD_ANGULAR_VELOCITY_RAD_PER_SEC =
40
+ /* @__PURE__ */ Object.freeze({ yaw: (4.39 * Math.PI) / 180, pitch: 0 });
40
41
  /** The largest whole-stop bias accepted, either way (makers offer up to 2 stops faster or slower). */
41
42
  export const AUTO_ISO_SLOWEST_SHUTTER_MAXIMUM_BIAS_STOPS = 2;
42
43
  function requirePositive(value, path) {
@@ -1 +1 @@
1
- {"version":3,"file":"auto-iso-slowest-shutter.js","sourceRoot":"","sources":["../../src/exposure/auto-iso-slowest-shutter.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * #504: the automatic slowest preferred shutter for Auto ISO (\"Auto\" minimum shutter speed), derived\n * from the engine's camera-shake model (`estimateCameraShakeBlur()`), not from the 1/focal-length rule\n * of thumb.\n *\n * The camera turns at a constant angular velocity ω (yaw, pitch) during the exposure, reduced by the\n * stabilization factor r = 2^−stops. A still point on the optical axis then moves\n * b(t) = v·hypot(tan(r·ω_yaw·t), tan(r·ω_pitch·t)) on the sensor, where v is the projection distance (the\n * focal length, or the thin-lens image distance with a focus distance). The derived shutter is the\n * exposure time at which b(t) equals the caller's blur budget. b(t) grows strictly with t, so the\n * solution is unique; the whole-stop bias (as in makers' Auto minimum-shutter settings: faster or\n * slower in 1 EV steps) is applied after the derivation, and the optional shutter grid last.\n *\n * This is an approximation: the hand's motion is a constant-velocity profile supplied by the caller,\n * and stabilization stops scale the motion by 2^−stops (the educational model of\n * `estimateCameraShakeBlur()`, not a CIPA DC-011 rating).\n * @see docs/USAGE.md (\"Automatic slowest shutter (#504)\") for the equations, the cited generic\n * hand-shake default and the support limits.\n */\n\nimport { approximationResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { rejectUnknownFields, requireInputKeys, requireInputObject } from \"../core/record-validation.js\";\nimport { InvalidScientificInputError, InvalidScientificResultError } from \"../core/validation.js\";\nimport { portableHypot } from \"../core/portable-math.js\";\nimport type { ResolvedGenericEquipmentExposureCapabilities } from \"../equipment/exposure-capabilities.js\";\nimport { estimateCameraShakeBlur, type CameraShakeEstimate } from \"../stabilization/camera-shake.js\";\n\nexport const AUTO_ISO_SLOWEST_SHUTTER_MODEL_VERSION = \"1.0.0\" as const;\n\n/**\n * A generic handheld angular velocity, labelled as a default and not a measurement of any camera or\n * photographer: 4.39 °/s (≈ 0.0766 rad/s), the linear camera-motion rate Xiao, Farrell, Catrysse and\n * Wandell use for \"an average user\" holding \"a typical 100g mobile handset with two hands\", from their\n * earlier gyroscope measurements (F. Xiao, J. Farrell, P. Catrysse, B. Wandell, \"Mobile imaging: the\n * big challenge of the small pixel\", Proc. SPIE 7250, Digital Photography V, 2009, §2; author copy:\n * https://web.stanford.edu/~wandell/data/papers/2009-CameraShake-Xiao.pdf; they cite F. Xiao et al.,\n * \"Camera-motion and mobile imaging\", Proc. SPIE 6502, 2007). The value is one rate magnitude; it is\n * placed on the yaw axis (for small angles the blur length does not depend on the direction). A\n * heavier camera, another grip or another photographer moves differently: pass your own profile.\n */\nexport const GENERIC_HANDHELD_ANGULAR_VELOCITY_RAD_PER_SEC: Readonly<{ yaw: number; pitch: number }> =\n Object.freeze({ yaw: (4.39 * Math.PI) / 180, pitch: 0 });\n\n/** The largest whole-stop bias accepted, either way (makers offer up to 2 stops faster or slower). */\nexport const AUTO_ISO_SLOWEST_SHUTTER_MAXIMUM_BIAS_STOPS = 2 as const;\n\n/** The blur allowed during the exposure, on the sensor. */\nexport type AutoIsoSlowestShutterBlurBudget =\n | {\n /** The blur trace length in pixels of the given pitch. */\n kind: \"pixels\";\n pixels: number;\n pixelPitchMicrometers: number;\n }\n | {\n /** The blur trace length as a fraction of the image diagonal (width × height in millimetres). */\n kind: \"image-diagonal-fraction\";\n fraction: number;\n imageWidthMm: number;\n imageHeightMm: number;\n };\n\nexport interface EstimateAutoIsoSlowestShutterInput {\n focalLengthMm: number;\n /** Optional focus distance in metres: the thin-lens image distance becomes the projection distance, as in `estimateCameraShakeBlur()`. */\n focusDistanceM?: number;\n /**\n * The hand's constant angular velocity in radians per second, the explicit shake profile (signs as in\n * `estimateCameraShakeBlur()`; only the magnitude matters here). At least one axis must be non-zero.\n * `GENERIC_HANDHELD_ANGULAR_VELOCITY_RAD_PER_SEC` is a cited generic default, not a measurement of you.\n */\n angularVelocityRadPerSec: { yaw: number; pitch: number };\n /** Stabilization in equivalent stops (≥ 0); the motion is scaled by 2^−stops. Default 0. */\n stabilizationStopsEquivalent?: number;\n blurBudget: AutoIsoSlowestShutterBlurBudget;\n /**\n * The bias in whole stops, applied after the derivation: +1 is one stop faster (half the time), −1 one\n * stop slower (twice the time). An integer from −2 to +2. Default 0.\n */\n fasterByStops?: number;\n /** Optional: the body's resolved shutter capability. The biased shutter then snaps to it, never longer than the biased value, and is held at its ends. */\n shutterCapability?: ResolvedGenericEquipmentExposureCapabilities[\"shutter\"];\n}\n\nexport interface AutoIsoSlowestShutterEstimate {\n /** The exposure time whose shake blur equals the budget, before the bias and the grid. */\n derivedShutterSeconds: number;\n fasterByStops: number;\n /** derivedShutterSeconds · 2^−fasterByStops. */\n biasedShutterSeconds: number;\n /** The slowest preferred shutter: the biased value, snapped to `shutterCapability` when given. */\n shutterSeconds: number;\n shutterResolution: {\n kind: \"unconstrained\" | \"continuous\" | \"discrete\";\n selectionPolicy: \"not-longer-than-target\";\n quantized: boolean;\n clamped: false | \"minimum\" | \"maximum\";\n };\n blurBudgetMm: number;\n /** `estimateCameraShakeBlur()`'s stabilized blur at the derived shutter (equal to the budget within rounding). */\n blurAtDerivedShutterMm: number;\n /** `estimateCameraShakeBlur()`'s stabilized blur at `shutterSeconds`. */\n blurAtShutterMm: number;\n /** The same blur lengths in pixels, for a pixel budget. */\n blurBudgetPixels?: number;\n blurAtShutterPixels?: number;\n projectionDistanceMm: number;\n residualMotionFactor: number;\n angularVelocityRadPerSec: { yaw: number; pitch: number };\n stabilizationStopsEquivalent: number;\n}\n\nfunction requirePositive(value: unknown, path: string): number {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value <= 0) {\n throw new InvalidScientificInputError(path + \" must be finite and greater than zero.\");\n }\n return value;\n}\n\nfunction parseBudget(value: unknown): { budgetMm: number; budgetPixels?: number; pixelPitchMicrometers?: number } {\n requireInputObject(value, \"blurBudget\");\n const record = value as Record<string, unknown>;\n if (record.kind === \"pixels\") {\n rejectUnknownFields(record, [\"kind\", \"pixels\", \"pixelPitchMicrometers\"], \"blurBudget\");\n const pixels = requirePositive(record.pixels, \"blurBudget.pixels\");\n const pitch = requirePositive(record.pixelPitchMicrometers, \"blurBudget.pixelPitchMicrometers\");\n return { budgetMm: (pixels * pitch) / 1000, budgetPixels: pixels, pixelPitchMicrometers: pitch };\n }\n if (record.kind === \"image-diagonal-fraction\") {\n rejectUnknownFields(record, [\"kind\", \"fraction\", \"imageWidthMm\", \"imageHeightMm\"], \"blurBudget\");\n const fraction = requirePositive(record.fraction, \"blurBudget.fraction\");\n if (fraction >= 1) throw new InvalidScientificInputError(\"blurBudget.fraction must be less than 1.\");\n const width = requirePositive(record.imageWidthMm, \"blurBudget.imageWidthMm\");\n const height = requirePositive(record.imageHeightMm, \"blurBudget.imageHeightMm\");\n return { budgetMm: fraction * portableHypot(width, height) };\n }\n throw new InvalidConfigurationError('blurBudget.kind must be \"pixels\" or \"image-diagonal-fraction\".');\n}\n\n/**\n * Solves v·hypot(tan(a·t), tan(b·t)) = budget for t, with a, b ≥ 0 the stabilized angular speeds. One\n * axis: t = atan(budget/v)/a exactly. Two axes: bisection on the strictly increasing blur over\n * (0, π/(2·max(a, b))), a fixed number of halvings (deterministic, to the last bit of the bracket).\n */\nfunction solveShutterForBudget(projectionMm: number, a: number, b: number, budgetMm: number): number {\n const ratio = budgetMm / projectionMm;\n if (a === 0 || b === 0) return Math.atan(ratio) / Math.max(a, b);\n let low = 0, high = Math.PI / 2 / Math.max(a, b);\n for (let i = 0; i < 200 && high - low > 0; i++) {\n const mid = low + (high - low) / 2;\n if (mid <= low || mid >= high) break;\n if (portableHypot(Math.tan(a * mid), Math.tan(b * mid)) < ratio) low = mid;\n else high = mid;\n }\n return low + (high - low) / 2;\n}\n\nfunction snapNotLonger(\n target: number,\n shutter: ResolvedGenericEquipmentExposureCapabilities[\"shutter\"]\n): { shutterSeconds: number; kind: \"continuous\" | \"discrete\"; quantized: boolean; clamped: false | \"minimum\" | \"maximum\" } {\n const grid = shutter.settingGrid;\n if (grid.kind === \"continuous-within-range\") {\n if (target < shutter.minimumSeconds) return { shutterSeconds: shutter.minimumSeconds, kind: \"continuous\", quantized: false, clamped: \"minimum\" };\n if (target > shutter.maximumSeconds) return { shutterSeconds: shutter.maximumSeconds, kind: \"continuous\", quantized: false, clamped: \"maximum\" };\n return { shutterSeconds: target, kind: \"continuous\", quantized: false, clamped: false };\n }\n const values = grid.values;\n const tolerance = (x: number): number => Number.EPSILON * 32 * Math.max(Math.abs(x), 1);\n const exact = values.find((v) => Math.abs(v - target) <= tolerance(Math.max(v, target)));\n if (exact !== undefined) return { shutterSeconds: exact, kind: \"discrete\", quantized: false, clamped: false };\n if (target < values[0]!) return { shutterSeconds: values[0]!, kind: \"discrete\", quantized: true, clamped: \"minimum\" };\n const last = values[values.length - 1]!;\n if (target > last) return { shutterSeconds: last, kind: \"discrete\", quantized: true, clamped: \"maximum\" };\n let selected = values[0]!;\n for (const v of values) if (v <= target) selected = v;\n return { shutterSeconds: selected, kind: \"discrete\", quantized: true, clamped: false };\n}\n\n/**\n * Estimates the slowest preferred shutter for Auto ISO from the lens, the sensor and the hand's\n * shake: the exposure time at which `estimateCameraShakeBlur()`'s stabilized blur equals the blur\n * budget, then shifted by a whole-stop bias and optionally snapped to the body's shutter grid (never\n * longer than the biased value). Longer focal lengths give faster shutters; each stabilization stop\n * doubles the time; each bias stop halves (+1) or doubles (−1) it.\n *\n * An approximation: constant angular velocity (a caller profile, or the labelled generic default\n * `GENERIC_HANDHELD_ANGULAR_VELOCITY_RAD_PER_SEC`), stabilization as 2^−stops, blur on the optical axis\n * only. The 1/focal-length \"reciprocal rule\" is a rule of thumb and is not used.\n * @param input - EstimateAutoIsoSlowestShutterInput.\n * @returns The derived, biased and snapped shutter with the blur at each, as an approximation result.\n */\nexport function estimateAutoIsoSlowestShutter(\n input: EstimateAutoIsoSlowestShutterInput\n): CalculationResult<AutoIsoSlowestShutterEstimate> {\n requireInputKeys(input, \"estimateAutoIsoSlowestShutter\", [\"focalLengthMm\", \"focusDistanceM\", \"angularVelocityRadPerSec\", \"stabilizationStopsEquivalent\",\n \"blurBudget\", \"fasterByStops\", \"shutterCapability\"]);\n const focalLengthMm = requirePositive(input.focalLengthMm, \"focalLengthMm\");\n requireInputObject(input.angularVelocityRadPerSec, \"angularVelocityRadPerSec\");\n rejectUnknownFields(input.angularVelocityRadPerSec, [\"yaw\", \"pitch\"], \"angularVelocityRadPerSec\");\n const { yaw, pitch } = input.angularVelocityRadPerSec;\n for (const [name, v] of [[\"yaw\", yaw], [\"pitch\", pitch]] as const) {\n if (typeof v !== \"number\" || !Number.isFinite(v)) throw new InvalidScientificInputError(\"angularVelocityRadPerSec.\" + name + \" must be finite.\");\n }\n if (yaw === 0 && pitch === 0) {\n throw new InvalidScientificInputError(\"angularVelocityRadPerSec must be non-zero on at least one axis: without shake no shutter limit follows.\");\n }\n const stops = input.stabilizationStopsEquivalent ?? 0;\n if (typeof stops !== \"number\" || !Number.isFinite(stops) || stops < 0) {\n throw new InvalidScientificInputError(\"stabilizationStopsEquivalent must be finite and greater than or equal to zero.\");\n }\n const bias = input.fasterByStops ?? 0;\n if (typeof bias !== \"number\" || !Number.isInteger(bias) || Math.abs(bias) > AUTO_ISO_SLOWEST_SHUTTER_MAXIMUM_BIAS_STOPS) {\n throw new InvalidScientificInputError(\"fasterByStops must be a whole number of stops from -2 to +2.\");\n }\n const budget = parseBudget(input.blurBudget);\n if (input.focusDistanceM !== undefined) requirePositive(input.focusDistanceM, \"focusDistanceM\");\n if (input.shutterCapability !== undefined) {\n requireInputObject(input.shutterCapability, \"shutterCapability\");\n const s = input.shutterCapability;\n requirePositive(s.minimumSeconds, \"shutterCapability.minimumSeconds\");\n requirePositive(s.maximumSeconds, \"shutterCapability.maximumSeconds\");\n if (s.minimumSeconds > s.maximumSeconds) throw new InvalidScientificInputError(\"shutterCapability.minimumSeconds must not exceed shutterCapability.maximumSeconds.\");\n requireInputObject(s.settingGrid, \"shutterCapability.settingGrid\");\n if (s.settingGrid.kind === \"discrete-values\") {\n const values = s.settingGrid.values;\n if (!Array.isArray(values) || values.length === 0 || values.some((v, i) => typeof v !== \"number\" || !Number.isFinite(v) || v <= 0 || (i > 0 && v <= values[i - 1]!))) {\n throw new InvalidScientificInputError(\"shutterCapability.settingGrid.values must be strictly increasing positive finite seconds.\");\n }\n } else if (s.settingGrid.kind !== \"continuous-within-range\") {\n throw new InvalidConfigurationError(\"shutterCapability.settingGrid.kind is invalid.\");\n }\n }\n\n // The projection distance and the residual factor come from the shake model itself (a probe at a tiny time).\n const shake = (shutterSeconds: number): CameraShakeEstimate => estimateCameraShakeBlur({ focalLengthMm, shutterSeconds, angularVelocityRadPerSec: { yaw, pitch },\n stabilizationStopsEquivalent: stops, ...(input.focusDistanceM === undefined ? {} : { focusDistanceM: input.focusDistanceM }),\n ...(budget.pixelPitchMicrometers === undefined ? {} : { pixelPitchMicrometers: budget.pixelPitchMicrometers }) }).value;\n const probe = shake(Number.MIN_VALUE);\n const r = probe.residualMotionFactor, v = probe.projectionDistanceMm;\n const derived = solveShutterForBudget(v, Math.abs(yaw) * r, Math.abs(pitch) * r, budget.budgetMm);\n const biased = derived * 2 ** -bias;\n if (!Number.isFinite(derived) || derived <= 0 || !Number.isFinite(biased) || biased <= 0) {\n throw new InvalidScientificResultError(\"The derived slowest shutter is not a positive finite time.\");\n }\n const snapped = input.shutterCapability === undefined ? undefined : snapNotLonger(biased, input.shutterCapability);\n const shutterSeconds = snapped?.shutterSeconds ?? biased;\n // estimateCameraShakeBlur rejects a quarter turn or more on an axis of the *unstabilized* motion, which\n // a long biased or clamped time with stabilization can reach; the blur there is not reported.\n const quarterTurnSafe = (t: number): boolean => Math.max(Math.abs(yaw), Math.abs(pitch)) * t < Math.PI / 2;\n if (!quarterTurnSafe(derived) || !quarterTurnSafe(shutterSeconds)) {\n throw new InvalidScientificInputError(\n \"The shake over the slowest shutter would reach a quarter turn (|angular velocity × shutter| ≥ π/2 rad) before stabilization; the constant-velocity model does not apply.\");\n }\n const atDerived = shake(derived).stabilized, atShutter = shake(shutterSeconds).stabilized;\n\n return approximationResult<AutoIsoSlowestShutterEstimate>({\n derivedShutterSeconds: derived,\n fasterByStops: bias,\n biasedShutterSeconds: biased,\n shutterSeconds,\n shutterResolution: snapped === undefined\n ? { kind: \"unconstrained\", selectionPolicy: \"not-longer-than-target\", quantized: false, clamped: false }\n : { kind: snapped.kind, selectionPolicy: \"not-longer-than-target\", quantized: snapped.quantized, clamped: snapped.clamped },\n blurBudgetMm: budget.budgetMm,\n blurAtDerivedShutterMm: atDerived.distanceMm,\n blurAtShutterMm: atShutter.distanceMm,\n ...(budget.budgetPixels === undefined ? {} : { blurBudgetPixels: budget.budgetPixels, blurAtShutterPixels: atShutter.distancePixels! }),\n projectionDistanceMm: v,\n residualMotionFactor: r,\n angularVelocityRadPerSec: { yaw, pitch },\n stabilizationStopsEquivalent: stops\n }, \"camera-shake-blur-budget-slowest-shutter\", AUTO_ISO_SLOWEST_SHUTTER_MODEL_VERSION, [\n \"The slowest shutter is the exposure time at which estimateCameraShakeBlur()'s stabilized on-axis blur equals the blur budget\",\n \"The hand turns the camera at a constant angular velocity during the exposure (a caller-supplied profile)\",\n \"Stabilization attenuates the angular motion by 2^-stops (educational model, not a CIPA DC-011 rating)\",\n \"The whole-stop bias multiplies the derived time by 2^-fasterByStops, after the derivation\",\n \"Snapping to the shutter grid never lengthens the time, so the blur stays within the budget unless held at the body's fastest shutter\",\n \"The 1/focal-length reciprocal rule is a rule of thumb and is not used\",\n \"Roll, translation, off-axis field dependence and subject motion are not modelled\"\n ]);\n}\n"]}
1
+ {"version":3,"file":"auto-iso-slowest-shutter.js","sourceRoot":"","sources":["../../src/exposure/auto-iso-slowest-shutter.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * #504: the automatic slowest preferred shutter for Auto ISO (\"Auto\" minimum shutter speed), derived\n * from the engine's camera-shake model (`estimateCameraShakeBlur()`), not from the 1/focal-length rule\n * of thumb.\n *\n * The camera turns at a constant angular velocity ω (yaw, pitch) during the exposure, reduced by the\n * stabilization factor r = 2^−stops. A still point on the optical axis then moves\n * b(t) = v·hypot(tan(r·ω_yaw·t), tan(r·ω_pitch·t)) on the sensor, where v is the projection distance (the\n * focal length, or the thin-lens image distance with a focus distance). The derived shutter is the\n * exposure time at which b(t) equals the caller's blur budget. b(t) grows strictly with t, so the\n * solution is unique; the whole-stop bias (as in makers' Auto minimum-shutter settings: faster or\n * slower in 1 EV steps) is applied after the derivation, and the optional shutter grid last.\n *\n * This is an approximation: the hand's motion is a constant-velocity profile supplied by the caller,\n * and stabilization stops scale the motion by 2^−stops (the educational model of\n * `estimateCameraShakeBlur()`, not a CIPA DC-011 rating).\n * @see docs/USAGE.md (\"Automatic slowest shutter (#504)\") for the equations, the cited generic\n * hand-shake default and the support limits.\n */\n\nimport { approximationResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { rejectUnknownFields, requireInputKeys, requireInputObject } from \"../core/record-validation.js\";\nimport { InvalidScientificInputError, InvalidScientificResultError } from \"../core/validation.js\";\nimport { portableHypot } from \"../core/portable-math.js\";\nimport type { ResolvedGenericEquipmentExposureCapabilities } from \"../equipment/exposure-capabilities.js\";\nimport { estimateCameraShakeBlur, type CameraShakeEstimate } from \"../stabilization/camera-shake.js\";\n\nexport const AUTO_ISO_SLOWEST_SHUTTER_MODEL_VERSION = \"1.0.0\" as const;\n\n/**\n * A generic handheld angular velocity, labelled as a default and not a measurement of any camera or\n * photographer: 4.39 °/s (≈ 0.0766 rad/s), the linear camera-motion rate Xiao, Farrell, Catrysse and\n * Wandell use for \"an average user\" holding \"a typical 100g mobile handset with two hands\", from their\n * earlier gyroscope measurements (F. Xiao, J. Farrell, P. Catrysse, B. Wandell, \"Mobile imaging: the\n * big challenge of the small pixel\", Proc. SPIE 7250, Digital Photography V, 2009, §2; author copy:\n * https://web.stanford.edu/~wandell/data/papers/2009-CameraShake-Xiao.pdf; they cite F. Xiao et al.,\n * \"Camera-motion and mobile imaging\", Proc. SPIE 6502, 2007). The value is one rate magnitude; it is\n * placed on the yaw axis (for small angles the blur length does not depend on the direction). A\n * heavier camera, another grip or another photographer moves differently: pass your own profile.\n */\nexport const GENERIC_HANDHELD_ANGULAR_VELOCITY_RAD_PER_SEC: Readonly<{ yaw: number; pitch: number }> =\n /* @__PURE__ */ Object.freeze({ yaw: (4.39 * Math.PI) / 180, pitch: 0 });\n\n/** The largest whole-stop bias accepted, either way (makers offer up to 2 stops faster or slower). */\nexport const AUTO_ISO_SLOWEST_SHUTTER_MAXIMUM_BIAS_STOPS = 2 as const;\n\n/** The blur allowed during the exposure, on the sensor. */\nexport type AutoIsoSlowestShutterBlurBudget =\n | {\n /** The blur trace length in pixels of the given pitch. */\n kind: \"pixels\";\n pixels: number;\n pixelPitchMicrometers: number;\n }\n | {\n /** The blur trace length as a fraction of the image diagonal (width × height in millimetres). */\n kind: \"image-diagonal-fraction\";\n fraction: number;\n imageWidthMm: number;\n imageHeightMm: number;\n };\n\nexport interface EstimateAutoIsoSlowestShutterInput {\n focalLengthMm: number;\n /** Optional focus distance in metres: the thin-lens image distance becomes the projection distance, as in `estimateCameraShakeBlur()`. */\n focusDistanceM?: number;\n /**\n * The hand's constant angular velocity in radians per second, the explicit shake profile (signs as in\n * `estimateCameraShakeBlur()`; only the magnitude matters here). At least one axis must be non-zero.\n * `GENERIC_HANDHELD_ANGULAR_VELOCITY_RAD_PER_SEC` is a cited generic default, not a measurement of you.\n */\n angularVelocityRadPerSec: { yaw: number; pitch: number };\n /** Stabilization in equivalent stops (≥ 0); the motion is scaled by 2^−stops. Default 0. */\n stabilizationStopsEquivalent?: number;\n blurBudget: AutoIsoSlowestShutterBlurBudget;\n /**\n * The bias in whole stops, applied after the derivation: +1 is one stop faster (half the time), −1 one\n * stop slower (twice the time). An integer from −2 to +2. Default 0.\n */\n fasterByStops?: number;\n /** Optional: the body's resolved shutter capability. The biased shutter then snaps to it, never longer than the biased value, and is held at its ends. */\n shutterCapability?: ResolvedGenericEquipmentExposureCapabilities[\"shutter\"];\n}\n\nexport interface AutoIsoSlowestShutterEstimate {\n /** The exposure time whose shake blur equals the budget, before the bias and the grid. */\n derivedShutterSeconds: number;\n fasterByStops: number;\n /** derivedShutterSeconds · 2^−fasterByStops. */\n biasedShutterSeconds: number;\n /** The slowest preferred shutter: the biased value, snapped to `shutterCapability` when given. */\n shutterSeconds: number;\n shutterResolution: {\n kind: \"unconstrained\" | \"continuous\" | \"discrete\";\n selectionPolicy: \"not-longer-than-target\";\n quantized: boolean;\n clamped: false | \"minimum\" | \"maximum\";\n };\n blurBudgetMm: number;\n /** `estimateCameraShakeBlur()`'s stabilized blur at the derived shutter (equal to the budget within rounding). */\n blurAtDerivedShutterMm: number;\n /** `estimateCameraShakeBlur()`'s stabilized blur at `shutterSeconds`. */\n blurAtShutterMm: number;\n /** The same blur lengths in pixels, for a pixel budget. */\n blurBudgetPixels?: number;\n blurAtShutterPixels?: number;\n projectionDistanceMm: number;\n residualMotionFactor: number;\n angularVelocityRadPerSec: { yaw: number; pitch: number };\n stabilizationStopsEquivalent: number;\n}\n\nfunction requirePositive(value: unknown, path: string): number {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value <= 0) {\n throw new InvalidScientificInputError(path + \" must be finite and greater than zero.\");\n }\n return value;\n}\n\nfunction parseBudget(value: unknown): { budgetMm: number; budgetPixels?: number; pixelPitchMicrometers?: number } {\n requireInputObject(value, \"blurBudget\");\n const record = value as Record<string, unknown>;\n if (record.kind === \"pixels\") {\n rejectUnknownFields(record, [\"kind\", \"pixels\", \"pixelPitchMicrometers\"], \"blurBudget\");\n const pixels = requirePositive(record.pixels, \"blurBudget.pixels\");\n const pitch = requirePositive(record.pixelPitchMicrometers, \"blurBudget.pixelPitchMicrometers\");\n return { budgetMm: (pixels * pitch) / 1000, budgetPixels: pixels, pixelPitchMicrometers: pitch };\n }\n if (record.kind === \"image-diagonal-fraction\") {\n rejectUnknownFields(record, [\"kind\", \"fraction\", \"imageWidthMm\", \"imageHeightMm\"], \"blurBudget\");\n const fraction = requirePositive(record.fraction, \"blurBudget.fraction\");\n if (fraction >= 1) throw new InvalidScientificInputError(\"blurBudget.fraction must be less than 1.\");\n const width = requirePositive(record.imageWidthMm, \"blurBudget.imageWidthMm\");\n const height = requirePositive(record.imageHeightMm, \"blurBudget.imageHeightMm\");\n return { budgetMm: fraction * portableHypot(width, height) };\n }\n throw new InvalidConfigurationError('blurBudget.kind must be \"pixels\" or \"image-diagonal-fraction\".');\n}\n\n/**\n * Solves v·hypot(tan(a·t), tan(b·t)) = budget for t, with a, b ≥ 0 the stabilized angular speeds. One\n * axis: t = atan(budget/v)/a exactly. Two axes: bisection on the strictly increasing blur over\n * (0, π/(2·max(a, b))), a fixed number of halvings (deterministic, to the last bit of the bracket).\n */\nfunction solveShutterForBudget(projectionMm: number, a: number, b: number, budgetMm: number): number {\n const ratio = budgetMm / projectionMm;\n if (a === 0 || b === 0) return Math.atan(ratio) / Math.max(a, b);\n let low = 0, high = Math.PI / 2 / Math.max(a, b);\n for (let i = 0; i < 200 && high - low > 0; i++) {\n const mid = low + (high - low) / 2;\n if (mid <= low || mid >= high) break;\n if (portableHypot(Math.tan(a * mid), Math.tan(b * mid)) < ratio) low = mid;\n else high = mid;\n }\n return low + (high - low) / 2;\n}\n\nfunction snapNotLonger(\n target: number,\n shutter: ResolvedGenericEquipmentExposureCapabilities[\"shutter\"]\n): { shutterSeconds: number; kind: \"continuous\" | \"discrete\"; quantized: boolean; clamped: false | \"minimum\" | \"maximum\" } {\n const grid = shutter.settingGrid;\n if (grid.kind === \"continuous-within-range\") {\n if (target < shutter.minimumSeconds) return { shutterSeconds: shutter.minimumSeconds, kind: \"continuous\", quantized: false, clamped: \"minimum\" };\n if (target > shutter.maximumSeconds) return { shutterSeconds: shutter.maximumSeconds, kind: \"continuous\", quantized: false, clamped: \"maximum\" };\n return { shutterSeconds: target, kind: \"continuous\", quantized: false, clamped: false };\n }\n const values = grid.values;\n const tolerance = (x: number): number => Number.EPSILON * 32 * Math.max(Math.abs(x), 1);\n const exact = values.find((v) => Math.abs(v - target) <= tolerance(Math.max(v, target)));\n if (exact !== undefined) return { shutterSeconds: exact, kind: \"discrete\", quantized: false, clamped: false };\n if (target < values[0]!) return { shutterSeconds: values[0]!, kind: \"discrete\", quantized: true, clamped: \"minimum\" };\n const last = values[values.length - 1]!;\n if (target > last) return { shutterSeconds: last, kind: \"discrete\", quantized: true, clamped: \"maximum\" };\n let selected = values[0]!;\n for (const v of values) if (v <= target) selected = v;\n return { shutterSeconds: selected, kind: \"discrete\", quantized: true, clamped: false };\n}\n\n/**\n * Estimates the slowest preferred shutter for Auto ISO from the lens, the sensor and the hand's\n * shake: the exposure time at which `estimateCameraShakeBlur()`'s stabilized blur equals the blur\n * budget, then shifted by a whole-stop bias and optionally snapped to the body's shutter grid (never\n * longer than the biased value). Longer focal lengths give faster shutters; each stabilization stop\n * doubles the time; each bias stop halves (+1) or doubles (−1) it.\n *\n * An approximation: constant angular velocity (a caller profile, or the labelled generic default\n * `GENERIC_HANDHELD_ANGULAR_VELOCITY_RAD_PER_SEC`), stabilization as 2^−stops, blur on the optical axis\n * only. The 1/focal-length \"reciprocal rule\" is a rule of thumb and is not used.\n * @param input - EstimateAutoIsoSlowestShutterInput.\n * @returns The derived, biased and snapped shutter with the blur at each, as an approximation result.\n */\nexport function estimateAutoIsoSlowestShutter(\n input: EstimateAutoIsoSlowestShutterInput\n): CalculationResult<AutoIsoSlowestShutterEstimate> {\n requireInputKeys(input, \"estimateAutoIsoSlowestShutter\", [\"focalLengthMm\", \"focusDistanceM\", \"angularVelocityRadPerSec\", \"stabilizationStopsEquivalent\",\n \"blurBudget\", \"fasterByStops\", \"shutterCapability\"]);\n const focalLengthMm = requirePositive(input.focalLengthMm, \"focalLengthMm\");\n requireInputObject(input.angularVelocityRadPerSec, \"angularVelocityRadPerSec\");\n rejectUnknownFields(input.angularVelocityRadPerSec, [\"yaw\", \"pitch\"], \"angularVelocityRadPerSec\");\n const { yaw, pitch } = input.angularVelocityRadPerSec;\n for (const [name, v] of [[\"yaw\", yaw], [\"pitch\", pitch]] as const) {\n if (typeof v !== \"number\" || !Number.isFinite(v)) throw new InvalidScientificInputError(\"angularVelocityRadPerSec.\" + name + \" must be finite.\");\n }\n if (yaw === 0 && pitch === 0) {\n throw new InvalidScientificInputError(\"angularVelocityRadPerSec must be non-zero on at least one axis: without shake no shutter limit follows.\");\n }\n const stops = input.stabilizationStopsEquivalent ?? 0;\n if (typeof stops !== \"number\" || !Number.isFinite(stops) || stops < 0) {\n throw new InvalidScientificInputError(\"stabilizationStopsEquivalent must be finite and greater than or equal to zero.\");\n }\n const bias = input.fasterByStops ?? 0;\n if (typeof bias !== \"number\" || !Number.isInteger(bias) || Math.abs(bias) > AUTO_ISO_SLOWEST_SHUTTER_MAXIMUM_BIAS_STOPS) {\n throw new InvalidScientificInputError(\"fasterByStops must be a whole number of stops from -2 to +2.\");\n }\n const budget = parseBudget(input.blurBudget);\n if (input.focusDistanceM !== undefined) requirePositive(input.focusDistanceM, \"focusDistanceM\");\n if (input.shutterCapability !== undefined) {\n requireInputObject(input.shutterCapability, \"shutterCapability\");\n const s = input.shutterCapability;\n requirePositive(s.minimumSeconds, \"shutterCapability.minimumSeconds\");\n requirePositive(s.maximumSeconds, \"shutterCapability.maximumSeconds\");\n if (s.minimumSeconds > s.maximumSeconds) throw new InvalidScientificInputError(\"shutterCapability.minimumSeconds must not exceed shutterCapability.maximumSeconds.\");\n requireInputObject(s.settingGrid, \"shutterCapability.settingGrid\");\n if (s.settingGrid.kind === \"discrete-values\") {\n const values = s.settingGrid.values;\n if (!Array.isArray(values) || values.length === 0 || values.some((v, i) => typeof v !== \"number\" || !Number.isFinite(v) || v <= 0 || (i > 0 && v <= values[i - 1]!))) {\n throw new InvalidScientificInputError(\"shutterCapability.settingGrid.values must be strictly increasing positive finite seconds.\");\n }\n } else if (s.settingGrid.kind !== \"continuous-within-range\") {\n throw new InvalidConfigurationError(\"shutterCapability.settingGrid.kind is invalid.\");\n }\n }\n\n // The projection distance and the residual factor come from the shake model itself (a probe at a tiny time).\n const shake = (shutterSeconds: number): CameraShakeEstimate => estimateCameraShakeBlur({ focalLengthMm, shutterSeconds, angularVelocityRadPerSec: { yaw, pitch },\n stabilizationStopsEquivalent: stops, ...(input.focusDistanceM === undefined ? {} : { focusDistanceM: input.focusDistanceM }),\n ...(budget.pixelPitchMicrometers === undefined ? {} : { pixelPitchMicrometers: budget.pixelPitchMicrometers }) }).value;\n const probe = shake(Number.MIN_VALUE);\n const r = probe.residualMotionFactor, v = probe.projectionDistanceMm;\n const derived = solveShutterForBudget(v, Math.abs(yaw) * r, Math.abs(pitch) * r, budget.budgetMm);\n const biased = derived * 2 ** -bias;\n if (!Number.isFinite(derived) || derived <= 0 || !Number.isFinite(biased) || biased <= 0) {\n throw new InvalidScientificResultError(\"The derived slowest shutter is not a positive finite time.\");\n }\n const snapped = input.shutterCapability === undefined ? undefined : snapNotLonger(biased, input.shutterCapability);\n const shutterSeconds = snapped?.shutterSeconds ?? biased;\n // estimateCameraShakeBlur rejects a quarter turn or more on an axis of the *unstabilized* motion, which\n // a long biased or clamped time with stabilization can reach; the blur there is not reported.\n const quarterTurnSafe = (t: number): boolean => Math.max(Math.abs(yaw), Math.abs(pitch)) * t < Math.PI / 2;\n if (!quarterTurnSafe(derived) || !quarterTurnSafe(shutterSeconds)) {\n throw new InvalidScientificInputError(\n \"The shake over the slowest shutter would reach a quarter turn (|angular velocity × shutter| ≥ π/2 rad) before stabilization; the constant-velocity model does not apply.\");\n }\n const atDerived = shake(derived).stabilized, atShutter = shake(shutterSeconds).stabilized;\n\n return approximationResult<AutoIsoSlowestShutterEstimate>({\n derivedShutterSeconds: derived,\n fasterByStops: bias,\n biasedShutterSeconds: biased,\n shutterSeconds,\n shutterResolution: snapped === undefined\n ? { kind: \"unconstrained\", selectionPolicy: \"not-longer-than-target\", quantized: false, clamped: false }\n : { kind: snapped.kind, selectionPolicy: \"not-longer-than-target\", quantized: snapped.quantized, clamped: snapped.clamped },\n blurBudgetMm: budget.budgetMm,\n blurAtDerivedShutterMm: atDerived.distanceMm,\n blurAtShutterMm: atShutter.distanceMm,\n ...(budget.budgetPixels === undefined ? {} : { blurBudgetPixels: budget.budgetPixels, blurAtShutterPixels: atShutter.distancePixels! }),\n projectionDistanceMm: v,\n residualMotionFactor: r,\n angularVelocityRadPerSec: { yaw, pitch },\n stabilizationStopsEquivalent: stops\n }, \"camera-shake-blur-budget-slowest-shutter\", AUTO_ISO_SLOWEST_SHUTTER_MODEL_VERSION, [\n \"The slowest shutter is the exposure time at which estimateCameraShakeBlur()'s stabilized on-axis blur equals the blur budget\",\n \"The hand turns the camera at a constant angular velocity during the exposure (a caller-supplied profile)\",\n \"Stabilization attenuates the angular motion by 2^-stops (educational model, not a CIPA DC-011 rating)\",\n \"The whole-stop bias multiplies the derived time by 2^-fasterByStops, after the derivation\",\n \"Snapping to the shutter grid never lengthens the time, so the blur stays within the budget unless held at the body's fastest shutter\",\n \"The 1/focal-length reciprocal rule is a rule of thumb and is not used\",\n \"Roll, translation, off-axis field dependence and subject motion are not modelled\"\n ]);\n}\n"]}
@@ -18,7 +18,7 @@ import { InvalidScientificInputError } from "../core/validation.js";
18
18
  import { rejectUnknownFields, requireInputKeys, requireInputObject } from "../core/record-validation.js";
19
19
  import { parseManualFlashProfile } from "./flash.js";
20
20
  import { meterRelativeExposure, parseExposureMeteringProfile } from "./metering.js";
21
- export const TTL_FLASH_MODEL = Object.freeze({ id: "preflash-linear-ttl", version: "1.0.0" });
21
+ export const TTL_FLASH_MODEL = /* @__PURE__ */ Object.freeze({ id: "preflash-linear-ttl", version: "1.0.0" });
22
22
  function requirePositive(value, name) {
23
23
  if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
24
24
  throw new InvalidScientificInputError(name + " must be a finite number greater than zero.");
@@ -1 +1 @@
1
- {"version":3,"file":"flash-ttl.js","sourceRoot":"","sources":["../../src/exposure/flash-ttl.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Flash guide numbers, and through-the-lens (TTL) flash exposure with a pre-flash that does not\n * depend on any manufacturer.\n *\n * How TTL works here:\n * 1. The pre-flash is measured with the existing relative exposure meter.\n * 2. The solved power fraction is applied to a ManualFlashProfile.\n * 3. That pulse is placed by the existing front/rear-curtain sync (resolveManualFlashSync) or by\n * high-speed sync (resolveHighSpeedFlashSync).\n * Flash metering stays separate from ambient metering. The ambient meter result is an input and is\n * not changed. No vendor's TTL algorithm, zone weighting table or flash protocol is copied.\n * @see docs/SCENE_SENSOR_TEMPORAL_EXPOSURE.md#flash-guide-number-ttl-and-high-speed-sync\n */\n\nimport { approximationResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport { rejectUnknownFields, requireInputKeys, requireInputObject } from \"../core/record-validation.js\";\nimport { parseManualFlashProfile, type ManualFlashProfile } from \"./flash.js\";\nimport {\n meterRelativeExposure,\n parseExposureMeteringProfile,\n type ExposureMeteringProfile,\n type ExposureMeteringSampleSet\n} from \"./metering.js\";\n\nexport const TTL_FLASH_MODEL = Object.freeze({ id: \"preflash-linear-ttl\", version: \"1.0.0\" } as const);\n\nfunction requirePositive(value: unknown, name: string): number {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value <= 0) {\n throw new InvalidScientificInputError(name + \" must be a finite number greater than zero.\");\n }\n return value;\n}\n\nfunction requireFraction(value: unknown, name: string): number {\n const parsed = requirePositive(value, name);\n if (parsed > 1) throw new InvalidScientificInputError(name + \" must lie in (0, 1].\");\n return parsed;\n}\n\nexport interface FlashGuideNumberInput {\n /** Guide number at full power for ISO 100, in metres (GN = distance · f-number). */\n fullPowerGuideNumberMetersIso100: number;\n /** Output as a fraction of full power, (0, 1]. */\n powerFraction: number;\n iso: number;\n}\n\n/**\n * Effective guide number GN = GN_full · √p · √(ISO/100).\n *\n * The guide number relation GN = d·N follows from the inverse-square law. Light on the subject from a\n * point-like flash falls as 1/d², and sensor exposure falls as 1/N². So exposure stays correct while\n * d·N is constant (camera equation H = 0.65·L·t/N² and reference exposure H = 10/S, CIPA DC-004-2020,\n * free). Exposure grows linearly with emitted energy (power fraction p), and the exposure needed falls\n * inversely with ISO speed. So GN ∝ √p·√ISO. The engine does not model how a guide number is measured;\n * the caller supplies it.\n * @see docs/SCENE_SENSOR_TEMPORAL_EXPOSURE.md for the full derivation.\n */\nexport function calculateFlashGuideNumber(input: FlashGuideNumberInput): number {\n requireInputKeys(input, \"calculateFlashGuideNumber\", [\"fullPowerGuideNumberMetersIso100\", \"powerFraction\", \"iso\"]);\n return requirePositive(input.fullPowerGuideNumberMetersIso100, \"fullPowerGuideNumberMetersIso100\") *\n Math.sqrt(requireFraction(input.powerFraction, \"powerFraction\")) *\n Math.sqrt(requirePositive(input.iso, \"iso\") / 100);\n}\n\nexport interface FlashPowerForDistanceInput {\n fullPowerGuideNumberMetersIso100: number;\n subjectDistanceM: number;\n apertureFNumber: number;\n iso: number;\n /** Lowest available power fraction, (0, 1]. */\n minimumPowerFraction: number;\n}\n\nexport interface FlashPowerForDistance {\n /** p = (d·N / (GN_full·√(ISO/100)))², before clamping. */\n requiredPowerFraction: number;\n powerFraction: number;\n status: \"within-range\" | \"clamped-at-minimum-power\" | \"clamped-at-full-power\";\n /** log2(delivered / required) flash exposure; positive is overexposure. */\n flashExposureErrorStops: number;\n /** GN_full·√(ISO/100)/N: the farthest correctly exposed distance at full power. */\n maximumDistanceM: number;\n}\n\n/** Manual guide-number exposure: the power fraction that exposes a subject at distance d correctly. */\nexport function calculateFlashPowerForDistance(input: FlashPowerForDistanceInput): FlashPowerForDistance {\n requireInputKeys(input, \"calculateFlashPowerForDistance\", [\"fullPowerGuideNumberMetersIso100\", \"subjectDistanceM\",\n \"apertureFNumber\", \"iso\", \"minimumPowerFraction\"]);\n const gnIso = calculateFlashGuideNumber({ fullPowerGuideNumberMetersIso100: input.fullPowerGuideNumberMetersIso100,\n powerFraction: 1, iso: input.iso });\n const distance = requirePositive(input.subjectDistanceM, \"subjectDistanceM\");\n const aperture = requirePositive(input.apertureFNumber, \"apertureFNumber\");\n const minimum = requireFraction(input.minimumPowerFraction, \"minimumPowerFraction\");\n const required = (distance * aperture / gnIso) ** 2;\n const power = Math.min(1, Math.max(minimum, required));\n return { requiredPowerFraction: required, powerFraction: power,\n status: required < minimum ? \"clamped-at-minimum-power\" : required > 1 ? \"clamped-at-full-power\" : \"within-range\",\n flashExposureErrorStops: Math.log2(power / required), maximumDistanceM: gnIso / aperture };\n}\n\nexport interface ResolveTtlFlashExposureInput {\n /** Spatially linear metering profile (multi-zone-uniform, center-weighted-radial or spot). */\n meteringProfile: ExposureMeteringProfile;\n /** Measurement-window samples with the flash off. */\n ambientSampleSet: ExposureMeteringSampleSet;\n /** The same zones and settings with the pre-flash fired. */\n preFlashSampleSet: ExposureMeteringSampleSet;\n /** Pre-flash output as a fraction of full power, (0, 1]. */\n preFlashPowerFraction: number;\n /**\n * Ratio of ambient light collected in the main exposure to that in the measurement window, at the\n * same aperture and ISO (for example t_main/t_measure). Must be ≥ 0. The flash pulse falls inside\n * both windows, so it is not scaled.\n */\n ambientScaleToMainExposure: number;\n /** Flash exposure compensation applied to the TTL target, in stops. */\n flashExposureCompensationStops: number;\n /** Lowest available main-flash power fraction, (0, 1]. */\n minimumPowerFraction: number;\n /**\n * Flash energy each line receives, relative to an ordinary pulse at the same power fraction. 1 for\n * front/rear-curtain sync; η·t_e/D for high-speed sync (see resolveHighSpeedFlashSync). Defaults to 1.\n */\n syncEnergyFactor?: number;\n /** Optional guide-number reporting. */\n guideNumber?: { fullPowerGuideNumberMetersIso100: number; iso: number; apertureFNumber: number };\n}\n\nexport interface TtlFlashExposure {\n modelId: typeof TTL_FLASH_MODEL.id;\n modelVersion: typeof TTL_FLASH_MODEL.version;\n status: \"resolved\" | \"clamped-at-minimum-power\" | \"clamped-at-full-power\" | \"no-flash-return\";\n /** Metered ambient signal in the main exposure: k·M(ambient). */\n ambientMainExposureMeteredSignal: number;\n /** Metered flash-only signal per unit full power: M(preflash − ambient)/p_pre. */\n flashMeteredSignalPerFullPower: number;\n /** T·2^FEC. */\n targetRelativeSignal: number;\n /** Unclamped solution (T' − k·A)/(F·syncEnergyFactor); null with no flash return. */\n requiredPowerFraction: number | null;\n /** Commanded main-flash power fraction (clamped); null with no flash return. */\n powerFraction: number | null;\n ambientAloneReachesTarget: boolean;\n predictedMeteredSignal: number;\n /** log2(predicted / target); 0 when the solution is within range; null when nothing is predicted. */\n predictedExposureErrorStops: number | null;\n effectiveGuideNumberMetersAtIso: number | null;\n flashRangeAtApertureM: number | null;\n preFlashInsideMainExposure: false;\n ambientMeteringModified: false;\n}\n\nconst LINEAR_POLICIES = new Set([\"multi-zone-uniform\", \"center-weighted-radial\", \"spot\"]);\n\nfunction sameZones(a: ExposureMeteringSampleSet, b: ExposureMeteringSampleSet): void {\n if (a.sceneStateId !== b.sceneStateId || a.samples.length !== b.samples.length ||\n JSON.stringify(a.captureGeometry) !== JSON.stringify(b.captureGeometry)) {\n throw new InvalidScientificInputError(\"Ambient and pre-flash measurements must share scene state, geometry and zones.\");\n }\n a.samples.forEach((sample, index) => {\n const other = b.samples[index]!;\n requireInputObject(sample, \"ambientSampleSet.samples[\" + index + \"]\");\n requireInputObject(other, \"preFlashSampleSet.samples[\" + index + \"]\");\n requireInputObject(sample.positionOrientedCaptureUv, \"ambientSampleSet.samples[\" + index + \"].positionOrientedCaptureUv\");\n requireInputObject(other.positionOrientedCaptureUv, \"preFlashSampleSet.samples[\" + index + \"].positionOrientedCaptureUv\");\n if (sample.sampleId !== other.sampleId || sample.areaWeight !== other.areaWeight ||\n sample.positionOrientedCaptureUv.u !== other.positionOrientedCaptureUv.u ||\n sample.positionOrientedCaptureUv.v !== other.positionOrientedCaptureUv.v) {\n throw new InvalidScientificInputError(\"Ambient and pre-flash zones must match exactly and in order.\");\n }\n });\n}\n\n/**\n * Solves the main-flash power from a pre-flash measurement (manufacturer-independent TTL).\n *\n * The meter is linear for the policies allowed here: M(a + b) = M(a) + M(b).\n * - The pre-flash at power p_pre adds a flash-only signal f_i = pre_i − ambient_i in each zone.\n * - Flash signal is proportional to emitted energy, so at main power p it adds (p/p_pre)·M(f).\n * - With ambient scaled to the main exposure by k, the metered total is k·A + p·F, where\n * F = M(f)/p_pre.\n * - Setting this equal to the target T' = T·2^FEC gives p = (T' − k·A)/(F·s). Here s is the sync\n * energy factor (1 for ordinary sync, η·t_e/D for high-speed sync).\n * - p is clamped to [p_min, 1], the guide-number range.\n * Highlight policies are rejected: their weights depend on the signal, so the total is no longer\n * linear in p.\n */\nexport function resolveTtlFlashExposure(input: ResolveTtlFlashExposureInput): CalculationResult<TtlFlashExposure> {\n requireInputKeys(input, \"resolveTtlFlashExposure\", [\"meteringProfile\", \"ambientSampleSet\", \"preFlashSampleSet\", \"preFlashPowerFraction\", \"ambientScaleToMainExposure\", \"flashExposureCompensationStops\", \"minimumPowerFraction\", \"syncEnergyFactor\", \"guideNumber\"]);\n requireInputObject(input.ambientSampleSet, \"resolveTtlFlashExposure ambientSampleSet\");\n requireInputObject(input.preFlashSampleSet, \"resolveTtlFlashExposure preFlashSampleSet\");\n if (input.guideNumber !== undefined) {\n requireInputObject(input.guideNumber, \"resolveTtlFlashExposure guideNumber\");\n rejectUnknownFields(input.guideNumber, [\"fullPowerGuideNumberMetersIso100\", \"iso\", \"apertureFNumber\"], \"resolveTtlFlashExposure guideNumber\");\n }\n if (!Array.isArray(input.ambientSampleSet.samples) || !Array.isArray(input.preFlashSampleSet.samples)) {\n throw new InvalidConfigurationError(\"TTL sample sets must contain sample arrays.\");\n }\n const profile = parseExposureMeteringProfile(input.meteringProfile);\n if (!LINEAR_POLICIES.has(profile.policy.kind)) {\n throw new InvalidScientificInputError(\n \"TTL flash requires a spatially linear metering policy (multi-zone-uniform, center-weighted-radial or spot).\"\n );\n }\n sameZones(input.ambientSampleSet, input.preFlashSampleSet);\n const preFlash = requireFraction(input.preFlashPowerFraction, \"preFlashPowerFraction\");\n const minimum = requireFraction(input.minimumPowerFraction, \"minimumPowerFraction\");\n const k = input.ambientScaleToMainExposure;\n if (typeof k !== \"number\" || !Number.isFinite(k) || k < 0) {\n throw new InvalidScientificInputError(\"ambientScaleToMainExposure must be a finite number greater than or equal to zero.\");\n }\n const fec = input.flashExposureCompensationStops;\n if (typeof fec !== \"number\" || !Number.isFinite(fec)) {\n throw new InvalidScientificInputError(\"flashExposureCompensationStops must be finite.\");\n }\n const syncFactor = input.syncEnergyFactor === undefined ? 1 : requireFraction(input.syncEnergyFactor, \"syncEnergyFactor\");\n const flashOnly: ExposureMeteringSampleSet = { ...input.preFlashSampleSet,\n measurementId: input.preFlashSampleSet.measurementId + \":flash-only\",\n samples: input.preFlashSampleSet.samples.map((sample, index) => {\n const difference = sample.relativeLinearSignal - input.ambientSampleSet.samples[index]!.relativeLinearSignal;\n if (!(difference >= 0)) {\n throw new InvalidScientificInputError(\"Pre-flash signal must not be below the ambient signal in any zone.\");\n }\n return { ...sample, relativeLinearSignal: difference };\n }) };\n const ambient = meterRelativeExposure({ profile, sampleSet: input.ambientSampleSet }).value.meteredRelativeSignal;\n const flashPerFull = meterRelativeExposure({ profile, sampleSet: flashOnly }).value.meteredRelativeSignal / preFlash;\n const ambientMain = k * ambient;\n const target = profile.target.targetRelativeSignal * Math.pow(2, fec);\n const deliveredPerFull = flashPerFull * syncFactor;\n let status: TtlFlashExposure[\"status\"], required: number | null, power: number | null;\n if (deliveredPerFull === 0) {\n status = \"no-flash-return\"; required = null; power = null;\n } else {\n required = (target - ambientMain) / deliveredPerFull;\n power = Math.min(1, Math.max(minimum, required));\n status = required < minimum ? \"clamped-at-minimum-power\" : required > 1 ? \"clamped-at-full-power\" : \"resolved\";\n }\n const predicted = ambientMain + (power ?? 0) * deliveredPerFull;\n const gn = input.guideNumber;\n const effectiveGn = gn === undefined || power === null ? null : calculateFlashGuideNumber({\n fullPowerGuideNumberMetersIso100: gn.fullPowerGuideNumberMetersIso100, powerFraction: power, iso: gn.iso }) * Math.sqrt(syncFactor);\n return approximationResult({\n modelId: TTL_FLASH_MODEL.id,\n modelVersion: TTL_FLASH_MODEL.version,\n status,\n ambientMainExposureMeteredSignal: ambientMain,\n flashMeteredSignalPerFullPower: flashPerFull,\n targetRelativeSignal: target,\n requiredPowerFraction: required,\n powerFraction: power,\n ambientAloneReachesTarget: ambientMain >= target,\n predictedMeteredSignal: predicted,\n predictedExposureErrorStops: predicted > 0 ? Math.log2(predicted / target) : null,\n effectiveGuideNumberMetersAtIso: effectiveGn,\n flashRangeAtApertureM: effectiveGn === null || gn === undefined ? null : effectiveGn / requirePositive(gn.apertureFNumber, \"apertureFNumber\"),\n preFlashInsideMainExposure: false,\n ambientMeteringModified: false\n }, TTL_FLASH_MODEL.id, TTL_FLASH_MODEL.version, [\n \"Relative pre-exposure meter signals only; no calibrated flash energy, subject reflectance or distance is inferred.\",\n \"Flash signal is assumed proportional to emitted energy and fully inside the main exposure (pulse) or each line's slit (HSS factor).\",\n \"The pre-flash precedes the main exposure; subject motion and ambient change between pre-flash and exposure are not modeled.\",\n \"This is a generic linear pre-flash solution, not any manufacturer's TTL algorithm, zone weighting or flash protocol.\"\n ]);\n}\n\n/**\n * Returns a copy of a manual flash profile with its relative output scaled by a resolved TTL power\n * fraction, ready for resolveManualFlashSync (front/rear curtain). For high-speed sync, instead pass\n * the unscaled profile, and pass `ttl.powerFraction` as resolveHighSpeedFlashSync's powerFraction.\n * That way power is applied only once.\n */\nexport function createTtlScaledFlashProfile(flashProfile: ManualFlashProfile, ttl: TtlFlashExposure): ManualFlashProfile {\n const flash = parseManualFlashProfile(flashProfile);\n requireInputObject(ttl, \"createTtlScaledFlashProfile ttl\");\n if (ttl.powerFraction === null || typeof ttl.powerFraction !== \"number\") {\n throw new InvalidScientificInputError(\"TTL found no flash return; there is no power fraction to apply.\");\n }\n return { ...flash, outputControl: { ...flash.outputControl, scale: flash.outputControl.scale * ttl.powerFraction } };\n}\n"]}
1
+ {"version":3,"file":"flash-ttl.js","sourceRoot":"","sources":["../../src/exposure/flash-ttl.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Flash guide numbers, and through-the-lens (TTL) flash exposure with a pre-flash that does not\n * depend on any manufacturer.\n *\n * How TTL works here:\n * 1. The pre-flash is measured with the existing relative exposure meter.\n * 2. The solved power fraction is applied to a ManualFlashProfile.\n * 3. That pulse is placed by the existing front/rear-curtain sync (resolveManualFlashSync) or by\n * high-speed sync (resolveHighSpeedFlashSync).\n * Flash metering stays separate from ambient metering. The ambient meter result is an input and is\n * not changed. No vendor's TTL algorithm, zone weighting table or flash protocol is copied.\n * @see docs/SCENE_SENSOR_TEMPORAL_EXPOSURE.md#flash-guide-number-ttl-and-high-speed-sync\n */\n\nimport { approximationResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport { rejectUnknownFields, requireInputKeys, requireInputObject } from \"../core/record-validation.js\";\nimport { parseManualFlashProfile, type ManualFlashProfile } from \"./flash.js\";\nimport {\n meterRelativeExposure,\n parseExposureMeteringProfile,\n type ExposureMeteringProfile,\n type ExposureMeteringSampleSet\n} from \"./metering.js\";\n\nexport const TTL_FLASH_MODEL = /* @__PURE__ */ Object.freeze({ id: \"preflash-linear-ttl\", version: \"1.0.0\" } as const);\n\nfunction requirePositive(value: unknown, name: string): number {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value <= 0) {\n throw new InvalidScientificInputError(name + \" must be a finite number greater than zero.\");\n }\n return value;\n}\n\nfunction requireFraction(value: unknown, name: string): number {\n const parsed = requirePositive(value, name);\n if (parsed > 1) throw new InvalidScientificInputError(name + \" must lie in (0, 1].\");\n return parsed;\n}\n\nexport interface FlashGuideNumberInput {\n /** Guide number at full power for ISO 100, in metres (GN = distance · f-number). */\n fullPowerGuideNumberMetersIso100: number;\n /** Output as a fraction of full power, (0, 1]. */\n powerFraction: number;\n iso: number;\n}\n\n/**\n * Effective guide number GN = GN_full · √p · √(ISO/100).\n *\n * The guide number relation GN = d·N follows from the inverse-square law. Light on the subject from a\n * point-like flash falls as 1/d², and sensor exposure falls as 1/N². So exposure stays correct while\n * d·N is constant (camera equation H = 0.65·L·t/N² and reference exposure H = 10/S, CIPA DC-004-2020,\n * free). Exposure grows linearly with emitted energy (power fraction p), and the exposure needed falls\n * inversely with ISO speed. So GN ∝ √p·√ISO. The engine does not model how a guide number is measured;\n * the caller supplies it.\n * @see docs/SCENE_SENSOR_TEMPORAL_EXPOSURE.md for the full derivation.\n */\nexport function calculateFlashGuideNumber(input: FlashGuideNumberInput): number {\n requireInputKeys(input, \"calculateFlashGuideNumber\", [\"fullPowerGuideNumberMetersIso100\", \"powerFraction\", \"iso\"]);\n return requirePositive(input.fullPowerGuideNumberMetersIso100, \"fullPowerGuideNumberMetersIso100\") *\n Math.sqrt(requireFraction(input.powerFraction, \"powerFraction\")) *\n Math.sqrt(requirePositive(input.iso, \"iso\") / 100);\n}\n\nexport interface FlashPowerForDistanceInput {\n fullPowerGuideNumberMetersIso100: number;\n subjectDistanceM: number;\n apertureFNumber: number;\n iso: number;\n /** Lowest available power fraction, (0, 1]. */\n minimumPowerFraction: number;\n}\n\nexport interface FlashPowerForDistance {\n /** p = (d·N / (GN_full·√(ISO/100)))², before clamping. */\n requiredPowerFraction: number;\n powerFraction: number;\n status: \"within-range\" | \"clamped-at-minimum-power\" | \"clamped-at-full-power\";\n /** log2(delivered / required) flash exposure; positive is overexposure. */\n flashExposureErrorStops: number;\n /** GN_full·√(ISO/100)/N: the farthest correctly exposed distance at full power. */\n maximumDistanceM: number;\n}\n\n/** Manual guide-number exposure: the power fraction that exposes a subject at distance d correctly. */\nexport function calculateFlashPowerForDistance(input: FlashPowerForDistanceInput): FlashPowerForDistance {\n requireInputKeys(input, \"calculateFlashPowerForDistance\", [\"fullPowerGuideNumberMetersIso100\", \"subjectDistanceM\",\n \"apertureFNumber\", \"iso\", \"minimumPowerFraction\"]);\n const gnIso = calculateFlashGuideNumber({ fullPowerGuideNumberMetersIso100: input.fullPowerGuideNumberMetersIso100,\n powerFraction: 1, iso: input.iso });\n const distance = requirePositive(input.subjectDistanceM, \"subjectDistanceM\");\n const aperture = requirePositive(input.apertureFNumber, \"apertureFNumber\");\n const minimum = requireFraction(input.minimumPowerFraction, \"minimumPowerFraction\");\n const required = (distance * aperture / gnIso) ** 2;\n const power = Math.min(1, Math.max(minimum, required));\n return { requiredPowerFraction: required, powerFraction: power,\n status: required < minimum ? \"clamped-at-minimum-power\" : required > 1 ? \"clamped-at-full-power\" : \"within-range\",\n flashExposureErrorStops: Math.log2(power / required), maximumDistanceM: gnIso / aperture };\n}\n\nexport interface ResolveTtlFlashExposureInput {\n /** Spatially linear metering profile (multi-zone-uniform, center-weighted-radial or spot). */\n meteringProfile: ExposureMeteringProfile;\n /** Measurement-window samples with the flash off. */\n ambientSampleSet: ExposureMeteringSampleSet;\n /** The same zones and settings with the pre-flash fired. */\n preFlashSampleSet: ExposureMeteringSampleSet;\n /** Pre-flash output as a fraction of full power, (0, 1]. */\n preFlashPowerFraction: number;\n /**\n * Ratio of ambient light collected in the main exposure to that in the measurement window, at the\n * same aperture and ISO (for example t_main/t_measure). Must be ≥ 0. The flash pulse falls inside\n * both windows, so it is not scaled.\n */\n ambientScaleToMainExposure: number;\n /** Flash exposure compensation applied to the TTL target, in stops. */\n flashExposureCompensationStops: number;\n /** Lowest available main-flash power fraction, (0, 1]. */\n minimumPowerFraction: number;\n /**\n * Flash energy each line receives, relative to an ordinary pulse at the same power fraction. 1 for\n * front/rear-curtain sync; η·t_e/D for high-speed sync (see resolveHighSpeedFlashSync). Defaults to 1.\n */\n syncEnergyFactor?: number;\n /** Optional guide-number reporting. */\n guideNumber?: { fullPowerGuideNumberMetersIso100: number; iso: number; apertureFNumber: number };\n}\n\nexport interface TtlFlashExposure {\n modelId: typeof TTL_FLASH_MODEL.id;\n modelVersion: typeof TTL_FLASH_MODEL.version;\n status: \"resolved\" | \"clamped-at-minimum-power\" | \"clamped-at-full-power\" | \"no-flash-return\";\n /** Metered ambient signal in the main exposure: k·M(ambient). */\n ambientMainExposureMeteredSignal: number;\n /** Metered flash-only signal per unit full power: M(preflash − ambient)/p_pre. */\n flashMeteredSignalPerFullPower: number;\n /** T·2^FEC. */\n targetRelativeSignal: number;\n /** Unclamped solution (T' − k·A)/(F·syncEnergyFactor); null with no flash return. */\n requiredPowerFraction: number | null;\n /** Commanded main-flash power fraction (clamped); null with no flash return. */\n powerFraction: number | null;\n ambientAloneReachesTarget: boolean;\n predictedMeteredSignal: number;\n /** log2(predicted / target); 0 when the solution is within range; null when nothing is predicted. */\n predictedExposureErrorStops: number | null;\n effectiveGuideNumberMetersAtIso: number | null;\n flashRangeAtApertureM: number | null;\n preFlashInsideMainExposure: false;\n ambientMeteringModified: false;\n}\n\nconst LINEAR_POLICIES = new Set([\"multi-zone-uniform\", \"center-weighted-radial\", \"spot\"]);\n\nfunction sameZones(a: ExposureMeteringSampleSet, b: ExposureMeteringSampleSet): void {\n if (a.sceneStateId !== b.sceneStateId || a.samples.length !== b.samples.length ||\n JSON.stringify(a.captureGeometry) !== JSON.stringify(b.captureGeometry)) {\n throw new InvalidScientificInputError(\"Ambient and pre-flash measurements must share scene state, geometry and zones.\");\n }\n a.samples.forEach((sample, index) => {\n const other = b.samples[index]!;\n requireInputObject(sample, \"ambientSampleSet.samples[\" + index + \"]\");\n requireInputObject(other, \"preFlashSampleSet.samples[\" + index + \"]\");\n requireInputObject(sample.positionOrientedCaptureUv, \"ambientSampleSet.samples[\" + index + \"].positionOrientedCaptureUv\");\n requireInputObject(other.positionOrientedCaptureUv, \"preFlashSampleSet.samples[\" + index + \"].positionOrientedCaptureUv\");\n if (sample.sampleId !== other.sampleId || sample.areaWeight !== other.areaWeight ||\n sample.positionOrientedCaptureUv.u !== other.positionOrientedCaptureUv.u ||\n sample.positionOrientedCaptureUv.v !== other.positionOrientedCaptureUv.v) {\n throw new InvalidScientificInputError(\"Ambient and pre-flash zones must match exactly and in order.\");\n }\n });\n}\n\n/**\n * Solves the main-flash power from a pre-flash measurement (manufacturer-independent TTL).\n *\n * The meter is linear for the policies allowed here: M(a + b) = M(a) + M(b).\n * - The pre-flash at power p_pre adds a flash-only signal f_i = pre_i − ambient_i in each zone.\n * - Flash signal is proportional to emitted energy, so at main power p it adds (p/p_pre)·M(f).\n * - With ambient scaled to the main exposure by k, the metered total is k·A + p·F, where\n * F = M(f)/p_pre.\n * - Setting this equal to the target T' = T·2^FEC gives p = (T' − k·A)/(F·s). Here s is the sync\n * energy factor (1 for ordinary sync, η·t_e/D for high-speed sync).\n * - p is clamped to [p_min, 1], the guide-number range.\n * Highlight policies are rejected: their weights depend on the signal, so the total is no longer\n * linear in p.\n */\nexport function resolveTtlFlashExposure(input: ResolveTtlFlashExposureInput): CalculationResult<TtlFlashExposure> {\n requireInputKeys(input, \"resolveTtlFlashExposure\", [\"meteringProfile\", \"ambientSampleSet\", \"preFlashSampleSet\", \"preFlashPowerFraction\", \"ambientScaleToMainExposure\", \"flashExposureCompensationStops\", \"minimumPowerFraction\", \"syncEnergyFactor\", \"guideNumber\"]);\n requireInputObject(input.ambientSampleSet, \"resolveTtlFlashExposure ambientSampleSet\");\n requireInputObject(input.preFlashSampleSet, \"resolveTtlFlashExposure preFlashSampleSet\");\n if (input.guideNumber !== undefined) {\n requireInputObject(input.guideNumber, \"resolveTtlFlashExposure guideNumber\");\n rejectUnknownFields(input.guideNumber, [\"fullPowerGuideNumberMetersIso100\", \"iso\", \"apertureFNumber\"], \"resolveTtlFlashExposure guideNumber\");\n }\n if (!Array.isArray(input.ambientSampleSet.samples) || !Array.isArray(input.preFlashSampleSet.samples)) {\n throw new InvalidConfigurationError(\"TTL sample sets must contain sample arrays.\");\n }\n const profile = parseExposureMeteringProfile(input.meteringProfile);\n if (!LINEAR_POLICIES.has(profile.policy.kind)) {\n throw new InvalidScientificInputError(\n \"TTL flash requires a spatially linear metering policy (multi-zone-uniform, center-weighted-radial or spot).\"\n );\n }\n sameZones(input.ambientSampleSet, input.preFlashSampleSet);\n const preFlash = requireFraction(input.preFlashPowerFraction, \"preFlashPowerFraction\");\n const minimum = requireFraction(input.minimumPowerFraction, \"minimumPowerFraction\");\n const k = input.ambientScaleToMainExposure;\n if (typeof k !== \"number\" || !Number.isFinite(k) || k < 0) {\n throw new InvalidScientificInputError(\"ambientScaleToMainExposure must be a finite number greater than or equal to zero.\");\n }\n const fec = input.flashExposureCompensationStops;\n if (typeof fec !== \"number\" || !Number.isFinite(fec)) {\n throw new InvalidScientificInputError(\"flashExposureCompensationStops must be finite.\");\n }\n const syncFactor = input.syncEnergyFactor === undefined ? 1 : requireFraction(input.syncEnergyFactor, \"syncEnergyFactor\");\n const flashOnly: ExposureMeteringSampleSet = { ...input.preFlashSampleSet,\n measurementId: input.preFlashSampleSet.measurementId + \":flash-only\",\n samples: input.preFlashSampleSet.samples.map((sample, index) => {\n const difference = sample.relativeLinearSignal - input.ambientSampleSet.samples[index]!.relativeLinearSignal;\n if (!(difference >= 0)) {\n throw new InvalidScientificInputError(\"Pre-flash signal must not be below the ambient signal in any zone.\");\n }\n return { ...sample, relativeLinearSignal: difference };\n }) };\n const ambient = meterRelativeExposure({ profile, sampleSet: input.ambientSampleSet }).value.meteredRelativeSignal;\n const flashPerFull = meterRelativeExposure({ profile, sampleSet: flashOnly }).value.meteredRelativeSignal / preFlash;\n const ambientMain = k * ambient;\n const target = profile.target.targetRelativeSignal * Math.pow(2, fec);\n const deliveredPerFull = flashPerFull * syncFactor;\n let status: TtlFlashExposure[\"status\"], required: number | null, power: number | null;\n if (deliveredPerFull === 0) {\n status = \"no-flash-return\"; required = null; power = null;\n } else {\n required = (target - ambientMain) / deliveredPerFull;\n power = Math.min(1, Math.max(minimum, required));\n status = required < minimum ? \"clamped-at-minimum-power\" : required > 1 ? \"clamped-at-full-power\" : \"resolved\";\n }\n const predicted = ambientMain + (power ?? 0) * deliveredPerFull;\n const gn = input.guideNumber;\n const effectiveGn = gn === undefined || power === null ? null : calculateFlashGuideNumber({\n fullPowerGuideNumberMetersIso100: gn.fullPowerGuideNumberMetersIso100, powerFraction: power, iso: gn.iso }) * Math.sqrt(syncFactor);\n return approximationResult({\n modelId: TTL_FLASH_MODEL.id,\n modelVersion: TTL_FLASH_MODEL.version,\n status,\n ambientMainExposureMeteredSignal: ambientMain,\n flashMeteredSignalPerFullPower: flashPerFull,\n targetRelativeSignal: target,\n requiredPowerFraction: required,\n powerFraction: power,\n ambientAloneReachesTarget: ambientMain >= target,\n predictedMeteredSignal: predicted,\n predictedExposureErrorStops: predicted > 0 ? Math.log2(predicted / target) : null,\n effectiveGuideNumberMetersAtIso: effectiveGn,\n flashRangeAtApertureM: effectiveGn === null || gn === undefined ? null : effectiveGn / requirePositive(gn.apertureFNumber, \"apertureFNumber\"),\n preFlashInsideMainExposure: false,\n ambientMeteringModified: false\n }, TTL_FLASH_MODEL.id, TTL_FLASH_MODEL.version, [\n \"Relative pre-exposure meter signals only; no calibrated flash energy, subject reflectance or distance is inferred.\",\n \"Flash signal is assumed proportional to emitted energy and fully inside the main exposure (pulse) or each line's slit (HSS factor).\",\n \"The pre-flash precedes the main exposure; subject motion and ambient change between pre-flash and exposure are not modeled.\",\n \"This is a generic linear pre-flash solution, not any manufacturer's TTL algorithm, zone weighting or flash protocol.\"\n ]);\n}\n\n/**\n * Returns a copy of a manual flash profile with its relative output scaled by a resolved TTL power\n * fraction, ready for resolveManualFlashSync (front/rear curtain). For high-speed sync, instead pass\n * the unscaled profile, and pass `ttl.powerFraction` as resolveHighSpeedFlashSync's powerFraction.\n * That way power is applied only once.\n */\nexport function createTtlScaledFlashProfile(flashProfile: ManualFlashProfile, ttl: TtlFlashExposure): ManualFlashProfile {\n const flash = parseManualFlashProfile(flashProfile);\n requireInputObject(ttl, \"createTtlScaledFlashProfile ttl\");\n if (ttl.powerFraction === null || typeof ttl.powerFraction !== \"number\") {\n throw new InvalidScientificInputError(\"TTL found no flash return; there is no power fraction to apply.\");\n }\n return { ...flash, outputControl: { ...flash.outputControl, scale: flash.outputControl.scale * ttl.powerFraction } };\n}\n"]}
@@ -26,7 +26,7 @@ export const FLASH_SYNC_CAPABILITY_SCHEMA_VERSION = "0.1.0";
26
26
  /** Sync-capability schema that may declare high-speed sync (a fast pulse train that acts as continuous light). */
27
27
  export const FLASH_SYNC_CAPABILITY_HSS_SCHEMA_VERSION = "0.2.0";
28
28
  /** Model identity of the pulsed quasi-continuous high-speed-sync burst. */
29
- export const HIGH_SPEED_SYNC_MODEL = Object.freeze({
29
+ export const HIGH_SPEED_SYNC_MODEL = /* @__PURE__ */ Object.freeze({
30
30
  id: "flash-hss-quasi-continuous-burst",
31
31
  version: "1.0.0"
32
32
  });