@photivra/engine 2.8.1 → 2.10.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 (91) hide show
  1. package/CHANGELOG.md +144 -1
  2. package/CITATION.cff +1 -1
  3. package/README.md +8 -6
  4. package/SECURITY.md +1 -1
  5. package/THIRD_PARTY.md +2 -0
  6. package/dist/capture/export-jpeg.d.ts +19 -0
  7. package/dist/capture/export-jpeg.js +190 -108
  8. package/dist/capture/export-jpeg.js.map +1 -1
  9. package/dist/capture/native-environment-lean.js +106 -12
  10. package/dist/capture/native-environment-lean.js.map +1 -1
  11. package/dist/capture/native-environment-tabulated.d.ts +37 -0
  12. package/dist/capture/native-environment-tabulated.js +42 -6
  13. package/dist/capture/native-environment-tabulated.js.map +1 -1
  14. package/dist/core/portable-math.d.ts +13 -0
  15. package/dist/core/portable-math.js +43 -0
  16. package/dist/core/portable-math.js.map +1 -1
  17. package/dist/core/version.d.ts +1 -1
  18. package/dist/core/version.js +1 -1
  19. package/dist/core/version.js.map +1 -1
  20. package/dist/facade/channel-response.d.ts +15 -0
  21. package/dist/facade/channel-response.js +33 -0
  22. package/dist/facade/channel-response.js.map +1 -0
  23. package/dist/facade/diffraction-provider.js +50 -15
  24. package/dist/facade/diffraction-provider.js.map +1 -1
  25. package/dist/facade/display-view.d.ts +75 -0
  26. package/dist/facade/display-view.js +471 -109
  27. package/dist/facade/display-view.js.map +1 -1
  28. package/dist/facade/expected-signal-setup.js +2 -1
  29. package/dist/facade/expected-signal-setup.js.map +1 -1
  30. package/dist/facade/expected-signal.d.ts +36 -4
  31. package/dist/facade/expected-signal.js +331 -43
  32. package/dist/facade/expected-signal.js.map +1 -1
  33. package/dist/facade/scene-radiance.d.ts +4 -1
  34. package/dist/facade/scene-radiance.js +4 -4
  35. package/dist/facade/scene-radiance.js.map +1 -1
  36. package/dist/facade/simulate-capture.d.ts +77 -5
  37. package/dist/facade/simulate-capture.js +232 -40
  38. package/dist/facade/simulate-capture.js.map +1 -1
  39. package/dist/facade/simulate-display-view.d.ts +25 -2
  40. package/dist/facade/simulate-display-view.js +55 -21
  41. package/dist/facade/simulate-display-view.js.map +1 -1
  42. package/dist/gpu/image-kernel-execution.d.ts +5 -0
  43. package/dist/gpu/image-kernel-execution.js +2 -2
  44. package/dist/gpu/image-kernel-execution.js.map +1 -1
  45. package/dist/index.d.ts +5 -4
  46. package/dist/index.js +3 -3
  47. package/dist/index.js.map +1 -1
  48. package/dist/optics/diffraction-psf-profile.d.ts +19 -17
  49. package/dist/optics/diffraction-psf-profile.js +49 -18
  50. package/dist/optics/diffraction-psf-profile.js.map +1 -1
  51. package/dist/optics/irradiance-convolution.d.ts +11 -0
  52. package/dist/optics/irradiance-convolution.js +64 -24
  53. package/dist/optics/irradiance-convolution.js.map +1 -1
  54. package/dist/optics/scene-to-sensor-irradiance.d.ts +13 -0
  55. package/dist/optics/scene-to-sensor-irradiance.js +58 -24
  56. package/dist/optics/scene-to-sensor-irradiance.js.map +1 -1
  57. package/dist/optics/sensor-aperture-rays.js +2 -1
  58. package/dist/optics/sensor-aperture-rays.js.map +1 -1
  59. package/dist/optics/sensor-environment-query.js +4 -3
  60. package/dist/optics/sensor-environment-query.js.map +1 -1
  61. package/dist/optics/site-diffraction-kernel.d.ts +16 -0
  62. package/dist/optics/site-diffraction-kernel.js +60 -8
  63. package/dist/optics/site-diffraction-kernel.js.map +1 -1
  64. package/dist/output/print-jpeg.d.ts +28 -1
  65. package/dist/output/print-jpeg.js +79 -7
  66. package/dist/output/print-jpeg.js.map +1 -1
  67. package/dist/processing/demosaic.js +114 -37
  68. package/dist/processing/demosaic.js.map +1 -1
  69. package/dist/schema/scene-radiance.d.ts +8 -0
  70. package/dist/schema/scene-radiance.js +28 -17
  71. package/dist/schema/scene-radiance.js.map +1 -1
  72. package/dist/sensor/environment-photo-signal.d.ts +21 -0
  73. package/dist/sensor/environment-photo-signal.js +68 -2
  74. package/dist/sensor/environment-photo-signal.js.map +1 -1
  75. package/dist/sensor/stochastic-seed.d.ts +12 -0
  76. package/dist/sensor/stochastic-seed.js +25 -0
  77. package/dist/sensor/stochastic-seed.js.map +1 -1
  78. package/docs/API_REFERENCE.md +23 -1
  79. package/docs/QUICK_START.md +77 -8
  80. package/docs/USAGE.md +30 -0
  81. package/docs/api/capture-export-jpeg.md +10 -0
  82. package/docs/api/core-version.md +1 -1
  83. package/docs/api/exports.json +77 -2
  84. package/docs/api/facade-display-view.md +50 -0
  85. package/docs/api/facade-scene-radiance.md +2 -1
  86. package/docs/api/facade-simulate-capture.md +88 -8
  87. package/docs/api/facade-simulate-display-view.md +23 -1
  88. package/docs/api/gpu-image-kernel-execution.md +5 -0
  89. package/docs/api/optics-diffraction-psf-profile.md +1 -1
  90. package/docs/api/output-print-jpeg.md +41 -0
  91. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"environment-photo-signal.js","sourceRoot":"","sources":["../../src/sensor/environment-photo-signal.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Photo signal for one sensor site from a scene-radiance provider (the \"environment\"): asks the\n * provider for radiance along each sampled direction, then applies optics and the sensor response.\n *\n * Work is split into a plan and an execution step. Planning checks spatial, spectral and temporal\n * coverage and builds an internal plan before any provider code runs. Each query gives an explicit\n * scene direction, a wavelength (air or vacuum) and a time in seconds from the shutter's opening\n * reference. Optical throughput, an optional local PSF (point-spread function) and the EQE (external\n * quantum efficiency) response then give the expected photo signal. Calling the provider does not\n * verify its light transport, visibility or calibration.\n * @see docs/ENVIRONMENT_RAW_CAPTURE.md for equations, coordinate/unit conventions, blockers and\n * support limits.\n */\n\nimport { approximationResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { freezeOwnedData } from \"../core/owned-data.js\";\nimport { requirePublicOpaqueId, requireInputKeys } from \"../core/record-validation.js\";\nimport { parseEvidenceList, type EvidenceProvenance } from \"../core/evidence-provenance.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport { parseSceneIlluminationProfile } from \"../schema/illumination.js\";\nimport { parseSceneIlluminationTemporalProfile } from \"../schema/illumination-temporal.js\";\nimport { parseSceneMaterialResponseProfile, parseSceneRadianceProviderProfile, parseSceneRadianceEvaluationResult,\n validateSceneRadianceEvaluationBindings, type SceneRadianceEvaluationRequest,\n type SceneRadianceEvaluationResult } from \"../schema/scene-radiance.js\";\nimport { calculateMappedSensorEnvironmentRadianceQuery, calculateSensorEnvironmentRadianceQuery, prepareEnvironmentLensFieldMapping,\n type CalculateSensorEnvironmentRadianceQueryInput, type EnvironmentLensFieldMapping } from \"../optics/sensor-environment-query.js\";\nimport { calculateSceneRadianceToSensorIrradiance, type CalculateSceneRadianceToSensorIrradianceInput } from \"../optics/scene-to-sensor-irradiance.js\";\nimport { calculateSensorPsfIrradianceQuadrature, type CalculateSensorPsfIrradianceQuadratureInput } from \"../optics/sensor-psf-quadrature.js\";\nimport { calculateIlluminationVignetting, type IlluminationVignettingProfile } from \"../optics/illumination-vignetting.js\";\nimport { calculateSensorApertureRays, type IdealCircularPupil, type SensorApertureRay } from \"../optics/sensor-aperture-rays.js\";\nimport { sensorPsfSourcePoint } from \"../optics/sensor-psf-support.js\";\nimport { markEngineOwnedLensSampledPsfProfile, resolveLensSampledPsf } from \"../optics/lens-psf-profile.js\";\nimport { calculateSensorSpatialSamplingQuadrature } from \"./spatial-sampling-quadrature.js\";\nimport { calculateSensorSpectralQuadrature } from \"./spectral-quadrature.js\";\nimport { resolveNativeEffectiveRasterColorSamplingBinding } from \"./capture-color-sampling-binding.js\";\nimport { calculateCaptureExposureWindows } from \"./exposure-window.js\";\nimport { createSensorEqeTemporalPhotoSignal } from \"./temporal-photo-signal.js\";\nimport type { CalculateSensorEqeTemporalExposureInput } from \"./eqe-temporal-exposure.js\";\nimport type { CalculateSceneToSensorIrradianceQuadratureInput } from \"../optics/scene-to-sensor-quadrature.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\n\n/** Caller-supplied synchronous radiance function, run locally. Calling it does not prove its light transport is physical. */\nexport type EnvironmentRadianceEvaluator = (request: Readonly<SceneRadianceEvaluationRequest>) => SceneRadianceEvaluationResult;\nexport type EnvironmentApertureRadianceEvaluator = (request: Readonly<SceneRadianceEvaluationRequest>, apertureRay: Readonly<SensorApertureRay>) => SceneRadianceEvaluationResult;\nexport interface CalculateEnvironmentSensorPhotoSignalInput {\n temporalIntegrationId: string;\n sensor: Omit<CalculateSensorEqeTemporalExposureInput, \"samples\">;\n sceneBindings: CalculateSceneToSensorIrradianceQuadratureInput[\"sceneBindings\"];\n optics: Omit<CalculateSceneRadianceToSensorIrradianceInput,\n \"sceneRadianceRequest\" | \"sceneRadianceResult\" | \"imagePointMm\" | \"fieldThroughput\">;\n motion: Pick<CalculateSensorEnvironmentRadianceQueryInput,\n \"angularVelocityRadPerSec\" | \"timeReference\" | \"environmentDirectionConvention\">;\n temporalSampleCount: number;\n /** Optional ideal circular pupil, for geometric depth of field (DOF) and visibility. Cannot be combined with a sampled PSF. */\n pupil?: IdealCircularPupil;\n /**\n * Optional lens field mapping (added in 2.0): a projection model and radial or Brown-Conrady\n * distortion for the scene-ray query. Its distortion area factor is applied to irradiance exactly\n * once. If omitted, the ideal rectilinear, distortion-free path is unchanged. Cannot be combined\n * with `pupil`.\n */\n lensFieldMapping?: EnvironmentLensFieldMapping;\n /** Explicit attenuation across the field (such as vignetting), applied once before the PSF spreads the light. */\n fieldThroughput: { kind: \"unity\"; evidence: readonly EvidenceProvenance[]; limitation: string } | {\n kind: \"radial-illumination-vignetting\"; profile: IlluminationVignettingProfile;\n evidence: readonly EvidenceProvenance[]; limitation: string;\n };\n psf: { kind: \"not-applied\"; evidence: readonly EvidenceProvenance[]; limitation: string } | {\n kind: \"sampled-local\";\n configuration: Pick<CalculateSensorPsfIrradianceQuadratureInput, \"psf\" | \"psfWavelengthBasis\" | \"spatialModel\">;\n };\n evaluateRadiance: EnvironmentRadianceEvaluator;\n /** Required with `pupil`: rays start at different points on the aperture, so a direction-only function cannot be used. */\n evaluateApertureRadiance?: EnvironmentApertureRadianceEvaluator;\n}\nexport interface EnvironmentSensorPhotoSignal {\n sourceTargetProjectionCalculated: true;\n providerCallbackExecuted: true;\n providerTransportVerified: false;\n sceneVisibilityCalculated: false;\n productionPlanActivated: false;\n providerEvaluationCount: number;\n sourcePlane: \"sensor-package-incident\";\n fieldThroughputEvidence: readonly EvidenceProvenance[];\n fieldThroughputLimitation: string;\n psfOmission: { evidence: readonly EvidenceProvenance[]; limitation: string } | null;\n psfRedistributionApplied: boolean;\n pupilIntegrationApplied: boolean;\n pupilIntegrationProfile: IdealCircularPupil | null;\n photo: ReturnType<typeof createSensorEqeTemporalPhotoSignal>;\n instants: readonly {\n temporalSampleIndex: number;\n timeSecondsFromOpeningReference: number;\n psf: ReturnType<typeof calculateSensorPsfIrradianceQuadrature> | null;\n evaluations: readonly {\n query: ReturnType<typeof calculateSensorEnvironmentRadianceQuery>;\n result: SceneRadianceEvaluationResult;\n bindings: ReturnType<typeof validateSceneRadianceEvaluationBindings>;\n optics: ReturnType<typeof calculateSceneRadianceToSensorIrradiance>;\n apertureRay?: SensorApertureRay;\n apertureRequest?: SceneRadianceEvaluationRequest;\n }[];\n }[];\n}\n\n/** Internal plan, shared with the full-frame composer, so all work is bounded before any callback runs. */\ninterface EnvironmentPhotoPlan {\n owned: Omit<CalculateEnvironmentSensorPhotoSignalInput, \"evaluateRadiance\" | \"evaluateApertureRadiance\">;\n sceneBindings: CalculateSceneToSensorIrradianceQuadratureInput[\"sceneBindings\"];\n fieldThroughputEvidence: readonly EvidenceProvenance[];\n spatialQuadrature: ReturnType<typeof calculateSensorSpatialSamplingQuadrature>[\"value\"];\n spectralQuadrature: ReturnType<typeof calculateSensorSpectralQuadrature>[\"value\"];\n count: number;\n focus: CalculateSensorEnvironmentRadianceQueryInput[\"focus\"];\n instants: { temporalSampleIndex: number; time: number; groups: {\n node: CalculateSensorPsfIrradianceQuadratureInput[\"samples\"][number][\"node\"];\n taps: { kernelSampleX: number; kernelSampleY: number; point: { x: number; y: number };\n /** Deep-frozen provider request of the planned query (the same object as query.value.request). */\n request: SceneRadianceEvaluationRequest;\n /** The planned query's image-plane source point (the same object as query.value.sourcePointImagePlaneMm). */\n sourcePointImagePlaneMm: { x: number; y: number };\n /** The complete query result. Kept only by \"full\" plans, whose per-evaluation records include it. */\n query?: ReturnType<typeof calculateSensorEnvironmentRadianceQuery>;\n apertureRays?: readonly SensorApertureRay[];\n /** 1 / det(d distorted / d ideal), the change in area from distortion. Present only with a lens field mapping. */\n distortionAreaFactor?: number;\n fieldThroughput: CalculateSceneRadianceToSensorIrradianceInput[\"fieldThroughput\"] }[];\n }[] }[];\n}\n/**\n * Builds an internal execution plan covering all optical, spatial, spectral and temporal sampling,\n * without calling any provider code.\n *\n * Coverage is checked completely before any supplied radiance function runs. Each query gives an\n * explicit scene direction, a wavelength (air or vacuum) and seconds from the opening reference.\n * @param input - The photo-signal input without its radiance functions; see\n * `CalculateEnvironmentSensorPhotoSignalInput`.\n * @param ownership - Internal. \"copy\" (default) makes a structured clone of the input. \"engine-owned\"\n * uses the input as the plan's own data without cloning again. Only engine callers that already hold a\n * private structured clone, never shown to supplied code and never changed afterwards, may pass it.\n * @param retention - Internal. \"full\" (default) keeps every planned query result for \"full\" execution\n * records. \"photo-only\" keeps only each query's frozen request and image-plane point, which is all that\n * photo-only execution reads. Every query is still computed and validated at plan time, so limit checks\n * and errors are unchanged.\n * @returns The result. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function planEnvironmentSensorPhotoSignal(input: Omit<CalculateEnvironmentSensorPhotoSignalInput, \"evaluateRadiance\" | \"evaluateApertureRadiance\">,\n ownership: \"copy\" | \"engine-owned\" = \"copy\", retention: \"full\" | \"photo-only\" = \"full\"): EnvironmentPhotoPlan {\n const owned = ownership === \"engine-owned\" ? input : structuredClone(input);\n requirePublicOpaqueId(owned.temporalIntegrationId, \"Environment integration requires a public identity.\");\n if (!Number.isSafeInteger(owned.temporalSampleCount) || owned.temporalSampleCount < 1 || owned.temporalSampleCount > 256 ||\n owned.sensor.responseApplication.sourcePlane.value !== \"sensor-package-incident\" ||\n (owned.fieldThroughput?.kind !== \"unity\" && owned.fieldThroughput?.kind !== \"radial-illumination-vignetting\") || typeof owned.fieldThroughput.limitation !== \"string\" || !owned.fieldThroughput.limitation.trim()) {\n throw new InvalidScientificInputError(\"Environment exposure requires package-incident response, 1..256 midpoints and explicit supported field throughput.\");\n }\n if (owned.fieldThroughput.kind === \"radial-illumination-vignetting\" &&\n (typeof owned.fieldThroughput.profile !== \"object\" || owned.fieldThroughput.profile === null ||\n typeof owned.fieldThroughput.profile.coefficients !== \"object\" || owned.fieldThroughput.profile.coefficients === null)) {\n throw new InvalidScientificInputError(\"Radial throughput requires a declared profile and coefficients.\");\n }\n const fieldThroughputEvidence = parseEvidenceList(owned.fieldThroughput.evidence, \"environmentFieldThroughput.evidence\");\n const sceneBindings = { providerProfile: parseSceneRadianceProviderProfile(owned.sceneBindings.providerProfile),\n illuminationProfile: parseSceneIlluminationProfile(owned.sceneBindings.illuminationProfile),\n materialResponseProfile: parseSceneMaterialResponseProfile(owned.sceneBindings.materialResponseProfile),\n ...(owned.sceneBindings.illuminationTemporalProfile === undefined ? {} : {\n illuminationTemporalProfile: parseSceneIlluminationTemporalProfile(owned.sceneBindings.illuminationTemporalProfile) }) };\n if (sceneBindings.providerProfile.fidelity.spectral !== \"wavelength-resolved\") throw new InvalidScientificInputError(\"Environment exposure requires wavelength-resolved provider fidelity.\");\n const sensor = owned.sensor;\n const nativeRaster = sensor.localExposure.exposureWindowInput.nativeRaster;\n const binding = resolveNativeEffectiveRasterColorSamplingBinding({ nativeRaster, colorSamplingProfile: sensor.colorSamplingProfile,\n bindingProfile: sensor.localExposure.bindingProfile });\n if (binding.relationshipMeaning !== \"one-native-effective-sample-to-one-color-site\") throw new InvalidScientificInputError(\"Environment exposure requires explicit one-to-one native/color timing.\");\n const spatialQuadrature = calculateSensorSpatialSamplingQuadrature({ ...sensor.spatialSampling, nativeRaster,\n colorSamplingProfile: sensor.colorSamplingProfile, colorSamplingBindingProfile: sensor.localExposure.bindingProfile }).value;\n const spectralQuadrature = calculateSensorSpectralQuadrature({ ...sensor.spectralSampling,\n colorSamplingProfile: sensor.colorSamplingProfile, spectralResponseProfile: sensor.spectralResponseProfile }).value;\n if (spectralQuadrature.wavelengthBasis === \"unspecified\") throw new InvalidScientificInputError(\"Environment spectral basis must be explicit.\");\n const wavelengthBasis = spectralQuadrature.wavelengthBasis;\n const site = sensor.spatialSampling.site;\n const window = calculateCaptureExposureWindows({ ...sensor.localExposure.exposureWindowInput,\n samplePointsNative: [{ x: site.x+.5, y: site.y+.5 }] }).value.samples[0]!;\n const focus = owned.optics.focus.kind === \"infinity-focus\" ? { kind: \"infinity\" as const } :\n owned.optics.focus.kind === \"ideal-symmetric-thin-lens\" ? { kind: \"finite\" as const, distanceM: owned.optics.focus.objectDistanceM } :\n owned.optics.focus.focus.kind === \"infinity\" ? { kind: \"infinity\" as const } : { kind: \"finite\" as const, distanceM: owned.optics.focus.focus.objectDistanceM };\n if (owned.psf.kind === \"not-applied\") {\n parseEvidenceList(owned.psf.evidence, \"environmentPsfOmission.evidence\");\n if (typeof owned.psf.limitation !== \"string\" || !owned.psf.limitation.trim()) throw new InvalidScientificInputError(\"PSF omission needs an explicit limitation.\");\n } else if (owned.psf.kind !== \"sampled-local\") throw new InvalidConfigurationError(\"Unknown environment PSF selection.\");\n if (owned.psf.kind === \"sampled-local\") {\n const p = owned.psf.configuration.psf;\n // The profile belongs only to this plan, so its parse can be cached across nodes and time instants.\n markEngineOwnedLensSampledPsfProfile(p.profile);\n if (p.focalLengthMm !== owned.optics.focalLengthMm || p.apertureFNumber !== owned.optics.nominalFNumber ||\n p.focus.kind !== focus.kind || (p.focus.kind === \"finite\" && focus.kind === \"finite\" && p.focus.distanceM !== focus.distanceM)) {\n throw new InvalidScientificInputError(\"PSF and optical focal/aperture/focus state must match exactly.\");\n }\n }\n if (owned.pupil !== undefined && owned.psf.kind !== \"not-applied\") throw new InvalidScientificInputError(\"Ideal pupil integration cannot mix with a sampled PSF; geometric defocus would be ambiguous.\");\n const lensMapping = owned.lensFieldMapping === undefined ? undefined : prepareEnvironmentLensFieldMapping(owned.lensFieldMapping);\n if (lensMapping !== undefined && owned.pupil !== undefined) {\n throw new InvalidScientificInputError(\"Ideal pupil rays use the rectilinear distortion-free projection and cannot combine with a lens field mapping.\");\n }\n const pupilCount = owned.pupil === undefined ? 1 : calculateSensorApertureRays({sourcePointNativeSensorMm:{x:0,y:0},\n focalLengthMm:owned.optics.focalLengthMm,nominalFNumber:owned.optics.nominalFNumber,focus,pupil:owned.pupil,\n angularVelocityRadPerSec:owned.motion.angularVelocityRadPerSec,timeSecondsFromOpeningReference:window.startOffsetSecondsFromOpeningReference}).value.rays.length;\n let count = 0;\n const nodes = spectralQuadrature.nodes.flatMap(spectral => spatialQuadrature.nodes.map(spatial => {\n const node = { spatialNode: { antiAliasingComponentIndex: spatial.antiAliasingComponentIndex,\n apertureSampleXIndex: spatial.apertureSampleXIndex, apertureSampleYIndex: spatial.apertureSampleYIndex },\n spectralSampleIndex: spectral.spectralSampleIndex, wavelengthNanometers: spectral.wavelengthNanometers };\n const destination = spatial.preAntiAliasingSourcePointMm;\n const resolved = owned.psf.kind === \"sampled-local\" ? resolveLensSampledPsf({ ...owned.psf.configuration.psf,\n wavelengthNm: spectral.wavelengthNanometers, fieldPointMm: { x: destination.x, y: -destination.y } }) : null;\n const kernel = resolved?.value.kernel;\n const taps = kernel ? kernel.widthSamples*kernel.heightSamples : 1;\n count += taps*owned.temporalSampleCount*pupilCount;\n if (count > 100000) throw new InvalidScientificInputError(\"Environment exposure exceeds the 100000-provider-evaluation budget.\");\n const points = Array.from({ length: taps }, (_, i) => ({ kernelSampleX: kernel ? i%kernel.widthSamples : 0,\n kernelSampleY: kernel ? Math.floor(i/kernel.widthSamples) : 0,\n point: kernel ? sensorPsfSourcePoint(destination, kernel, i%kernel.widthSamples, Math.floor(i/kernel.widthSamples)) : { ...destination } }));\n return { node, points: points.map(p => ({ ...p, fieldThroughput: owned.fieldThroughput.kind === \"unity\"\n ? { kind: \"unity\" as const }\n : { kind: \"illumination-vignetting-result\" as const, result: calculateIlluminationVignetting({\n imagePointMm: { x: p.point.x, y: -p.point.y }, profile: owned.fieldThroughput.profile }).value } })) };\n }));\n const measure = window.localExposureDurationSeconds/owned.temporalSampleCount;\n let previous = -Infinity;\n const instants = Array.from({ length: owned.temporalSampleCount }, (_, temporalSampleIndex) => {\n const time = window.startOffsetSecondsFromOpeningReference+(temporalSampleIndex+.5)*measure;\n if (!(time > window.startOffsetSecondsFromOpeningReference && time > previous && time < window.endOffsetSecondsFromOpeningReference)) throw new InvalidScientificInputError(\"Local midpoints must be representable and strictly ordered.\");\n previous = time;\n const groups = nodes.map((n, ni) => ({ node: n.node, taps: n.points.map((p, pi) => {\n const apertureRays = owned.pupil === undefined ? undefined : calculateSensorApertureRays({sourcePointNativeSensorMm:p.point,\n focalLengthMm:owned.optics.focalLengthMm,nominalFNumber:owned.optics.nominalFNumber,focus,pupil:owned.pupil,\n angularVelocityRadPerSec:owned.motion.angularVelocityRadPerSec,timeSecondsFromOpeningReference:time}).value.rays;\n const queryInput: CalculateSensorEnvironmentRadianceQueryInput = { ...owned.motion, sourcePointNativeSensorMm: p.point,\n focalLengthMm: owned.optics.focalLengthMm, focus, timeSecondsFromOpeningReference: time,\n request: { schemaVersion: \"0.1.0\", sampleId: `${owned.temporalIntegrationId}-t${temporalSampleIndex}-n${ni}-p${pi}`,\n providerProfileId: sceneBindings.providerProfile.profileId, sceneId: sceneBindings.providerProfile.sceneId,\n illuminationProfileId: sceneBindings.illuminationProfile.profileId, materialResponseProfileId: sceneBindings.materialResponseProfile.profileId,\n wavelengthNanometers: n.node.wavelengthNanometers, wavelengthBasis } };\n const query = freezePlannedRequest(lensMapping === undefined ? calculateSensorEnvironmentRadianceQuery(queryInput)\n : calculateMappedSensorEnvironmentRadianceQuery(queryInput, lensMapping));\n const areaFactor = query.value.lensFieldMapping?.distortionAreaFactor;\n // Photo-only plans drop the query result (projection, diagnostics) once it is validated (a 2.0 memory saving).\n return { ...p, ...(apertureRays === undefined ? {} : { apertureRays }),\n ...(areaFactor === undefined ? {} : { distortionAreaFactor: areaFactor }), request: query.value.request,\n sourcePointImagePlaneMm: query.value.sourcePointImagePlaneMm, ...(retention === \"full\" ? { query } : {}) };\n }) }));\n // Validate PSF evidence/basis/support before supplied code is invoked.\n if (owned.psf.kind === \"sampled-local\") calculateSensorPsfIrradianceQuadrature({ ...owned.psf.configuration,\n spatialQuadrature, spectralQuadrature, timeSecondsFromOpeningReference: time,\n inputMeaning: \"pre-psf-pre-sensor-stack-pre-aa-spectral-irradiance\", samples: groups.map(g => ({ node: g.node,\n sourceSamples: g.taps.map(t => ({ kernelSampleX: t.kernelSampleX, kernelSampleY: t.kernelSampleY,\n sourcePointNativeSensorMm: t.point, timeSecondsFromOpeningReference: time, spectralIrradianceWattsPerSquareMeterPerNanometer: 0 })) })) });\n return { temporalSampleIndex, time, groups };\n });\n return { owned, sceneBindings, fieldThroughputEvidence, spatialQuadrature, spectralQuadrature, instants, count, focus };\n}\n\n/** Aperture queries already built for a batched provider, keyed by the planned ray; each is used once. */\nconst PREBUILT_APERTURE_QUERIES = new WeakMap<object, { base: object; request: SceneRadianceEvaluationRequest; apertureRay: SensorApertureRay }>();\n\n/**\n * Internal: the frozen provider request and frozen private ray copy of one ideal-pupil query (sample ID suffix \"-a\"\n * and the pupil index, reversed ray direction). If prebuildApertureRadianceQuery already built this query for a batch,\n * the same objects are returned once, so the batch and the executor share them; otherwise new ones are built.\n */\nexport function apertureRadianceQuery(base: SceneRadianceEvaluationRequest, ray: SensorApertureRay):\n { request: SceneRadianceEvaluationRequest; apertureRay: SensorApertureRay } {\n const prebuilt = PREBUILT_APERTURE_QUERIES.get(ray);\n if (prebuilt !== undefined && prebuilt.base === base) {\n PREBUILT_APERTURE_QUERIES.delete(ray);\n return { request: prebuilt.request, apertureRay: prebuilt.apertureRay };\n }\n return { request: freezeOwnedData({ ...base, sampleId: base.sampleId+\"-a\"+ray.pupilSampleIndex,\n target: { kind: \"environment-direction\" as const, outgoingDirectionUnitVector: {\n x: -ray.directionUnitVector.x, y: -ray.directionUnitVector.y, z: -ray.directionUnitVector.z } } }),\n apertureRay: freezeOwnedData(copyRay(ray)) };\n}\n/**\n * A private copy of an engine-computed ray: plain records of numbers, copied key by key in the same order, so it\n * equals what structuredClone returns for this data (a 2.0 performance change: structuredClone dominated pupil runs).\n */\nfunction copyRay(ray: SensorApertureRay): SensorApertureRay {\n const copy = (value: unknown): unknown => {\n if (typeof value !== \"object\" || value === null) return value;\n if (Array.isArray(value)) return value.map(copy);\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(value)) out[key] = copy((value as Record<string, unknown>)[key]);\n return out;\n };\n return copy(ray) as SensorApertureRay;\n}\n/** Internal: builds a pupil query for a batched provider and keeps it for the executor's apertureRadianceQuery call. */\nexport function prebuildApertureRadianceQuery(base: SceneRadianceEvaluationRequest, ray: SensorApertureRay):\n { request: SceneRadianceEvaluationRequest; apertureRay: SensorApertureRay } {\n PREBUILT_APERTURE_QUERIES.delete(ray);\n const query = apertureRadianceQuery(base, ray);\n PREBUILT_APERTURE_QUERIES.set(ray, { base, ...query });\n return query;\n}\n\n/** Deep-freezes a planned provider request once, so execution can hand the same immutable request to supplied code. */\nfunction freezePlannedRequest<T extends { value: { request: object } }>(query: T): T {\n freezeOwnedData(query.value.request);\n return query;\n}\n\n/**\n * Internal: runs a completed, bounded plan. On failure nothing partial is returned.\n * With retention \"full\" (default), every per-evaluation record is kept in value.instants[].evaluations.\n * With \"photo-only\", those lists are empty, for engine callers that read only value.photo. Photo values\n * are identical either way.\n */\nexport function executeEnvironmentSensorPhotoSignal(plan: ReturnType<typeof planEnvironmentSensorPhotoSignal>, evaluate: EnvironmentRadianceEvaluator, evaluateAperture?: EnvironmentApertureRadianceEvaluator,\n retention: \"full\" | \"photo-only\" = \"full\"): CalculationResult<EnvironmentSensorPhotoSignal> {\n if (typeof evaluate !== \"function\") throw new InvalidScientificInputError(\"Environment radiance evaluator must be synchronous callable code.\");\n const { owned } = plan;\n if (owned.pupil !== undefined && typeof evaluateAperture !== \"function\") throw new InvalidScientificInputError(\"Pupil integration requires an origin-aware aperture evaluator.\");\n if (retention === \"full\" && plan.instants.some(instant => instant.groups.some(group => group.taps.some(tap => tap.query === undefined)))) {\n throw new InvalidScientificInputError(\"Full environment execution records require a full plan.\");\n }\n const irradiance = [];\n const instants: EnvironmentSensorPhotoSignal[\"instants\"][number][] = [];\n for (const instant of plan.instants) {\n const evaluations: EnvironmentSensorPhotoSignal[\"instants\"][number][\"evaluations\"][number][] = [];\n const samples = instant.groups.map(group => ({ node: group.node, sourceSamples: group.taps.map(tap => {\n let integratedIrradiance = 0;\n const support: readonly (SensorApertureRay | undefined)[] = tap.apertureRays ?? [undefined];\n for (const ray of support) {\n // Planned requests are deep-frozen once at plan time (freezePlannedRequest); no per-evaluation clone.\n const query = ray === undefined ? undefined : apertureRadianceQuery(tap.request, ray);\n const request = query === undefined ? tap.request : query.request, apertureRay = query?.apertureRay;\n const result = parseSceneRadianceEvaluationResult(apertureRay === undefined ? evaluate(request) : evaluateAperture!(request,apertureRay));\n const bindings = validateSceneRadianceEvaluationBindings({ ...plan.sceneBindings, request, result });\n const optics = calculateSceneRadianceToSensorIrradiance({ ...owned.optics, sceneRadianceRequest: request,\n sceneRadianceResult: result, imagePointMm: tap.sourcePointImagePlaneMm, fieldThroughput: tap.fieldThroughput });\n if (retention === \"full\") evaluations.push({ query: tap.query!, result, bindings, optics,\n ...(apertureRay === undefined ? {} : {apertureRay,apertureRequest:request}) });\n integratedIrradiance += optics.value.sensorPlaneSpectralIrradianceWattsPerSquareMeterNanometer*(ray?.weight ?? 1);\n }\n if (tap.distortionAreaFactor !== undefined) {\n // Distortion squeezes or stretches the ideal image. Flux is conserved, so irradiance scales by 1/det, once.\n integratedIrradiance *= tap.distortionAreaFactor;\n }\n return { kernelSampleX: tap.kernelSampleX, kernelSampleY: tap.kernelSampleY, sourcePointNativeSensorMm: tap.point,\n timeSecondsFromOpeningReference: instant.time, spectralIrradianceWattsPerSquareMeterPerNanometer: integratedIrradiance };\n }) }));\n const psf = owned.psf.kind === \"sampled-local\" ? calculateSensorPsfIrradianceQuadrature({ ...owned.psf.configuration,\n spatialQuadrature: plan.spatialQuadrature, spectralQuadrature: plan.spectralQuadrature,\n timeSecondsFromOpeningReference: instant.time, inputMeaning: \"pre-psf-pre-sensor-stack-pre-aa-spectral-irradiance\", samples }) : null;\n irradiance.push({ temporalSampleIndex: instant.temporalSampleIndex, timeSecondsFromOpeningReference: instant.time,\n irradianceSamples: psf ? psf.value.irradianceSamples : samples.map(s => ({ node: s.node,\n spectralIrradianceWattsPerSquareMeterPerNanometer: s.sourceSamples[0]!.spectralIrradianceWattsPerSquareMeterPerNanometer })) });\n instants.push({ temporalSampleIndex: instant.temporalSampleIndex, timeSecondsFromOpeningReference: instant.time, psf, evaluations });\n }\n const photo = createSensorEqeTemporalPhotoSignal({ temporalIntegrationId: owned.temporalIntegrationId,\n exposure: { ...owned.sensor, samples: irradiance } });\n return approximationResult({ sourceTargetProjectionCalculated: true, providerCallbackExecuted: true,\n providerTransportVerified: false, sceneVisibilityCalculated: false, productionPlanActivated: false,\n providerEvaluationCount: plan.count, sourcePlane: \"sensor-package-incident\", fieldThroughputEvidence: plan.fieldThroughputEvidence,\n fieldThroughputLimitation: owned.fieldThroughput.limitation,\n psfOmission: owned.psf.kind === \"not-applied\" ? { evidence: parseEvidenceList(owned.psf.evidence, \"psfOmission.evidence\"), limitation: owned.psf.limitation } : null,\n psfRedistributionApplied: owned.psf.kind === \"sampled-local\", pupilIntegrationApplied: owned.pupil !== undefined, pupilIntegrationProfile: owned.pupil ?? null, photo, instants\n }, \"environment-sensor-photo-signal\", owned.lensFieldMapping === undefined ? \"0.3.0\" : \"0.4.0\", [\n \"Supplied synchronous provider code was invoked at each generated environment query; physical transport and visibility are not verified.\",\n \"Ideal focus-aware rotation/projection, explicit evidence-bound field throughput and optional destination-local sampled PSF are bounded approximations.\",\n \"Optional ideal pupil rays use normalized radiance averaging; source intersections remain provider-owned and convergence is unverified.\",\n \"PSF pupil throughput is not applied; child uncertainty, finite-support and quadrature convergence are not combined or established.\",\n ...(owned.lensFieldMapping === undefined ? [] : [\n \"Lens field mapping: rays use the declared projection after inverse distortion; irradiance is multiplied once by the distortion area factor 1/det(d distorted/d ideal); field throughput and sampled PSFs remain evaluated at the physical sensor point.\"\n ])\n ]);\n}\n\n/** Plans, queries the provider and integrates the photo signal for one site, at the site's own midpoint sample positions. */\nexport function calculateEnvironmentSensorPhotoSignal(input: CalculateEnvironmentSensorPhotoSignalInput): CalculationResult<EnvironmentSensorPhotoSignal> {\n requireInputKeys(input, \"calculateEnvironmentSensorPhotoSignal\", [\"temporalIntegrationId\", \"sensor\", \"sceneBindings\", \"optics\", \"motion\", \"temporalSampleCount\", \"pupil\", \"lensFieldMapping\", \"fieldThroughput\", \"psf\", \"evaluateRadiance\", \"evaluateApertureRadiance\"]);\n const { evaluateRadiance, evaluateApertureRadiance, ...data } = input;\n return executeEnvironmentSensorPhotoSignal(planEnvironmentSensorPhotoSignal(data), evaluateRadiance, evaluateApertureRadiance);\n}\n"]}
1
+ {"version":3,"file":"environment-photo-signal.js","sourceRoot":"","sources":["../../src/sensor/environment-photo-signal.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Photo signal for one sensor site from a scene-radiance provider (the \"environment\"): asks the\n * provider for radiance along each sampled direction, then applies optics and the sensor response.\n *\n * Work is split into a plan and an execution step. Planning checks spatial, spectral and temporal\n * coverage and builds an internal plan before any provider code runs. Each query gives an explicit\n * scene direction, a wavelength (air or vacuum) and a time in seconds from the shutter's opening\n * reference. Optical throughput, an optional local PSF (point-spread function) and the EQE (external\n * quantum efficiency) response then give the expected photo signal. Calling the provider does not\n * verify its light transport, visibility or calibration.\n * @see docs/ENVIRONMENT_RAW_CAPTURE.md for equations, coordinate/unit conventions, blockers and\n * support limits.\n */\n\nimport { approximationResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { freezeOwnedData } from \"../core/owned-data.js\";\nimport { requirePublicOpaqueId, requireInputKeys } from \"../core/record-validation.js\";\nimport { parseEvidenceList, type EvidenceProvenance } from \"../core/evidence-provenance.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport { parseSceneIlluminationProfile } from \"../schema/illumination.js\";\nimport { parseSceneIlluminationTemporalProfile } from \"../schema/illumination-temporal.js\";\nimport { parseSceneMaterialResponseProfile, parseSceneRadianceProviderProfile, parseSceneRadianceEvaluationResult,\n parseSceneRadianceEvaluationResultWithValidatedRecords, validateSceneRadianceEvaluationBindings, validateSceneRadianceRequestResultBindings,\n type SceneRadianceEvaluationRequest, type SceneRadianceEvaluationResult, type ValidatedSceneRadianceRecords } from \"../schema/scene-radiance.js\";\nimport { calculateMappedSensorEnvironmentRadianceQuery, calculateSensorEnvironmentRadianceQuery, prepareEnvironmentLensFieldMapping,\n type CalculateSensorEnvironmentRadianceQueryInput, type EnvironmentLensFieldMapping } from \"../optics/sensor-environment-query.js\";\nimport { calculateSceneRadianceToSensorIrradiance, prepareSceneRadianceToSensorIrradianceValue,\n type CalculateSceneRadianceToSensorIrradianceInput } from \"../optics/scene-to-sensor-irradiance.js\";\nimport { isDeeplyFrozen } from \"../core/internal-radiance-evaluator.js\";\nimport { calculateSensorPsfIrradianceQuadrature, type CalculateSensorPsfIrradianceQuadratureInput } from \"../optics/sensor-psf-quadrature.js\";\nimport { calculateIlluminationVignetting, type IlluminationVignettingProfile } from \"../optics/illumination-vignetting.js\";\nimport { calculateSensorApertureRays, type IdealCircularPupil, type SensorApertureRay } from \"../optics/sensor-aperture-rays.js\";\nimport { sensorPsfSourcePoint } from \"../optics/sensor-psf-support.js\";\nimport { markEngineOwnedLensSampledPsfProfile, resolveLensSampledPsf } from \"../optics/lens-psf-profile.js\";\nimport { calculateSensorSpatialSamplingQuadrature } from \"./spatial-sampling-quadrature.js\";\nimport { calculateSensorSpectralQuadrature } from \"./spectral-quadrature.js\";\nimport { resolveNativeEffectiveRasterColorSamplingBinding } from \"./capture-color-sampling-binding.js\";\nimport { calculateCaptureExposureWindows } from \"./exposure-window.js\";\nimport { createSensorEqeTemporalPhotoSignal } from \"./temporal-photo-signal.js\";\nimport type { CalculateSensorEqeTemporalExposureInput } from \"./eqe-temporal-exposure.js\";\nimport type { CalculateSceneToSensorIrradianceQuadratureInput } from \"../optics/scene-to-sensor-quadrature.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\n\n/** Caller-supplied synchronous radiance function, run locally. Calling it does not prove its light transport is physical. */\nexport type EnvironmentRadianceEvaluator = (request: Readonly<SceneRadianceEvaluationRequest>) => SceneRadianceEvaluationResult;\nexport type EnvironmentApertureRadianceEvaluator = (request: Readonly<SceneRadianceEvaluationRequest>, apertureRay: Readonly<SensorApertureRay>) => SceneRadianceEvaluationResult;\nexport interface CalculateEnvironmentSensorPhotoSignalInput {\n temporalIntegrationId: string;\n sensor: Omit<CalculateSensorEqeTemporalExposureInput, \"samples\">;\n sceneBindings: CalculateSceneToSensorIrradianceQuadratureInput[\"sceneBindings\"];\n optics: Omit<CalculateSceneRadianceToSensorIrradianceInput,\n \"sceneRadianceRequest\" | \"sceneRadianceResult\" | \"imagePointMm\" | \"fieldThroughput\">;\n motion: Pick<CalculateSensorEnvironmentRadianceQueryInput,\n \"angularVelocityRadPerSec\" | \"timeReference\" | \"environmentDirectionConvention\">;\n temporalSampleCount: number;\n /** Optional ideal circular pupil, for geometric depth of field (DOF) and visibility. Cannot be combined with a sampled PSF. */\n pupil?: IdealCircularPupil;\n /**\n * Optional lens field mapping (added in 2.0): a projection model and radial or Brown-Conrady\n * distortion for the scene-ray query. Its distortion area factor is applied to irradiance exactly\n * once. If omitted, the ideal rectilinear, distortion-free path is unchanged. Cannot be combined\n * with `pupil`.\n */\n lensFieldMapping?: EnvironmentLensFieldMapping;\n /** Explicit attenuation across the field (such as vignetting), applied once before the PSF spreads the light. */\n fieldThroughput: { kind: \"unity\"; evidence: readonly EvidenceProvenance[]; limitation: string } | {\n kind: \"radial-illumination-vignetting\"; profile: IlluminationVignettingProfile;\n evidence: readonly EvidenceProvenance[]; limitation: string;\n };\n psf: { kind: \"not-applied\"; evidence: readonly EvidenceProvenance[]; limitation: string } | {\n kind: \"sampled-local\";\n configuration: Pick<CalculateSensorPsfIrradianceQuadratureInput, \"psf\" | \"psfWavelengthBasis\" | \"spatialModel\">;\n };\n evaluateRadiance: EnvironmentRadianceEvaluator;\n /** Required with `pupil`: rays start at different points on the aperture, so a direction-only function cannot be used. */\n evaluateApertureRadiance?: EnvironmentApertureRadianceEvaluator;\n}\nexport interface EnvironmentSensorPhotoSignal {\n sourceTargetProjectionCalculated: true;\n providerCallbackExecuted: true;\n providerTransportVerified: false;\n sceneVisibilityCalculated: false;\n productionPlanActivated: false;\n providerEvaluationCount: number;\n sourcePlane: \"sensor-package-incident\";\n fieldThroughputEvidence: readonly EvidenceProvenance[];\n fieldThroughputLimitation: string;\n psfOmission: { evidence: readonly EvidenceProvenance[]; limitation: string } | null;\n psfRedistributionApplied: boolean;\n pupilIntegrationApplied: boolean;\n pupilIntegrationProfile: IdealCircularPupil | null;\n photo: ReturnType<typeof createSensorEqeTemporalPhotoSignal>;\n instants: readonly {\n temporalSampleIndex: number;\n timeSecondsFromOpeningReference: number;\n psf: ReturnType<typeof calculateSensorPsfIrradianceQuadrature> | null;\n evaluations: readonly {\n query: ReturnType<typeof calculateSensorEnvironmentRadianceQuery>;\n result: SceneRadianceEvaluationResult;\n bindings: ReturnType<typeof validateSceneRadianceEvaluationBindings>;\n optics: ReturnType<typeof calculateSceneRadianceToSensorIrradiance>;\n apertureRay?: SensorApertureRay;\n apertureRequest?: SceneRadianceEvaluationRequest;\n }[];\n }[];\n}\n\n/** Internal plan, shared with the full-frame composer, so all work is bounded before any callback runs. */\ninterface EnvironmentPhotoPlan {\n owned: Omit<CalculateEnvironmentSensorPhotoSignalInput, \"evaluateRadiance\" | \"evaluateApertureRadiance\">;\n sceneBindings: CalculateSceneToSensorIrradianceQuadratureInput[\"sceneBindings\"];\n fieldThroughputEvidence: readonly EvidenceProvenance[];\n spatialQuadrature: ReturnType<typeof calculateSensorSpatialSamplingQuadrature>[\"value\"];\n spectralQuadrature: ReturnType<typeof calculateSensorSpectralQuadrature>[\"value\"];\n count: number;\n focus: CalculateSensorEnvironmentRadianceQueryInput[\"focus\"];\n instants: { temporalSampleIndex: number; time: number; groups: {\n node: CalculateSensorPsfIrradianceQuadratureInput[\"samples\"][number][\"node\"];\n taps: { kernelSampleX: number; kernelSampleY: number; point: { x: number; y: number };\n /** Deep-frozen provider request of the planned query (the same object as query.value.request). */\n request: SceneRadianceEvaluationRequest;\n /** The planned query's image-plane source point (the same object as query.value.sourcePointImagePlaneMm). */\n sourcePointImagePlaneMm: { x: number; y: number };\n /** The complete query result. Kept only by \"full\" plans, whose per-evaluation records include it. */\n query?: ReturnType<typeof calculateSensorEnvironmentRadianceQuery>;\n apertureRays?: readonly SensorApertureRay[];\n /** 1 / det(d distorted / d ideal), the change in area from distortion. Present only with a lens field mapping. */\n distortionAreaFactor?: number;\n fieldThroughput: CalculateSceneRadianceToSensorIrradianceInput[\"fieldThroughput\"] }[];\n }[] }[];\n}\n/**\n * Builds an internal execution plan covering all optical, spatial, spectral and temporal sampling,\n * without calling any provider code.\n *\n * Coverage is checked completely before any supplied radiance function runs. Each query gives an\n * explicit scene direction, a wavelength (air or vacuum) and seconds from the opening reference.\n * @param input - The photo-signal input without its radiance functions; see\n * `CalculateEnvironmentSensorPhotoSignalInput`.\n * @param ownership - Internal. \"copy\" (default) makes a structured clone of the input. \"engine-owned\"\n * uses the input as the plan's own data without cloning again. Only engine callers that already hold a\n * private structured clone, never shown to supplied code and never changed afterwards, may pass it.\n * @param retention - Internal. \"full\" (default) keeps every planned query result for \"full\" execution\n * records. \"photo-only\" keeps only each query's frozen request and image-plane point, which is all that\n * photo-only execution reads. Every query is still computed and validated at plan time, so limit checks\n * and errors are unchanged.\n * @returns The result. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function planEnvironmentSensorPhotoSignal(input: Omit<CalculateEnvironmentSensorPhotoSignalInput, \"evaluateRadiance\" | \"evaluateApertureRadiance\">,\n ownership: \"copy\" | \"engine-owned\" = \"copy\", retention: \"full\" | \"photo-only\" = \"full\"): EnvironmentPhotoPlan {\n const owned = ownership === \"engine-owned\" ? input : structuredClone(input);\n requirePublicOpaqueId(owned.temporalIntegrationId, \"Environment integration requires a public identity.\");\n if (!Number.isSafeInteger(owned.temporalSampleCount) || owned.temporalSampleCount < 1 || owned.temporalSampleCount > 256 ||\n owned.sensor.responseApplication.sourcePlane.value !== \"sensor-package-incident\" ||\n (owned.fieldThroughput?.kind !== \"unity\" && owned.fieldThroughput?.kind !== \"radial-illumination-vignetting\") || typeof owned.fieldThroughput.limitation !== \"string\" || !owned.fieldThroughput.limitation.trim()) {\n throw new InvalidScientificInputError(\"Environment exposure requires package-incident response, 1..256 midpoints and explicit supported field throughput.\");\n }\n if (owned.fieldThroughput.kind === \"radial-illumination-vignetting\" &&\n (typeof owned.fieldThroughput.profile !== \"object\" || owned.fieldThroughput.profile === null ||\n typeof owned.fieldThroughput.profile.coefficients !== \"object\" || owned.fieldThroughput.profile.coefficients === null)) {\n throw new InvalidScientificInputError(\"Radial throughput requires a declared profile and coefficients.\");\n }\n const fieldThroughputEvidence = parseEvidenceList(owned.fieldThroughput.evidence, \"environmentFieldThroughput.evidence\");\n const sceneBindings = { providerProfile: parseSceneRadianceProviderProfile(owned.sceneBindings.providerProfile),\n illuminationProfile: parseSceneIlluminationProfile(owned.sceneBindings.illuminationProfile),\n materialResponseProfile: parseSceneMaterialResponseProfile(owned.sceneBindings.materialResponseProfile),\n ...(owned.sceneBindings.illuminationTemporalProfile === undefined ? {} : {\n illuminationTemporalProfile: parseSceneIlluminationTemporalProfile(owned.sceneBindings.illuminationTemporalProfile) }) };\n if (sceneBindings.providerProfile.fidelity.spectral !== \"wavelength-resolved\") throw new InvalidScientificInputError(\"Environment exposure requires wavelength-resolved provider fidelity.\");\n const sensor = owned.sensor;\n const nativeRaster = sensor.localExposure.exposureWindowInput.nativeRaster;\n const binding = resolveNativeEffectiveRasterColorSamplingBinding({ nativeRaster, colorSamplingProfile: sensor.colorSamplingProfile,\n bindingProfile: sensor.localExposure.bindingProfile });\n if (binding.relationshipMeaning !== \"one-native-effective-sample-to-one-color-site\") throw new InvalidScientificInputError(\"Environment exposure requires explicit one-to-one native/color timing.\");\n const spatialQuadrature = calculateSensorSpatialSamplingQuadrature({ ...sensor.spatialSampling, nativeRaster,\n colorSamplingProfile: sensor.colorSamplingProfile, colorSamplingBindingProfile: sensor.localExposure.bindingProfile }).value;\n const spectralQuadrature = calculateSensorSpectralQuadrature({ ...sensor.spectralSampling,\n colorSamplingProfile: sensor.colorSamplingProfile, spectralResponseProfile: sensor.spectralResponseProfile }).value;\n if (spectralQuadrature.wavelengthBasis === \"unspecified\") throw new InvalidScientificInputError(\"Environment spectral basis must be explicit.\");\n const wavelengthBasis = spectralQuadrature.wavelengthBasis;\n const site = sensor.spatialSampling.site;\n const window = calculateCaptureExposureWindows({ ...sensor.localExposure.exposureWindowInput,\n samplePointsNative: [{ x: site.x+.5, y: site.y+.5 }] }).value.samples[0]!;\n const focus = owned.optics.focus.kind === \"infinity-focus\" ? { kind: \"infinity\" as const } :\n owned.optics.focus.kind === \"ideal-symmetric-thin-lens\" ? { kind: \"finite\" as const, distanceM: owned.optics.focus.objectDistanceM } :\n owned.optics.focus.focus.kind === \"infinity\" ? { kind: \"infinity\" as const } : { kind: \"finite\" as const, distanceM: owned.optics.focus.focus.objectDistanceM };\n if (owned.psf.kind === \"not-applied\") {\n parseEvidenceList(owned.psf.evidence, \"environmentPsfOmission.evidence\");\n if (typeof owned.psf.limitation !== \"string\" || !owned.psf.limitation.trim()) throw new InvalidScientificInputError(\"PSF omission needs an explicit limitation.\");\n } else if (owned.psf.kind !== \"sampled-local\") throw new InvalidConfigurationError(\"Unknown environment PSF selection.\");\n if (owned.psf.kind === \"sampled-local\") {\n const p = owned.psf.configuration.psf;\n // The profile belongs only to this plan, so its parse can be cached across nodes and time instants.\n markEngineOwnedLensSampledPsfProfile(p.profile);\n if (p.focalLengthMm !== owned.optics.focalLengthMm || p.apertureFNumber !== owned.optics.nominalFNumber ||\n p.focus.kind !== focus.kind || (p.focus.kind === \"finite\" && focus.kind === \"finite\" && p.focus.distanceM !== focus.distanceM)) {\n throw new InvalidScientificInputError(\"PSF and optical focal/aperture/focus state must match exactly.\");\n }\n }\n if (owned.pupil !== undefined && owned.psf.kind !== \"not-applied\") throw new InvalidScientificInputError(\"Ideal pupil integration cannot mix with a sampled PSF; geometric defocus would be ambiguous.\");\n const lensMapping = owned.lensFieldMapping === undefined ? undefined : prepareEnvironmentLensFieldMapping(owned.lensFieldMapping);\n if (lensMapping !== undefined && owned.pupil !== undefined) {\n throw new InvalidScientificInputError(\"Ideal pupil rays use the rectilinear distortion-free projection and cannot combine with a lens field mapping.\");\n }\n const pupilCount = owned.pupil === undefined ? 1 : calculateSensorApertureRays({sourcePointNativeSensorMm:{x:0,y:0},\n focalLengthMm:owned.optics.focalLengthMm,nominalFNumber:owned.optics.nominalFNumber,focus,pupil:owned.pupil,\n angularVelocityRadPerSec:owned.motion.angularVelocityRadPerSec,timeSecondsFromOpeningReference:window.startOffsetSecondsFromOpeningReference}).value.rays.length;\n let count = 0;\n const nodes = spectralQuadrature.nodes.flatMap(spectral => spatialQuadrature.nodes.map(spatial => {\n const node = { spatialNode: { antiAliasingComponentIndex: spatial.antiAliasingComponentIndex,\n apertureSampleXIndex: spatial.apertureSampleXIndex, apertureSampleYIndex: spatial.apertureSampleYIndex },\n spectralSampleIndex: spectral.spectralSampleIndex, wavelengthNanometers: spectral.wavelengthNanometers };\n const destination = spatial.preAntiAliasingSourcePointMm;\n const resolved = owned.psf.kind === \"sampled-local\" ? resolveLensSampledPsf({ ...owned.psf.configuration.psf,\n wavelengthNm: spectral.wavelengthNanometers, fieldPointMm: { x: destination.x, y: -destination.y } }) : null;\n const kernel = resolved?.value.kernel;\n const taps = kernel ? kernel.widthSamples*kernel.heightSamples : 1;\n count += taps*owned.temporalSampleCount*pupilCount;\n if (count > 100000) throw new InvalidScientificInputError(\"Environment exposure exceeds the 100000-provider-evaluation budget.\");\n const points = Array.from({ length: taps }, (_, i) => ({ kernelSampleX: kernel ? i%kernel.widthSamples : 0,\n kernelSampleY: kernel ? Math.floor(i/kernel.widthSamples) : 0,\n point: kernel ? sensorPsfSourcePoint(destination, kernel, i%kernel.widthSamples, Math.floor(i/kernel.widthSamples)) : { ...destination } }));\n return { node, points: points.map(p => ({ ...p, fieldThroughput: owned.fieldThroughput.kind === \"unity\"\n ? { kind: \"unity\" as const }\n : { kind: \"illumination-vignetting-result\" as const, result: calculateIlluminationVignetting({\n imagePointMm: { x: p.point.x, y: -p.point.y }, profile: owned.fieldThroughput.profile }).value } })) };\n }));\n const measure = window.localExposureDurationSeconds/owned.temporalSampleCount;\n let previous = -Infinity;\n const instants = Array.from({ length: owned.temporalSampleCount }, (_, temporalSampleIndex) => {\n const time = window.startOffsetSecondsFromOpeningReference+(temporalSampleIndex+.5)*measure;\n if (!(time > window.startOffsetSecondsFromOpeningReference && time > previous && time < window.endOffsetSecondsFromOpeningReference)) throw new InvalidScientificInputError(\"Local midpoints must be representable and strictly ordered.\");\n previous = time;\n const groups = nodes.map((n, ni) => ({ node: n.node, taps: n.points.map((p, pi) => {\n const apertureRays = owned.pupil === undefined ? undefined : calculateSensorApertureRays({sourcePointNativeSensorMm:p.point,\n focalLengthMm:owned.optics.focalLengthMm,nominalFNumber:owned.optics.nominalFNumber,focus,pupil:owned.pupil,\n angularVelocityRadPerSec:owned.motion.angularVelocityRadPerSec,timeSecondsFromOpeningReference:time}).value.rays;\n const queryInput: CalculateSensorEnvironmentRadianceQueryInput = { ...owned.motion, sourcePointNativeSensorMm: p.point,\n focalLengthMm: owned.optics.focalLengthMm, focus, timeSecondsFromOpeningReference: time,\n request: { schemaVersion: \"0.1.0\", sampleId: `${owned.temporalIntegrationId}-t${temporalSampleIndex}-n${ni}-p${pi}`,\n providerProfileId: sceneBindings.providerProfile.profileId, sceneId: sceneBindings.providerProfile.sceneId,\n illuminationProfileId: sceneBindings.illuminationProfile.profileId, materialResponseProfileId: sceneBindings.materialResponseProfile.profileId,\n wavelengthNanometers: n.node.wavelengthNanometers, wavelengthBasis } };\n const query = freezePlannedRequest(lensMapping === undefined ? calculateSensorEnvironmentRadianceQuery(queryInput)\n : calculateMappedSensorEnvironmentRadianceQuery(queryInput, lensMapping));\n const areaFactor = query.value.lensFieldMapping?.distortionAreaFactor;\n // Photo-only plans drop the query result (projection, diagnostics) once it is validated (a 2.0 memory saving).\n return { ...p, ...(apertureRays === undefined ? {} : { apertureRays }),\n ...(areaFactor === undefined ? {} : { distortionAreaFactor: areaFactor }), request: query.value.request,\n sourcePointImagePlaneMm: query.value.sourcePointImagePlaneMm, ...(retention === \"full\" ? { query } : {}) };\n }) }));\n // Validate PSF evidence/basis/support before supplied code is invoked.\n if (owned.psf.kind === \"sampled-local\") calculateSensorPsfIrradianceQuadrature({ ...owned.psf.configuration,\n spatialQuadrature, spectralQuadrature, timeSecondsFromOpeningReference: time,\n inputMeaning: \"pre-psf-pre-sensor-stack-pre-aa-spectral-irradiance\", samples: groups.map(g => ({ node: g.node,\n sourceSamples: g.taps.map(t => ({ kernelSampleX: t.kernelSampleX, kernelSampleY: t.kernelSampleY,\n sourcePointNativeSensorMm: t.point, timeSecondsFromOpeningReference: time, spectralIrradianceWattsPerSquareMeterPerNanometer: 0 })) })) });\n return { temporalSampleIndex, time, groups };\n });\n return { owned, sceneBindings, fieldThroughputEvidence, spatialQuadrature, spectralQuadrature, instants, count, focus };\n}\n\n/** Aperture queries already built for a batched provider, keyed by the planned ray; each is used once. */\nconst PREBUILT_APERTURE_QUERIES = new WeakMap<object, { base: object; request: SceneRadianceEvaluationRequest; apertureRay: SensorApertureRay }>();\n\n/**\n * Internal: the frozen provider request and frozen private ray copy of one ideal-pupil query (sample ID suffix \"-a\"\n * and the pupil index, reversed ray direction). If prebuildApertureRadianceQuery already built this query for a batch,\n * the same objects are returned once, so the batch and the executor share them; otherwise new ones are built.\n */\nexport function apertureRadianceQuery(base: SceneRadianceEvaluationRequest, ray: SensorApertureRay):\n { request: SceneRadianceEvaluationRequest; apertureRay: SensorApertureRay } {\n const prebuilt = PREBUILT_APERTURE_QUERIES.get(ray);\n if (prebuilt !== undefined && prebuilt.base === base) {\n PREBUILT_APERTURE_QUERIES.delete(ray);\n return { request: prebuilt.request, apertureRay: prebuilt.apertureRay };\n }\n return { request: freezeOwnedData({ ...base, sampleId: base.sampleId+\"-a\"+ray.pupilSampleIndex,\n target: { kind: \"environment-direction\" as const, outgoingDirectionUnitVector: {\n x: -ray.directionUnitVector.x, y: -ray.directionUnitVector.y, z: -ray.directionUnitVector.z } } }),\n apertureRay: freezeOwnedData(copyRay(ray)) };\n}\n/**\n * A private copy of an engine-computed ray: plain records of numbers, copied key by key in the same order, so it\n * equals what structuredClone returns for this data (a 2.0 performance change: structuredClone dominated pupil runs).\n */\nfunction copyRay(ray: SensorApertureRay): SensorApertureRay {\n const copy = (value: unknown): unknown => {\n if (typeof value !== \"object\" || value === null) return value;\n if (Array.isArray(value)) return value.map(copy);\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(value)) out[key] = copy((value as Record<string, unknown>)[key]);\n return out;\n };\n return copy(ray) as SensorApertureRay;\n}\n/** Internal: builds a pupil query for a batched provider and keeps it for the executor's apertureRadianceQuery call. */\nexport function prebuildApertureRadianceQuery(base: SceneRadianceEvaluationRequest, ray: SensorApertureRay):\n { request: SceneRadianceEvaluationRequest; apertureRay: SensorApertureRay } {\n PREBUILT_APERTURE_QUERIES.delete(ray);\n const query = apertureRadianceQuery(base, ray);\n PREBUILT_APERTURE_QUERIES.set(ray, { base, ...query });\n return query;\n}\n\n/** Deep-freezes a planned provider request once, so execution can hand the same immutable request to supplied code. */\nfunction freezePlannedRequest<T extends { value: { request: object } }>(query: T): T {\n freezeOwnedData(query.value.request);\n return query;\n}\n\n/**\n * Internal: runs a completed, bounded plan. On failure nothing partial is returned.\n * With retention \"full\" (default), every per-evaluation record is kept in value.instants[].evaluations.\n * With \"photo-only\", those lists are empty, for engine callers that read only value.photo. Photo values\n * are identical either way.\n */\nexport function executeEnvironmentSensorPhotoSignal(plan: ReturnType<typeof planEnvironmentSensorPhotoSignal>, evaluate: EnvironmentRadianceEvaluator, evaluateAperture?: EnvironmentApertureRadianceEvaluator,\n retention: \"full\" | \"photo-only\" = \"full\"): CalculationResult<EnvironmentSensorPhotoSignal> {\n if (typeof evaluate !== \"function\") throw new InvalidScientificInputError(\"Environment radiance evaluator must be synchronous callable code.\");\n const { owned } = plan;\n if (owned.pupil !== undefined && typeof evaluateAperture !== \"function\") throw new InvalidScientificInputError(\"Pupil integration requires an origin-aware aperture evaluator.\");\n if (retention === \"full\" && plan.instants.some(instant => instant.groups.some(group => group.taps.some(tap => tap.query === undefined)))) {\n throw new InvalidScientificInputError(\"Full environment execution records require a full plan.\");\n }\n const irradiance = [];\n const instants: EnvironmentSensorPhotoSignal[\"instants\"][number][] = [];\n for (const instant of plan.instants) {\n const evaluations: EnvironmentSensorPhotoSignal[\"instants\"][number][\"evaluations\"][number][] = [];\n const samples = instant.groups.map(group => ({ node: group.node, sourceSamples: group.taps.map(tap => {\n let integratedIrradiance = 0;\n const support: readonly (SensorApertureRay | undefined)[] = tap.apertureRays ?? [undefined];\n for (const ray of support) {\n // Planned requests are deep-frozen once at plan time (freezePlannedRequest); no per-evaluation clone.\n const query = ray === undefined ? undefined : apertureRadianceQuery(tap.request, ray);\n const request = query === undefined ? tap.request : query.request, apertureRay = query?.apertureRay;\n const result = parseSceneRadianceEvaluationResult(apertureRay === undefined ? evaluate(request) : evaluateAperture!(request,apertureRay));\n const bindings = validateSceneRadianceEvaluationBindings({ ...plan.sceneBindings, request, result });\n const optics = calculateSceneRadianceToSensorIrradiance({ ...owned.optics, sceneRadianceRequest: request,\n sceneRadianceResult: result, imagePointMm: tap.sourcePointImagePlaneMm, fieldThroughput: tap.fieldThroughput });\n if (retention === \"full\") evaluations.push({ query: tap.query!, result, bindings, optics,\n ...(apertureRay === undefined ? {} : {apertureRay,apertureRequest:request}) });\n integratedIrradiance += optics.value.sensorPlaneSpectralIrradianceWattsPerSquareMeterNanometer*(ray?.weight ?? 1);\n }\n if (tap.distortionAreaFactor !== undefined) {\n // Distortion squeezes or stretches the ideal image. Flux is conserved, so irradiance scales by 1/det, once.\n integratedIrradiance *= tap.distortionAreaFactor;\n }\n return { kernelSampleX: tap.kernelSampleX, kernelSampleY: tap.kernelSampleY, sourcePointNativeSensorMm: tap.point,\n timeSecondsFromOpeningReference: instant.time, spectralIrradianceWattsPerSquareMeterPerNanometer: integratedIrradiance };\n }) }));\n const psf = owned.psf.kind === \"sampled-local\" ? calculateSensorPsfIrradianceQuadrature({ ...owned.psf.configuration,\n spatialQuadrature: plan.spatialQuadrature, spectralQuadrature: plan.spectralQuadrature,\n timeSecondsFromOpeningReference: instant.time, inputMeaning: \"pre-psf-pre-sensor-stack-pre-aa-spectral-irradiance\", samples }) : null;\n irradiance.push({ temporalSampleIndex: instant.temporalSampleIndex, timeSecondsFromOpeningReference: instant.time,\n irradianceSamples: psf ? psf.value.irradianceSamples : samples.map(s => ({ node: s.node,\n spectralIrradianceWattsPerSquareMeterPerNanometer: s.sourceSamples[0]!.spectralIrradianceWattsPerSquareMeterPerNanometer })) });\n instants.push({ temporalSampleIndex: instant.temporalSampleIndex, timeSecondsFromOpeningReference: instant.time, psf, evaluations });\n }\n const photo = createSensorEqeTemporalPhotoSignal({ temporalIntegrationId: owned.temporalIntegrationId,\n exposure: { ...owned.sensor, samples: irradiance } });\n return approximationResult({ sourceTargetProjectionCalculated: true, providerCallbackExecuted: true,\n providerTransportVerified: false, sceneVisibilityCalculated: false, productionPlanActivated: false,\n providerEvaluationCount: plan.count, sourcePlane: \"sensor-package-incident\", fieldThroughputEvidence: plan.fieldThroughputEvidence,\n fieldThroughputLimitation: owned.fieldThroughput.limitation,\n psfOmission: owned.psf.kind === \"not-applied\" ? { evidence: parseEvidenceList(owned.psf.evidence, \"psfOmission.evidence\"), limitation: owned.psf.limitation } : null,\n psfRedistributionApplied: owned.psf.kind === \"sampled-local\", pupilIntegrationApplied: owned.pupil !== undefined, pupilIntegrationProfile: owned.pupil ?? null, photo, instants\n }, \"environment-sensor-photo-signal\", owned.lensFieldMapping === undefined ? \"0.3.0\" : \"0.4.0\", [\n \"Supplied synchronous provider code was invoked at each generated environment query; physical transport and visibility are not verified.\",\n \"Ideal focus-aware rotation/projection, explicit evidence-bound field throughput and optional destination-local sampled PSF are bounded approximations.\",\n \"Optional ideal pupil rays use normalized radiance averaging; source intersections remain provider-owned and convergence is unverified.\",\n \"PSF pupil throughput is not applied; child uncertainty, finite-support and quadrature convergence are not combined or established.\",\n ...(owned.lensFieldMapping === undefined ? [] : [\n \"Lens field mapping: rays use the declared projection after inverse distortion; irradiance is multiplied once by the distortion area factor 1/det(d distorted/d ideal); field throughput and sampled PSFs remain evaluated at the physical sensor point.\"\n ])\n ]);\n}\n\n/**\n * Internal (#453; not exported from the package): the photo signal of {@link executeEnvironmentSensorPhotoSignal} with\n * retention \"photo-only\" (its `value.photo`), for a plan of engine-built records (planned as \"engine-owned\" from a record the\n * engine built, never shown to supplied code and never changed). It is the same chain with the same operations: every query of\n * the plan calls `evaluate` once with the same frozen request, in the same order; each result is parsed and its bindings and\n * identity checked; the optical bridge forms each irradiance with its own code; the taps, nodes and instants are assembled as\n * the full executor does; and `createSensorEqeTemporalPhotoSignal` forms the photo signal from the same input. So the photo\n * signal is the full executor's, value for value.\n *\n * What it skips is only re-validation of records that cannot have changed since they passed it, and records nobody reads:\n * - the first query runs the full per-query steps (full result parse, `validateSceneRadianceEvaluationBindings`,\n * `calculateSceneRadianceToSensorIrradiance`); later queries skip the profile-to-profile binding checks and the optical\n * bridge's re-parse of its unchanged optical inputs, request and result, keeping every per-query check\n * (`validateSceneRadianceRequestResultBindings`, `prepareSceneRadianceToSensorIrradianceValue`), with the same messages;\n * - with `engineBuiltResults` (an evaluator marked by `markInternalSceneRadianceEvaluator`, or one returning such an\n * evaluator's results), later results reuse the first result's parsed uncertainty, evidence and limitations when they are\n * the same deep-frozen objects (`parseSceneRadianceEvaluationResultWithValidatedRecords`, as the lean executor does);\n * - no per-query optical result envelopes and no outer execution envelope are built.\n * Plans with ideal-pupil rays or a sampled PSF run the full executor.\n */\nexport function executeEngineOwnedEnvironmentPhotoSignal(plan: ReturnType<typeof planEnvironmentSensorPhotoSignal>, evaluate: EnvironmentRadianceEvaluator,\n engineBuiltResults: boolean): EnvironmentSensorPhotoSignal[\"photo\"] {\n const { owned } = plan;\n if (owned.pupil !== undefined || owned.psf.kind !== \"not-applied\") return executeEnvironmentSensorPhotoSignal(plan, evaluate, undefined, \"photo-only\").value.photo;\n if (typeof evaluate !== \"function\") throw new InvalidScientificInputError(\"Environment radiance evaluator must be synchronous callable code.\");\n const { providerProfile, illuminationProfile, materialResponseProfile } = plan.sceneBindings;\n let validated: ValidatedSceneRadianceRecords | undefined;\n let irradianceOf: ReturnType<typeof prepareSceneRadianceToSensorIrradianceValue> | undefined;\n const irradiance = [];\n for (const instant of plan.instants) {\n const samples = instant.groups.map(group => ({ node: group.node, sourceSamples: group.taps.map(tap => {\n let integratedIrradiance = 0;\n const request = tap.request, value = evaluate(request);\n let sensorPlane: number;\n if (irradianceOf === undefined) {\n const result = parseSceneRadianceEvaluationResult(value);\n validateSceneRadianceEvaluationBindings({ ...plan.sceneBindings, request, result });\n sensorPlane = calculateSceneRadianceToSensorIrradiance({ ...owned.optics, sceneRadianceRequest: request,\n sceneRadianceResult: result, imagePointMm: tap.sourcePointImagePlaneMm, fieldThroughput: tap.fieldThroughput }).value.sensorPlaneSpectralIrradianceWattsPerSquareMeterNanometer;\n const record = value as unknown as Record<string, unknown>;\n if (engineBuiltResults && isDeeplyFrozen(record.uncertainty) && isDeeplyFrozen(record.evidence) && isDeeplyFrozen(record.limitations)) {\n validated = { uncertainty: record.uncertainty, evidence: record.evidence, limitations: record.limitations, parsed: result };\n }\n irradianceOf = prepareSceneRadianceToSensorIrradianceValue(owned.optics);\n } else {\n const result = validated ? parseSceneRadianceEvaluationResultWithValidatedRecords(value, validated) : parseSceneRadianceEvaluationResult(value);\n validateSceneRadianceRequestResultBindings(providerProfile, illuminationProfile, materialResponseProfile, request, result);\n sensorPlane = irradianceOf(request, result, tap.sourcePointImagePlaneMm, tap.fieldThroughput);\n }\n // The full executor's sum over the tap's support (one direction, weight 1 without pupil rays).\n integratedIrradiance += sensorPlane*1;\n if (tap.distortionAreaFactor !== undefined) integratedIrradiance *= tap.distortionAreaFactor;\n return { kernelSampleX: tap.kernelSampleX, kernelSampleY: tap.kernelSampleY, sourcePointNativeSensorMm: tap.point,\n timeSecondsFromOpeningReference: instant.time, spectralIrradianceWattsPerSquareMeterPerNanometer: integratedIrradiance };\n }) }));\n irradiance.push({ temporalSampleIndex: instant.temporalSampleIndex, timeSecondsFromOpeningReference: instant.time,\n irradianceSamples: samples.map(s => ({ node: s.node,\n spectralIrradianceWattsPerSquareMeterPerNanometer: s.sourceSamples[0]!.spectralIrradianceWattsPerSquareMeterPerNanometer })) });\n }\n return createSensorEqeTemporalPhotoSignal({ temporalIntegrationId: owned.temporalIntegrationId,\n exposure: { ...owned.sensor, samples: irradiance } });\n}\n\n/** Plans, queries the provider and integrates the photo signal for one site, at the site's own midpoint sample positions. */\nexport function calculateEnvironmentSensorPhotoSignal(input: CalculateEnvironmentSensorPhotoSignalInput): CalculationResult<EnvironmentSensorPhotoSignal> {\n requireInputKeys(input, \"calculateEnvironmentSensorPhotoSignal\", [\"temporalIntegrationId\", \"sensor\", \"sceneBindings\", \"optics\", \"motion\", \"temporalSampleCount\", \"pupil\", \"lensFieldMapping\", \"fieldThroughput\", \"psf\", \"evaluateRadiance\", \"evaluateApertureRadiance\"]);\n const { evaluateRadiance, evaluateApertureRadiance, ...data } = input;\n return executeEnvironmentSensorPhotoSignal(planEnvironmentSensorPhotoSignal(data), evaluateRadiance, evaluateApertureRadiance);\n}\n"]}
@@ -25,6 +25,18 @@ export interface DeterministicUniformStream {
25
25
  * Changing any of this changes simulated RAW output and needs a model and replay review.
26
26
  */
27
27
  export declare function createDeterministicUniformStream(seedUint32: number): DeterministicUniformStream;
28
+ /** A uniform stream reseeded in place (internal, #418). */
29
+ export interface ResettableUniformStream extends DeterministicUniformStream {
30
+ /** Sets the state createDeterministicUniformStream(seedUint32) starts from, so the draws that follow are that stream's. */
31
+ reset(seedUint32: number): void;
32
+ }
33
+ /**
34
+ * The stream of createDeterministicUniformStream, reseeded in place instead of created anew (internal, #418). The seeding and
35
+ * the xorshift32 step are the same statements, so after `reset(seed)` every draw equals that of
36
+ * `createDeterministicUniformStream(seed)`. It saves a typed array and a closure per stream where a hot loop needs two
37
+ * fresh streams per site.
38
+ */
39
+ export declare function createResettableUniformStream(): ResettableUniformStream;
28
40
  /**
29
41
  * Box–Muller transform of two uniform values in (0,1) (Box & Muller 1958). Each call uses two values
30
42
  * and returns one standard normal sample. The second possible sample is not cached, so the order of
@@ -88,6 +88,31 @@ export function createDeterministicUniformStream(seedUint32) {
88
88
  }
89
89
  };
90
90
  }
91
+ /**
92
+ * The stream of createDeterministicUniformStream, reseeded in place instead of created anew (internal, #418). The seeding and
93
+ * the xorshift32 step are the same statements, so after `reset(seed)` every draw equals that of
94
+ * `createDeterministicUniformStream(seed)`. It saves a typed array and a closure per stream where a hot loop needs two
95
+ * fresh streams per site.
96
+ */
97
+ export function createResettableUniformStream() {
98
+ const state = new Uint32Array(1);
99
+ return {
100
+ reset(seedUint32) {
101
+ state[0] = fmix32((seedUint32 ^ 0x9e3779b9) >>> 0);
102
+ if (state[0] === 0) {
103
+ state[0] = 0x6d2b79f5;
104
+ }
105
+ },
106
+ next() {
107
+ let x = state[0];
108
+ x ^= x << 13;
109
+ x ^= x >>> 17;
110
+ x ^= x << 5;
111
+ state[0] = x;
112
+ return (state[0] + 0.5) / 0x1_0000_0000;
113
+ }
114
+ };
115
+ }
91
116
  /**
92
117
  * Box–Muller transform of two uniform values in (0,1) (Box & Muller 1958). Each call uses two values
93
118
  * and returns one standard normal sample. The second possible sample is not cached, so the order of
@@ -1 +1 @@
1
- {"version":3,"file":"stochastic-seed.js","sourceRoot":"","sources":["../../src/sensor/stochastic-seed.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Seeds for the random-number streams behind simulated sensor noise. The same inputs always give\n * the same noise.\n *\n * Each per-site stream seed is a strong 32-bit hash of the capture seed, the absolute native\n * row-major site index and a fixed stream ID. The hash is MurmurHash3_x86_32 (Austin Appleby,\n * placed in the public domain) of the 12-byte little-endian message `[captureSeed, siteIndex,\n * streamId]` with hash seed 0. Its avalanche finalizer (`fmix32`) makes a one-bit input change flip\n * each output bit with probability close to 1/2. So neighbouring sites, the two streams of one site\n * and captures with nearby seeds get unrelated seeds. A linear schedule such as `seed + 2*index` fed\n * into a linear generator (xorshift) lacks this property, and produced correlated noise with too\n * little variance (issue #270).\n *\n * This is deterministic and not cryptographic; it does not claim unpredictability. The moment and\n * correlation tests, not this hash alone, show that the resulting noise is statistically acceptable.\n */\n\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport { portableCosTwoPi, portableLog, portableSinTwoPi } from \"../core/portable-math.js\";\n\n/** Stream IDs. Changing them changes every simulated RAW code. */\nexport const NATIVE_SITE_CHARGE_STREAM_ID = 0 as const;\nexport const NATIVE_SITE_READ_NOISE_STREAM_ID = 1 as const;\n\nconst MURMUR3_C1 = 0xcc9e2d51;\nconst MURMUR3_C2 = 0x1b873593;\n\n/** MurmurHash3 32-bit avalanche finalizer; a bijection on uint32 with fmix32(0) = 0. */\nexport function fmix32(value: number): number {\n let h = value >>> 0;\n h ^= h >>> 16;\n h = Math.imul(h, 0x85ebca6b);\n h ^= h >>> 13;\n h = Math.imul(h, 0xc2b2ae35);\n h ^= h >>> 16;\n return h >>> 0;\n}\n\nfunction mixBlock(hash: number, block: number): number {\n let k = Math.imul(block >>> 0, MURMUR3_C1);\n k = (k << 15) | (k >>> 17);\n k = Math.imul(k, MURMUR3_C2);\n let h = (hash ^ k) >>> 0;\n h = (h << 13) | (h >>> 19);\n return (Math.imul(h, 5) + 0xe6546b64) >>> 0;\n}\n\nfunction isUint32(value: number): boolean {\n return Number.isInteger(value) && value >= 0 && value <= 0xffffffff;\n}\n\n/**\n * Returns the uint32 seed of one random stream for one absolute native site.\n * All inputs must be uint32 integers. The site index is the full-frame native row-major\n * index, never relative to a tile or chunk, so how the work is split cannot change it.\n */\nexport function deriveNativeSiteStreamSeedUint32(\n captureSeedUint32: number,\n nativeSiteIndex: number,\n streamId: number\n): number {\n if (!isUint32(captureSeedUint32) || !isUint32(nativeSiteIndex) || !isUint32(streamId)) {\n throw new InvalidScientificInputError(\"Stochastic stream seed inputs must be uint32 integers.\");\n }\n let h = 0;\n h = mixBlock(h, captureSeedUint32);\n h = mixBlock(h, nativeSiteIndex);\n h = mixBlock(h, streamId);\n return fmix32(h ^ 12);\n}\n\n/** One deterministic stream of uniforms strictly inside (0,1). */\nexport interface DeterministicUniformStream {\n next(): number;\n}\n\n/**\n * Creates the deterministic, non-cryptographic xorshift32 random stream for one charge, read-noise\n * or fixed-pattern identity.\n *\n * The seed is first scrambled by the MurmurHash3 `fmix32` avalanche finalizer. xorshift32 is linear\n * over GF(2), so an unscrambled seed would give nearby seeds almost identical first draws, and noise\n * that is correlated across the sensor with too little variance (issue #270). The nonzero fallback\n * avoids the zero state, which xorshift can never leave. Adding 0.5 before dividing gives values\n * strictly inside (0,1), so the logarithms in the Poisson and normal transforms never see 0 or 1.\n * Changing any of this changes simulated RAW output and needs a model and replay review.\n */\nexport function createDeterministicUniformStream(seedUint32: number): DeterministicUniformStream {\n // The state lives in a Uint32Array: storing the int32 result there is the same `>>> 0` reduction, and a uint32 above\n // 2^30 kept in a closure variable would be boxed on every step (half the speed). The draws are unchanged.\n const state = new Uint32Array(1);\n state[0] = fmix32((seedUint32 ^ 0x9e3779b9) >>> 0);\n if (state[0] === 0) {\n state[0] = 0x6d2b79f5;\n }\n return {\n next(): number {\n let x = state[0]!;\n x ^= x << 13;\n x ^= x >>> 17;\n x ^= x << 5;\n state[0] = x;\n return (state[0] + 0.5) / 0x1_0000_0000;\n }\n };\n}\n\n/**\n * Box–Muller transform of two uniform values in (0,1) (Box & Muller 1958). Each call uses two values\n * and returns one standard normal sample. The second possible sample is not cached, so the order of\n * draws from the stream never shifts.\n */\nexport function sampleDeterministicStandardNormal(random: DeterministicUniformStream): number {\n const u1 = random.next();\n const u2 = random.next();\n return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n}\n\n/**\n * The same Box–Muller draw (same two uniforms, same order) with the engine's portable logarithm and cosine\n * (`portableLog`, `portableCosTwoPi`), so the sample has the same bits in every JavaScript engine. Used by the\n * display view; the native noise keeps `sampleDeterministicStandardNormal`.\n */\nexport function samplePortableStandardNormal(random: DeterministicUniformStream): number {\n const u1 = random.next();\n const u2 = random.next();\n return Math.sqrt(-2 * portableLog(u1)) * portableCosTwoPi(u2);\n}\n\n/**\n * Standard normal samples from one stream, two per pair of uniforms: both Box–Muller outputs, r·cos(2πu₂) and then\n * r·sin(2πu₂) with r = √(−2 ln u₁) (Box & Muller 1958; the two are independent standard normals), with the portable\n * functions. Each call returns the next sample; the stream advances by two uniforms on every other call. Used by the\n * display view's AHD correction states, which draw many normals from one stream.\n */\nexport function createPortableStandardNormalPairs(random: DeterministicUniformStream): () => number {\n let spare = 0, hasSpare = false;\n return () => {\n if (hasSpare) { hasSpare = false; return spare; }\n const u1 = random.next(), u2 = random.next(), r = Math.sqrt(-2 * portableLog(u1));\n spare = r * portableSinTwoPi(u2); hasSpare = true;\n return r * portableCosTwoPi(u2);\n };\n}\n\n/**\n * Fixed-pattern stream IDs. They seed spatial patterns that belong to one physical sensor, never\n * per-capture noise, and are hashed with the sensor-instance seed instead of the capture seed.\n */\nexport const SENSOR_FIXED_PATTERN_PRNU_STREAM_ID = 16 as const;\nexport const SENSOR_FIXED_PATTERN_DSNU_STREAM_ID = 17 as const;\nexport const SENSOR_FIXED_PATTERN_HOT_PIXEL_STREAM_ID = 18 as const;\nexport const SENSOR_FIXED_PATTERN_ROW_STREAM_ID = 19 as const;\nexport const SENSOR_FIXED_PATTERN_COLUMN_STREAM_ID = 20 as const;\n\n/**\n * Returns the uint32 seed of one fixed-pattern stream for one physical colour-sampling site: the\n * MurmurHash3_x86_32 hash (hash seed 0) of the 16-byte little-endian message\n * `[sensorInstanceSeed, siteX, siteY, streamId]`. Site coordinates are absolute indices in the\n * colour-sampling grid, so the pattern belongs to the physical sensor and does not change with crop,\n * tiling or the per-capture seed. Deterministic and not cryptographic.\n */\nexport function deriveSensorFixedPatternSeedUint32(\n sensorInstanceSeedUint32: number,\n siteX: number,\n siteY: number,\n streamId: number\n): number {\n if (!isUint32(sensorInstanceSeedUint32) || !isUint32(siteX) || !isUint32(siteY) || !isUint32(streamId)) {\n throw new InvalidScientificInputError(\"Fixed-pattern seed inputs must be uint32 integers.\");\n }\n let h = 0;\n h = mixBlock(h, sensorInstanceSeedUint32);\n h = mixBlock(h, siteX);\n h = mixBlock(h, siteY);\n h = mixBlock(h, streamId);\n return fmix32(h ^ 16);\n}\n"]}
1
+ {"version":3,"file":"stochastic-seed.js","sourceRoot":"","sources":["../../src/sensor/stochastic-seed.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Seeds for the random-number streams behind simulated sensor noise. The same inputs always give\n * the same noise.\n *\n * Each per-site stream seed is a strong 32-bit hash of the capture seed, the absolute native\n * row-major site index and a fixed stream ID. The hash is MurmurHash3_x86_32 (Austin Appleby,\n * placed in the public domain) of the 12-byte little-endian message `[captureSeed, siteIndex,\n * streamId]` with hash seed 0. Its avalanche finalizer (`fmix32`) makes a one-bit input change flip\n * each output bit with probability close to 1/2. So neighbouring sites, the two streams of one site\n * and captures with nearby seeds get unrelated seeds. A linear schedule such as `seed + 2*index` fed\n * into a linear generator (xorshift) lacks this property, and produced correlated noise with too\n * little variance (issue #270).\n *\n * This is deterministic and not cryptographic; it does not claim unpredictability. The moment and\n * correlation tests, not this hash alone, show that the resulting noise is statistically acceptable.\n */\n\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport { portableCosTwoPi, portableLog, portableSinTwoPi } from \"../core/portable-math.js\";\n\n/** Stream IDs. Changing them changes every simulated RAW code. */\nexport const NATIVE_SITE_CHARGE_STREAM_ID = 0 as const;\nexport const NATIVE_SITE_READ_NOISE_STREAM_ID = 1 as const;\n\nconst MURMUR3_C1 = 0xcc9e2d51;\nconst MURMUR3_C2 = 0x1b873593;\n\n/** MurmurHash3 32-bit avalanche finalizer; a bijection on uint32 with fmix32(0) = 0. */\nexport function fmix32(value: number): number {\n let h = value >>> 0;\n h ^= h >>> 16;\n h = Math.imul(h, 0x85ebca6b);\n h ^= h >>> 13;\n h = Math.imul(h, 0xc2b2ae35);\n h ^= h >>> 16;\n return h >>> 0;\n}\n\nfunction mixBlock(hash: number, block: number): number {\n let k = Math.imul(block >>> 0, MURMUR3_C1);\n k = (k << 15) | (k >>> 17);\n k = Math.imul(k, MURMUR3_C2);\n let h = (hash ^ k) >>> 0;\n h = (h << 13) | (h >>> 19);\n return (Math.imul(h, 5) + 0xe6546b64) >>> 0;\n}\n\nfunction isUint32(value: number): boolean {\n return Number.isInteger(value) && value >= 0 && value <= 0xffffffff;\n}\n\n/**\n * Returns the uint32 seed of one random stream for one absolute native site.\n * All inputs must be uint32 integers. The site index is the full-frame native row-major\n * index, never relative to a tile or chunk, so how the work is split cannot change it.\n */\nexport function deriveNativeSiteStreamSeedUint32(\n captureSeedUint32: number,\n nativeSiteIndex: number,\n streamId: number\n): number {\n if (!isUint32(captureSeedUint32) || !isUint32(nativeSiteIndex) || !isUint32(streamId)) {\n throw new InvalidScientificInputError(\"Stochastic stream seed inputs must be uint32 integers.\");\n }\n let h = 0;\n h = mixBlock(h, captureSeedUint32);\n h = mixBlock(h, nativeSiteIndex);\n h = mixBlock(h, streamId);\n return fmix32(h ^ 12);\n}\n\n/** One deterministic stream of uniforms strictly inside (0,1). */\nexport interface DeterministicUniformStream {\n next(): number;\n}\n\n/**\n * Creates the deterministic, non-cryptographic xorshift32 random stream for one charge, read-noise\n * or fixed-pattern identity.\n *\n * The seed is first scrambled by the MurmurHash3 `fmix32` avalanche finalizer. xorshift32 is linear\n * over GF(2), so an unscrambled seed would give nearby seeds almost identical first draws, and noise\n * that is correlated across the sensor with too little variance (issue #270). The nonzero fallback\n * avoids the zero state, which xorshift can never leave. Adding 0.5 before dividing gives values\n * strictly inside (0,1), so the logarithms in the Poisson and normal transforms never see 0 or 1.\n * Changing any of this changes simulated RAW output and needs a model and replay review.\n */\nexport function createDeterministicUniformStream(seedUint32: number): DeterministicUniformStream {\n // The state lives in a Uint32Array: storing the int32 result there is the same `>>> 0` reduction, and a uint32 above\n // 2^30 kept in a closure variable would be boxed on every step (half the speed). The draws are unchanged.\n const state = new Uint32Array(1);\n state[0] = fmix32((seedUint32 ^ 0x9e3779b9) >>> 0);\n if (state[0] === 0) {\n state[0] = 0x6d2b79f5;\n }\n return {\n next(): number {\n let x = state[0]!;\n x ^= x << 13;\n x ^= x >>> 17;\n x ^= x << 5;\n state[0] = x;\n return (state[0] + 0.5) / 0x1_0000_0000;\n }\n };\n}\n\n/** A uniform stream reseeded in place (internal, #418). */\nexport interface ResettableUniformStream extends DeterministicUniformStream {\n /** Sets the state createDeterministicUniformStream(seedUint32) starts from, so the draws that follow are that stream's. */\n reset(seedUint32: number): void;\n}\n\n/**\n * The stream of createDeterministicUniformStream, reseeded in place instead of created anew (internal, #418). The seeding and\n * the xorshift32 step are the same statements, so after `reset(seed)` every draw equals that of\n * `createDeterministicUniformStream(seed)`. It saves a typed array and a closure per stream where a hot loop needs two\n * fresh streams per site.\n */\nexport function createResettableUniformStream(): ResettableUniformStream {\n const state = new Uint32Array(1);\n return {\n reset(seedUint32: number): void {\n state[0] = fmix32((seedUint32 ^ 0x9e3779b9) >>> 0);\n if (state[0] === 0) {\n state[0] = 0x6d2b79f5;\n }\n },\n next(): number {\n let x = state[0]!;\n x ^= x << 13;\n x ^= x >>> 17;\n x ^= x << 5;\n state[0] = x;\n return (state[0] + 0.5) / 0x1_0000_0000;\n }\n };\n}\n\n/**\n * Box–Muller transform of two uniform values in (0,1) (Box & Muller 1958). Each call uses two values\n * and returns one standard normal sample. The second possible sample is not cached, so the order of\n * draws from the stream never shifts.\n */\nexport function sampleDeterministicStandardNormal(random: DeterministicUniformStream): number {\n const u1 = random.next();\n const u2 = random.next();\n return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n}\n\n/**\n * The same Box–Muller draw (same two uniforms, same order) with the engine's portable logarithm and cosine\n * (`portableLog`, `portableCosTwoPi`), so the sample has the same bits in every JavaScript engine. Used by the\n * display view; the native noise keeps `sampleDeterministicStandardNormal`.\n */\nexport function samplePortableStandardNormal(random: DeterministicUniformStream): number {\n const u1 = random.next();\n const u2 = random.next();\n return Math.sqrt(-2 * portableLog(u1)) * portableCosTwoPi(u2);\n}\n\n/**\n * Standard normal samples from one stream, two per pair of uniforms: both Box–Muller outputs, r·cos(2πu₂) and then\n * r·sin(2πu₂) with r = √(−2 ln u₁) (Box & Muller 1958; the two are independent standard normals), with the portable\n * functions. Each call returns the next sample; the stream advances by two uniforms on every other call. Used by the\n * display view's AHD correction states, which draw many normals from one stream.\n */\nexport function createPortableStandardNormalPairs(random: DeterministicUniformStream): () => number {\n let spare = 0, hasSpare = false;\n return () => {\n if (hasSpare) { hasSpare = false; return spare; }\n const u1 = random.next(), u2 = random.next(), r = Math.sqrt(-2 * portableLog(u1));\n spare = r * portableSinTwoPi(u2); hasSpare = true;\n return r * portableCosTwoPi(u2);\n };\n}\n\n/**\n * Fixed-pattern stream IDs. They seed spatial patterns that belong to one physical sensor, never\n * per-capture noise, and are hashed with the sensor-instance seed instead of the capture seed.\n */\nexport const SENSOR_FIXED_PATTERN_PRNU_STREAM_ID = 16 as const;\nexport const SENSOR_FIXED_PATTERN_DSNU_STREAM_ID = 17 as const;\nexport const SENSOR_FIXED_PATTERN_HOT_PIXEL_STREAM_ID = 18 as const;\nexport const SENSOR_FIXED_PATTERN_ROW_STREAM_ID = 19 as const;\nexport const SENSOR_FIXED_PATTERN_COLUMN_STREAM_ID = 20 as const;\n\n/**\n * Returns the uint32 seed of one fixed-pattern stream for one physical colour-sampling site: the\n * MurmurHash3_x86_32 hash (hash seed 0) of the 16-byte little-endian message\n * `[sensorInstanceSeed, siteX, siteY, streamId]`. Site coordinates are absolute indices in the\n * colour-sampling grid, so the pattern belongs to the physical sensor and does not change with crop,\n * tiling or the per-capture seed. Deterministic and not cryptographic.\n */\nexport function deriveSensorFixedPatternSeedUint32(\n sensorInstanceSeedUint32: number,\n siteX: number,\n siteY: number,\n streamId: number\n): number {\n if (!isUint32(sensorInstanceSeedUint32) || !isUint32(siteX) || !isUint32(siteY) || !isUint32(streamId)) {\n throw new InvalidScientificInputError(\"Fixed-pattern seed inputs must be uint32 integers.\");\n }\n let h = 0;\n h = mixBlock(h, sensorInstanceSeedUint32);\n h = mixBlock(h, siteX);\n h = mixBlock(h, siteY);\n h = mixBlock(h, streamId);\n return fmix32(h ^ 16);\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  # Public API reference
2
2
 
3
- Generated from the exact root exports for **@photivra/engine 2.8.1 / root contract 2.8.1** by `node scripts/generate-api-reference.mjs`. No module deep import is supported.
3
+ Generated from the exact root exports for **@photivra/engine 2.10.0 / root contract 2.10.0** by `node scripts/generate-api-reference.mjs`. No module deep import is supported.
4
4
 
5
5
  Use [the developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md) for executable paths and scientific boundaries. Types are contracts, not runtime validation: parse untrusted data, preserve units/reference frames, read assessment blockers, and retain evidence/limitations. Optional properties do not imply a universal default. Model/schema IDs remain independent of the distribution.
6
6
 
@@ -126,6 +126,7 @@ Start here to find a function by task. [Using Photivra from an agent](AGENT_GUID
126
126
  | Export | Kind |
127
127
  | --- | --- |
128
128
  | [`EXPORT_JPEG_ENCODER_ID`](api/capture-export-jpeg.md#export_jpeg_encoder_id) | Runtime |
129
+ | [`JpegStripBits`](api/capture-export-jpeg.md#jpegstripbits) | Type |
129
130
 
130
131
  ## capture/focus-control.ts
131
132
 
@@ -1156,6 +1157,17 @@ Start here to find a function by task. [Using Photivra from an agent](AGENT_GUID
1156
1157
  | [`UniformChartScene`](api/facade-chart-scenes.md#uniformchartscene) | Type |
1157
1158
  | [`ZonePlateChartScene`](api/facade-chart-scenes.md#zoneplatechartscene) | Type |
1158
1159
 
1160
+ ## facade/display-view.ts
1161
+
1162
+ [Detailed contracts](api/facade-display-view.md) · [Source](https://github.com/Photivra/photivra/blob/main/src/facade/display-view.ts)
1163
+
1164
+ | Export | Kind |
1165
+ | --- | --- |
1166
+ | [`bindDisplayViewCorrectionStates`](api/facade-display-view.md#binddisplayviewcorrectionstates) | Runtime |
1167
+ | [`DisplayViewCorrectionStateKeys`](api/facade-display-view.md#displayviewcorrectionstatekeys) | Type |
1168
+ | [`DisplayViewCorrectionStateSet`](api/facade-display-view.md#displayviewcorrectionstateset) | Type |
1169
+ | [`serializeDisplayViewCorrectionStates`](api/facade-display-view.md#serializedisplayviewcorrectionstates) | Runtime |
1170
+
1159
1171
  ## facade/scene-radiance.ts
1160
1172
 
1161
1173
  [Detailed contracts](api/facade-scene-radiance.md) · [Source](https://github.com/Photivra/photivra/blob/main/src/facade/scene-radiance.ts)
@@ -1185,10 +1197,12 @@ Start here to find a function by task. [Using Photivra from an agent](AGENT_GUID
1185
1197
  | --- | --- |
1186
1198
  | [`DisplayViewResult`](api/facade-simulate-capture.md#displayviewresult) | Type |
1187
1199
  | [`IdealReferenceResult`](api/facade-simulate-capture.md#idealreferenceresult) | Type |
1200
+ | [`serializeSimulatedCaptureDngFromParts`](api/facade-simulate-capture.md#serializesimulatedcapturedngfromparts) | Runtime |
1188
1201
  | [`SIMULATE_CAPTURE_BOX_APERTURE_DEFAULT_SAMPLES`](api/facade-simulate-capture.md#simulate_capture_box_aperture_default_samples) | Runtime |
1189
1202
  | [`SIMULATE_CAPTURE_COST_OPTIONS`](api/facade-simulate-capture.md#simulate_capture_cost_options) | Runtime |
1190
1203
  | [`SIMULATE_CAPTURE_DEFAULT_ILLUMINANCE_LUX`](api/facade-simulate-capture.md#simulate_capture_default_illuminance_lux) | Runtime |
1191
1204
  | [`SIMULATE_CAPTURE_FACADE_VERSION`](api/facade-simulate-capture.md#simulate_capture_facade_version) | Runtime |
1205
+ | [`SIMULATE_CAPTURE_FAST_OPTIONS`](api/facade-simulate-capture.md#simulate_capture_fast_options) | Runtime |
1192
1206
  | [`SIMULATE_DISPLAY_VIEW_DEFAULT_SAMPLES_PER_AXIS`](api/facade-simulate-capture.md#simulate_display_view_default_samples_per_axis) | Runtime |
1193
1207
  | [`simulateCapture`](api/facade-simulate-capture.md#simulatecapture) | Runtime |
1194
1208
  | [`SimulateCaptureAssumption`](api/facade-simulate-capture.md#simulatecaptureassumption) | Type |
@@ -1196,7 +1210,11 @@ Start here to find a function by task. [Using Photivra from an agent](AGENT_GUID
1196
1210
  | [`SimulateCaptureColorEncoding`](api/facade-simulate-capture.md#simulatecapturecolorencoding) | Type |
1197
1211
  | [`SimulateCaptureCostOption`](api/facade-simulate-capture.md#simulatecapturecostoption) | Type |
1198
1212
  | [`SimulateCaptureDemosaic`](api/facade-simulate-capture.md#simulatecapturedemosaic) | Type |
1213
+ | [`SimulateCaptureDemosaicRecord`](api/facade-simulate-capture.md#simulatecapturedemosaicrecord) | Type |
1214
+ | [`SimulateCaptureDngPart`](api/facade-simulate-capture.md#simulatecapturedngpart) | Type |
1199
1215
  | [`SimulateCaptureExposureMode`](api/facade-simulate-capture.md#simulatecaptureexposuremode) | Type |
1216
+ | [`SimulateCaptureFastOption`](api/facade-simulate-capture.md#simulatecapturefastoption) | Type |
1217
+ | [`SimulateCaptureFastReport`](api/facade-simulate-capture.md#simulatecapturefastreport) | Type |
1200
1218
  | [`SimulateCaptureLensDistortion`](api/facade-simulate-capture.md#simulatecapturelensdistortion) | Type |
1201
1219
  | [`SimulateCaptureMotion`](api/facade-simulate-capture.md#simulatecapturemotion) | Type |
1202
1220
  | [`SimulateCaptureOptions`](api/facade-simulate-capture.md#simulatecaptureoptions) | Type |
@@ -2020,6 +2038,8 @@ Start here to find a function by task. [Using Photivra from an agent](AGENT_GUID
2020
2038
 
2021
2039
  | Export | Kind |
2022
2040
  | --- | --- |
2041
+ | [`composePrintJpegStrips`](api/output-print-jpeg.md#composeprintjpegstrips) | Runtime |
2042
+ | [`createPrintJpegStrip`](api/output-print-jpeg.md#createprintjpegstrip) | Runtime |
2023
2043
  | [`createPrintJpegTask`](api/output-print-jpeg.md#createprintjpegtask) | Runtime |
2024
2044
  | [`parsePrintJpegInput`](api/output-print-jpeg.md#parseprintjpeginput) | Runtime |
2025
2045
  | [`PRINT_JPEG_LIMITS`](api/output-print-jpeg.md#print_jpeg_limits) | Runtime |
@@ -2028,6 +2048,8 @@ Start here to find a function by task. [Using Photivra from an agent](AGENT_GUID
2028
2048
  | [`PrintJpegOutput`](api/output-print-jpeg.md#printjpegoutput) | Type |
2029
2049
  | [`PrintJpegProvider`](api/output-print-jpeg.md#printjpegprovider) | Type |
2030
2050
  | [`PrintJpegSource`](api/output-print-jpeg.md#printjpegsource) | Type |
2051
+ | [`PrintJpegStrip`](api/output-print-jpeg.md#printjpegstrip) | Type |
2052
+ | [`PrintJpegStripInput`](api/output-print-jpeg.md#printjpegstripinput) | Type |
2031
2053
  | [`PrintJpegTask`](api/output-print-jpeg.md#printjpegtask) | Type |
2032
2054
  | [`PrintJpegTile`](api/output-print-jpeg.md#printjpegtile) | Type |
2033
2055
  | [`PrintJpegTileRequest`](api/output-print-jpeg.md#printjpegtilerequest) | Type |
@@ -72,7 +72,7 @@ modelling choices (such as "no lens PSF") as `kind: "model"`.
72
72
  | `lens.distortion` | none | `{ kind: "radial", profile }` or `{ kind: "brown-conrady", profile }`. |
73
73
  | `lens.vignetting` | none | An `IlluminationVignettingProfile` (radial relative illumination). |
74
74
  | `lens.psf` | none | A `{ kind: "sampled-local", configuration }` sampled lens PSF, passed to the environment contract unchanged. |
75
- | `lens.diffraction` | off | 2.1: `"circular-aperture"` or `{ aperture: "circular-aperture", kernelRadiusSamples }`: diffraction blur of an ideal lens at the resolved f-number (see [Optics](#optics)). |
75
+ | `lens.diffraction` | off | 2.1: `"circular-aperture"` or `{ kind: "circular-aperture", kernelRadiusSamples, fast }`: diffraction blur of an ideal lens at the resolved f-number (see [Optics](#optics)). `aperture` is accepted as a deprecated alias of `kind` until 3.0.0. |
76
76
  | `exposure.mode` | `"manual"` | Also `"aperture-priority"`, `"shutter-priority"`, `"program"`. |
77
77
  | `exposure.iso` | 100 | Selects the body's ISO readout regime (gain and read noise). |
78
78
  | `exposure.shutterSeconds` | 1/125 | Used in `manual` and `shutter-priority`. |
@@ -91,7 +91,7 @@ modelling choices (such as "no lens PSF") as `kind: "model"`.
91
91
  | `output.gamutMapping` | `"clip"` | PNG only: or `"perceptual"`. |
92
92
  | `output.dng.capturedAtUtc` | none | DNG only: capture time for the date tags. |
93
93
  | `output.metadata` | none | 2.4: `{ description?, software?, copyright?, comment? }` written into JPEG, PNG and DNG files unchanged (see [Text metadata](#text-metadata-24)). |
94
- | `output.demosaic` | the 1.0 bilinear kernels | `"bilinear"` or, from 2.1, `"ahd"` (see [Demosaic](#demosaic)). |
94
+ | `output.demosaic` | the 1.0 bilinear kernels | `"bilinear"` or, from 2.1, `"ahd"` (see [Demosaic](#demosaic)); or the record `{ kind, fast }`. |
95
95
  | `output.borderHandling` | `"margin"` | Or `"mirror"`. |
96
96
  | `output.toneCurve` | `"identity"` | Any `SdrToneCurve`, for example `"positive-reinhard-per-channel"`. |
97
97
  | `output.toneCurveParameters` | none | Required by the luminance curves (`extended-reinhard-luminance`, `naka-rushton-luminance`); the facade invents no curve parameters. |
@@ -102,8 +102,9 @@ modelling choices (such as "no lens PSF") as `kind: "model"`.
102
102
  | `sensor.spectralStepNanometers` | 20 | Spectral quadrature over 380–780 nm. |
103
103
  | `sensor.temporalSampleCount` | 1, or 8 when the camera rotates | Midpoints across each site's exposure window. |
104
104
  | `sensor.noiseSeed` | 1 | Output is deterministic for a given seed. |
105
- | `sensor.antiAliasing` | off | 2.1: an optical low-pass (anti-aliasing) filter: `{ kind: "four-spot", separationSites? }`, `{ kind: "two-spot", axis, separationSites? }` or `{ kind: "point-splitting", components }` (see [Anti-aliasing filter](#anti-aliasing-filter)). |
106
- | `sensor.pixelAperture` | `"point"` | 2.3: `"box"` or `{ kind: "box", samplesPerAxis }` integrates each square photosite (see [Pixel aperture](#pixel-aperture-23)). |
105
+ | `sensor.antiAliasing` | off | 2.1: an optical low-pass (anti-aliasing) filter: `{ kind: "four-spot", separationSites? }`, `{ kind: "two-spot", axis, separationSites? }` or `{ kind: "point-splitting", components }` (see [Anti-aliasing filter](#anti-aliasing-filter)). Each record also takes `fast`. |
106
+ | `sensor.pixelAperture` | `"point"` | 2.3: `"box"` or `{ kind: "box", samplesPerAxis, fast }` integrates each square photosite (see [Pixel aperture](#pixel-aperture-23)). |
107
+ | `fast` | `false` | Exact or fast for every option in `SIMULATE_CAPTURE_FAST_OPTIONS`; an option's record `fast` overrides it. No option has a fast method yet, so results are exact; `diagnostics.fast` reports what was requested and what ran (see [Exact and fast modes](https://github.com/Photivra/photivra/blob/main/docs/API_STYLE.md#exact-and-fast-modes)). |
107
108
  | `motion.angularVelocityRadPerSec` | still | `{ pitch?, yaw?, roll? }` in rad/s (see [Motion](#motion)). |
108
109
  | `motion.shutter` | `"global"` | Or `"rolling"`. |
109
110
  | `motion.rollingShutterSkewSeconds` | the body's readout skew | Rolling only. |
@@ -1151,6 +1152,65 @@ strips.forEach((strip, i) => stitched.set(strip.image.samples as Uint8Array, i *
1151
1152
  console.log(strips[0]!.region, stitched.length);
1152
1153
  ```
1153
1154
 
1155
+ ### A whole-frame DNG from parts
1156
+
1157
+ `serializeSimulatedCaptureDngFromParts(options, parts)` writes the whole-frame DNG of `options` (`output.format: "dng"`,
1158
+ no `output.region`) from region renders of the same request, so a large DNG's RAW can render in parallel like a JPEG or PNG.
1159
+ Each part is a region result's `raw` and `region` (any region format; `result.raw` holds the region's native sites,
1160
+ `result.region.nativeRect` where they lie). A region's sites are the whole render's sites, so the joined RAW is the whole
1161
+ render's RAW; the function then resolves the request and writes the DNG exactly as `simulateCapture` does, and the bytes
1162
+ are the same. The parts must cover the frame's native raster; where they overlap (the demosaic margin) their sites must
1163
+ agree, and a part from another request is refused. Render the parts with a fixed white balance and without `output.dng`
1164
+ (a region takes neither; the RAW codes do not depend on the white-balance intent): auto white balance and the capture time
1165
+ apply to the DNG options passed here, and auto white balance is measured over the joined frame.
1166
+
1167
+ Tests compare the joined DNG byte for byte with the whole-frame DNG for bilinear and AHD, diffraction with the four-spot
1168
+ filter and the 2 × 2 box, auto white balance with a capture time, a reflectance-function scene and the environment path.
1169
+ On the Photivra app's 24 MP request (6000 × 4000 at 6 µm, 24 parts of about 1024 pixels; Node, one thread) the DNG was
1170
+ the same bytes both ways; the parts took 58.1 s of CPU against 50.6 s for one whole render (Quality options: 119.3 s against
1171
+ 113.6 s) and the join 2.6 s, so on four workers the wall time is roughly a quarter of the whole render's.
1172
+
1173
+ ```ts
1174
+ import { serializeSimulatedCaptureDngFromParts, simulateCapture, type SimulateCaptureDngPart, type SimulateCaptureOptions } from "@photivra/engine";
1175
+
1176
+ const dngFrame: SimulateCaptureOptions = { scene: { preset: "siemens-star" }, output: { width: 96, height: 64, format: "dng" } };
1177
+ // Two strips rendered as sRGB regions (in an application, in separate Web Workers), then one DNG of the whole frame.
1178
+ const parts: SimulateCaptureDngPart[] = await Promise.all([0, 32].map(async (y) => {
1179
+ const part = await simulateCapture({ ...dngFrame, output: { width: 96, height: 64, region: { x: 0, y, width: 96, height: 32 } } });
1180
+ return { raw: part.raw, region: part.region! };
1181
+ }));
1182
+ const dng = await serializeSimulatedCaptureDngFromParts(dngFrame, parts);
1183
+ console.log(dng.bytes.length);
1184
+ ```
1185
+
1186
+ ### A Print JPEG written in strips
1187
+
1188
+ At 1:1 (output size equal to the crop), a Print JPEG's rows can be coded on separate workers.
1189
+ `createPrintJpegStrip(input, { y, height, samples })` codes crop rows `y` to `y + height` (8-bit sRGB RGB, crop-wide;
1190
+ `y` a multiple of 8, and `height` too unless the strip ends at the image's bottom) apart from the rows before them.
1191
+ `composePrintJpegStrips(input, strips)` joins strips that cover the image, top to bottom, into the file
1192
+ `createPrintJpegTask` writes for the same plan and source pixels: the same bytes. Only the first block of each strip
1193
+ depends on the strip before it (its DC differences), so the join codes that block and copies the rest of the strip's
1194
+ bits. The output's `providerReads` is 0, because no source is read.
1195
+
1196
+ Tests compare the joined file with the task's file byte for byte, for several splits, crops, quantization steps and a
1197
+ density. For a 24 MP file (Node, one thread), the whole task took 8.1 s; four strips took 1.9 s each, and the join took
1198
+ 1.1 s (a 64 MB file). In the Photivra app (Chromium, four workers), writing a 24 MP JPEG takes about 3 s instead of 9–12 s.
1199
+
1200
+ ```ts
1201
+ import { composePrintJpegStrips, createPrintJpegStrip, type PrintJpegInput } from "@photivra/engine";
1202
+
1203
+ const width = 64, height = 40, pixels = new Uint8Array(width * height * 3).fill(120);
1204
+ const jpegPlan: PrintJpegInput = {
1205
+ source: { captureId: "capture", imageStateId: "sdr", artifactId: "pixels", sha256: "0".repeat(64), width, height, encoding: "encoded-srgb-8-rgb" },
1206
+ crop: { x: 0, y: 0, width, height }, outputWidth: width, outputHeight: height, quantizationStep: 2, maximumEncodedBytes: 1_000_000, maximumProviderReads: 16
1207
+ };
1208
+ // Two strips (in an application, coded in separate Web Workers), then the file.
1209
+ const strips = [[0, 24], [24, 16]].map(([y, rows]) => createPrintJpegStrip(jpegPlan, { y: y!, height: rows!, samples: pixels.slice(y! * width * 3, (y! + rows!) * width * 3) }));
1210
+ const jpeg = composePrintJpegStrips(jpegPlan, strips);
1211
+ console.log(jpeg.bytes.length);
1212
+ ```
1213
+
1154
1214
  ## Ideal reference (2.3)
1155
1215
 
1156
1216
  `simulateIdealReference(options)` takes the same options as `simulateCapture` and returns what the camera
@@ -1234,9 +1294,14 @@ Validated against real full-resolution captures resized in linear light: three s
1234
1294
  100 MP medium format), five charts, f/2.8–f/16, ISO 100–6400, 96–800 display pixels wide, Speed and Quality options (see
1235
1295
  [Validation results](https://github.com/Photivra/photivra/blob/main/docs/DISPLAY_VIEW.md#validation-results)).
1236
1296
 
1237
- **Cost.** It follows the detailed display pixels × `samplesPerAxis`², not the real pixel count. Measured against the
1238
- whole-frame view of the same request (Siemens star, f/8, ISO 100, one run, Node 24.21.0 on a 4-vCPU 2.80 GHz Xeon;
1239
- every row, and the times before the exact speed-ups, in [DISPLAY_VIEW.md](https://github.com/Photivra/photivra/blob/main/docs/DISPLAY_VIEW.md#cost)):
1297
+ **Cost.** Where a display pixel covers more than 25 real pixels, the cost follows the detailed display pixels ×
1298
+ `samplesPerAxis`², not the real pixel count; at 25 or fewer (the exact area mean) it follows the real pixels the region
1299
+ touches. A device-pixel view of a 24 MP frame, 2028 × 1352 in 96 × 96 parts from one prepared view, takes about 0.09 s
1300
+ per part with the engine defaults and about 0.18 s with the Quality options (2.9.0, Node 24.21.0, one thread on a
1301
+ 4-vCPU 2.80 GHz Xeon; [the table](https://github.com/Photivra/photivra/blob/main/docs/DISPLAY_VIEW.md#a-device-pixel-view-rendered-in-parts),
1302
+ `node scripts/benchmark-display-view.mjs --app`). The table below is the 2.6.0 record of whole views against the
1303
+ whole-frame view of the same request (Siemens star, f/8, ISO 100, one run, same host; every row, and the times before
1304
+ the exact speed-ups, in [DISPLAY_VIEW.md](https://github.com/Photivra/photivra/blob/main/docs/DISPLAY_VIEW.md#cost)):
1240
1305
 
1241
1306
  | Sensor | Options | 96 wide | 384 wide | 800 wide |
1242
1307
  | --- | --- | --- | --- | --- |
@@ -1262,7 +1327,11 @@ view allocates nothing at full resolution).
1262
1327
 
1263
1328
  **Many regions of one view (2.7).** Each `simulateDisplayView` call repeats the request's setup: the camera, lens,
1264
1329
  scene and illuminant, the channel responses, the diffraction kernels and the colour tables. That is about 0.25 s with
1265
- the engine defaults and 0.7 s with the Quality options on a 24 MP request. To render one view in many regions, prepare
1330
+ the engine defaults and 0.7 s with the Quality options on a 24 MP request (measured before #443). The channel responses
1331
+ do not depend on the scene and are kept across calls, so a call after a chart, illuminant or illuminance change costs
1332
+ less than one after an aperture or shutter change: with the Quality options, about 30–50 ms after a chart, illuminant or
1333
+ illuminance change and about 55–70 ms after an aperture or shutter change, against about 0.4 s for each before #443
1334
+ (Node 24.21.0, one thread, prepares in one process after a warm-up). To render one view in many regions, prepare
1266
1335
  the request once and render each region from it; each region then costs only its own pixels:
1267
1336
 
1268
1337
  ```ts