@photivra/engine 2.9.0 → 2.11.1
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.
- package/CHANGELOG.md +223 -1
- package/CITATION.cff +1 -1
- package/NODE_SUPPORT.md +21 -3
- package/README.md +6 -6
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +2 -0
- package/dist/capture/native-environment-lean.js +89 -5
- package/dist/capture/native-environment-lean.js.map +1 -1
- package/dist/capture/native-environment-tabulated.d.ts +37 -0
- package/dist/capture/native-environment-tabulated.js +42 -6
- package/dist/capture/native-environment-tabulated.js.map +1 -1
- package/dist/core/portable-math.d.ts +19 -0
- package/dist/core/portable-math.js +82 -0
- package/dist/core/portable-math.js.map +1 -1
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/dist/core/version.js.map +1 -1
- package/dist/facade/channel-response.d.ts +15 -0
- package/dist/facade/channel-response.js +33 -0
- package/dist/facade/channel-response.js.map +1 -0
- package/dist/facade/chart-material-layers.d.ts +115 -0
- package/dist/facade/chart-material-layers.js +224 -0
- package/dist/facade/chart-material-layers.js.map +1 -0
- package/dist/facade/chart-radiance-batch.d.ts +8 -2
- package/dist/facade/chart-radiance-batch.js +11 -4
- package/dist/facade/chart-radiance-batch.js.map +1 -1
- package/dist/facade/chart-scenes.d.ts +134 -0
- package/dist/facade/chart-scenes.js +32 -22
- package/dist/facade/chart-scenes.js.map +1 -1
- package/dist/facade/diffraction-provider.d.ts +8 -2
- package/dist/facade/diffraction-provider.js +120 -16
- package/dist/facade/diffraction-provider.js.map +1 -1
- package/dist/facade/display-view-gpu-session.d.ts +18 -0
- package/dist/facade/display-view-gpu-session.js +280 -0
- package/dist/facade/display-view-gpu-session.js.map +1 -0
- package/dist/facade/display-view-gpu.d.ts +146 -0
- package/dist/facade/display-view-gpu.js +59 -0
- package/dist/facade/display-view-gpu.js.map +1 -0
- package/dist/facade/display-view.d.ts +123 -1
- package/dist/facade/display-view.js +478 -116
- package/dist/facade/display-view.js.map +1 -1
- package/dist/facade/expected-signal-setup.d.ts +5 -0
- package/dist/facade/expected-signal-setup.js +18 -1
- package/dist/facade/expected-signal-setup.js.map +1 -1
- package/dist/facade/expected-signal.d.ts +80 -6
- package/dist/facade/expected-signal.js +200 -24
- package/dist/facade/expected-signal.js.map +1 -1
- package/dist/facade/scene-radiance.d.ts +4 -2
- package/dist/facade/scene-radiance.js +7 -4
- package/dist/facade/scene-radiance.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +81 -7
- package/dist/facade/simulate-capture.js +276 -53
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.d.ts +54 -3
- package/dist/facade/simulate-display-view.js +120 -27
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/chart-composite-gpu.d.ts +119 -0
- package/dist/gpu/chart-composite-gpu.js +572 -0
- package/dist/gpu/chart-composite-gpu.js.map +1 -0
- package/dist/gpu/chart-radiance-gpu.d.ts +6 -6
- package/dist/gpu/chart-radiance-gpu.js +9 -11
- package/dist/gpu/chart-radiance-gpu.js.map +1 -1
- package/dist/gpu/display-view-chart-material-module.d.ts +10 -0
- package/dist/gpu/display-view-chart-material-module.js +58 -0
- package/dist/gpu/display-view-chart-material-module.js.map +1 -0
- package/dist/gpu/display-view-composite-material-module.d.ts +68 -0
- package/dist/gpu/display-view-composite-material-module.js +248 -0
- package/dist/gpu/display-view-composite-material-module.js.map +1 -0
- package/dist/gpu/display-view-quality-gpu.d.ts +185 -0
- package/dist/gpu/display-view-quality-gpu.js +1000 -0
- package/dist/gpu/display-view-quality-gpu.js.map +1 -0
- package/dist/gpu/display-view-speed-gpu.d.ts +156 -0
- package/dist/gpu/display-view-speed-gpu.js +524 -0
- package/dist/gpu/display-view-speed-gpu.js.map +1 -0
- package/dist/gpu/image-kernel-accelerator.d.ts +23 -4
- package/dist/gpu/image-kernel-accelerator.js +58 -10
- package/dist/gpu/image-kernel-accelerator.js.map +1 -1
- package/dist/gpu/image-kernel-execution.d.ts +60 -3
- package/dist/gpu/image-kernel-execution.js +2 -2
- package/dist/gpu/image-kernel-execution.js.map +1 -1
- package/dist/gpu/sensor-stage-gpu.d.ts +11 -0
- package/dist/gpu/sensor-stage-gpu.js +42 -13
- package/dist/gpu/sensor-stage-gpu.js.map +1 -1
- package/dist/gpu/webgpu-runtime.d.ts +4 -2
- package/dist/gpu/webgpu-runtime.js +13 -4
- package/dist/gpu/webgpu-runtime.js.map +1 -1
- package/dist/gpu/webgpu-switch.d.ts +4 -0
- package/dist/gpu/webgpu-switch.js +29 -0
- package/dist/gpu/webgpu-switch.js.map +1 -0
- package/dist/gpu/wgsl-chart-composite-kernel.d.ts +52 -0
- package/dist/gpu/wgsl-chart-composite-kernel.js +515 -0
- package/dist/gpu/wgsl-chart-composite-kernel.js.map +1 -0
- package/dist/gpu/wgsl-chart-kernel.d.ts +11 -9
- package/dist/gpu/wgsl-chart-kernel.js +33 -27
- package/dist/gpu/wgsl-chart-kernel.js.map +1 -1
- package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +95 -0
- package/dist/gpu/wgsl-display-view-quality-kernel.js +769 -0
- package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -0
- package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +75 -0
- package/dist/gpu/wgsl-display-view-speed-kernel.js +386 -0
- package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -0
- package/dist/gpu/wgsl-sensor-kernel.d.ts +28 -9
- package/dist/gpu/wgsl-sensor-kernel.js +94 -50
- package/dist/gpu/wgsl-sensor-kernel.js.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/optics/defocus-disc-kernel.d.ts +61 -0
- package/dist/optics/defocus-disc-kernel.js +191 -0
- package/dist/optics/defocus-disc-kernel.js.map +1 -0
- package/dist/optics/irradiance-convolution.d.ts +11 -0
- package/dist/optics/irradiance-convolution.js +64 -24
- package/dist/optics/irradiance-convolution.js.map +1 -1
- package/dist/optics/scene-to-sensor-irradiance.d.ts +13 -0
- package/dist/optics/scene-to-sensor-irradiance.js +58 -24
- package/dist/optics/scene-to-sensor-irradiance.js.map +1 -1
- package/dist/optics/sensor-aperture-rays.js +2 -1
- package/dist/optics/sensor-aperture-rays.js.map +1 -1
- package/dist/optics/sensor-environment-query.js +4 -3
- package/dist/optics/sensor-environment-query.js.map +1 -1
- package/dist/optics/site-diffraction-kernel.d.ts +16 -0
- package/dist/optics/site-diffraction-kernel.js +60 -8
- package/dist/optics/site-diffraction-kernel.js.map +1 -1
- package/dist/processing/demosaic.js +114 -37
- package/dist/processing/demosaic.js.map +1 -1
- package/dist/schema/scene-radiance.d.ts +8 -0
- package/dist/schema/scene-radiance.js +28 -17
- package/dist/schema/scene-radiance.js.map +1 -1
- package/dist/sensor/environment-photo-signal.d.ts +21 -0
- package/dist/sensor/environment-photo-signal.js +68 -2
- package/dist/sensor/environment-photo-signal.js.map +1 -1
- package/docs/API_REFERENCE.md +60 -1
- package/docs/QUICK_START.md +120 -11
- package/docs/USAGE.md +82 -2
- package/docs/api/core-version.md +1 -1
- package/docs/api/exports.json +157 -2
- package/docs/api/facade-chart-material-layers.md +137 -0
- package/docs/api/facade-chart-radiance-batch.md +7 -2
- package/docs/api/facade-display-view-gpu.md +94 -0
- package/docs/api/facade-display-view.md +50 -0
- package/docs/api/facade-scene-radiance.md +3 -2
- package/docs/api/facade-simulate-capture.md +81 -12
- package/docs/api/facade-simulate-display-view.md +39 -2
- package/docs/api/gpu-image-kernel-accelerator.md +10 -3
- package/docs/api/gpu-image-kernel-execution.md +76 -4
- package/docs/api/gpu-webgpu-switch.md +23 -0
- package/package.json +3 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scene-to-sensor-irradiance.js","sourceRoot":"","sources":["../../src/optics/scene-to-sensor-irradiance.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Scene-to-sensor optical bridge: from spectral scene radiance to the irradiance that reaches the\n * sensor.\n *\n * The profile parser validates untrusted input and returns the normalized typed contract. Unknown\n * enum values, missing required fields and incompatible scientific data are rejected here.\n *\n * The calculation applies the declared pupil acceptance and exactly one transmission or\n * field-throughput path. It returns the irradiance before the sensor stack, with conservative\n * evidence.\n *\n * The optical bridge turns outgoing scene radiance (W/m²/sr/nm) into irradiance (W/m²/nm) arriving\n * at the front of the sensor stack.\n * - Circular paraxial acceptance is pi/(4*Nworking²). At finite focus with unit pupil magnification,\n * the working f-number is N*(1+m).\n * - Spectral transmission and an effective working T-stop are alternative paths; use one.\n * - Field throughput is applied exactly once.\n * - Sensor response and PSF (point-spread function) blur happen later in the pipeline.\n * @see docs/SCENE_SENSOR_QUADRATURE.md for the equations, units, coordinate conventions, known\n * blockers and support limits.\n */\n\nimport {\n approximationResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport {\n parseEvidenceList,\n type EvidenceBackedFact,\n type EvidenceProvenance\n} from \"../core/evidence-provenance.js\";\nimport {\n parseSpectralWavelengthBasis,\n type SpectralWavelengthBasis\n} from \"../core/spectral.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport {\n composeFrontOfLensFilterTransmission,\n type FrontOfLensFilterProfile\n} from \"./front-of-lens-filter.js\";\nimport type { IlluminationVignetting } from \"./illumination-vignetting.js\";\nimport { calculateThinLensImageDistance } from \"./thin-lens.js\";\nimport {\n parseSceneRadianceEvaluationRequest,\n parseSceneRadianceEvaluationResult,\n type SceneRadianceEvaluationRequest,\n type SceneRadianceEvaluationResult\n} from \"../schema/scene-radiance.js\";\nimport { requireInputKeys, rejectUnknownFields, rejectUnknownVariantFields, UNCERTAINTY_VARIANT_FIELDS } from \"../core/record-validation.js\";\n\ntype UnknownRecord = Record<string, unknown>;\n\nexport const SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION =\n \"0.1.0\" as const;\n\nexport type OpticalBridgeScientificStatus =\n | \"calibrated\"\n | \"approximation\";\n\nexport type OpticalBridgeUncertainty =\n | {\n kind: \"relative\";\n fraction: number;\n basis: string;\n }\n | {\n kind: \"not-quantified\";\n limitation: string;\n };\n\nexport interface NumericRange {\n minimum: number;\n maximum: number;\n}\n\nexport type OpticalBridgeFocusApplicability =\n | {\n kind: \"any\";\n }\n | {\n kind: \"finite-distance-range\";\n objectDistanceM: NumericRange;\n };\n\nexport interface SpectralTransmissionSample {\n wavelengthNanometers: number;\n linearTransmissionFactor: number;\n}\n\nexport type OpticalTransmissionModel =\n | {\n kind: \"spectral-transmission\";\n scientificStatus:\n OpticalBridgeScientificStatus;\n wavelengthBasis:\n Exclude<\n SpectralWavelengthBasis,\n \"unspecified\"\n >;\n samples:\n EvidenceBackedFact<\n readonly SpectralTransmissionSample[]\n >;\n uncertainty:\n OpticalBridgeUncertainty;\n }\n | {\n kind:\n \"effective-working-t-stop-approximation\";\n scientificStatus: \"approximation\";\n workingTStop:\n EvidenceBackedFact<number>;\n wavelengthBasis:\n Exclude<\n SpectralWavelengthBasis,\n \"unspecified\"\n >;\n wavelengthRangeNanometers:\n NumericRange;\n uncertainty:\n OpticalBridgeUncertainty;\n limitation: string;\n };\n\nexport interface SceneToSensorIrradianceProfile {\n schemaVersion:\n typeof SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION;\n profileId: string;\n profileVersion: string;\n lensProfileId: string;\n scientificStatus:\n OpticalBridgeScientificStatus;\n applicability: {\n focalLengthMm: NumericRange;\n nominalFNumber: NumericRange;\n focus:\n OpticalBridgeFocusApplicability;\n };\n transmission:\n OpticalTransmissionModel;\n distortionAreaMappingOwnership:\n \"not-applied-by-bridge\";\n psfRedistributionOwnership:\n \"downstream-normalized-energy-redistribution\";\n sensorOpticalStackIncluded: false;\n strayLightIncluded: false;\n polarizationModeled: false;\n wavelengthChangingBehaviorModeled: false;\n volumetricScatteringModeled: false;\n evidence: readonly EvidenceProvenance[];\n limitations: readonly string[];\n}\n\nexport type OpticalBridgeFocusContext =\n | {\n kind: \"infinity-focus\";\n }\n | {\n kind:\n \"ideal-symmetric-thin-lens\";\n objectDistanceM: number;\n pupilMagnificationAssumption:\n \"unity\";\n }\n | {\n kind:\n \"supplied-working-f-number\";\n focus:\n | {\n kind: \"infinity\";\n }\n | {\n kind: \"finite\";\n objectDistanceM: number;\n };\n workingFNumber:\n EvidenceBackedFact<number>;\n basis: string;\n };\n\nexport type OpticalBridgeFieldThroughput =\n | {\n kind: \"unity\";\n }\n | {\n kind:\n \"illumination-vignetting-result\";\n result:\n IlluminationVignetting;\n };\n\nexport interface CalculateSceneRadianceToSensorIrradianceInput {\n sceneRadianceRequest:\n SceneRadianceEvaluationRequest;\n sceneRadianceResult:\n SceneRadianceEvaluationResult;\n profile:\n SceneToSensorIrradianceProfile;\n focalLengthMm: number;\n nominalFNumber: number;\n focus:\n OpticalBridgeFocusContext;\n imagePointMm: {\n x: number;\n y: number;\n };\n fieldThroughput:\n OpticalBridgeFieldThroughput;\n frontOfLensFilters?:\n readonly FrontOfLensFilterProfile[];\n}\n\nexport interface SceneToSensorIrradianceResult {\n schemaVersion:\n typeof SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION;\n profileId: string;\n profileVersion: string;\n lensProfileId: string;\n sampleId: string;\n providerProfileId: string;\n sceneId: string;\n timeSecondsFromExposureStart: number;\n wavelengthNanometers: number;\n wavelengthBasis:\n Exclude<\n SpectralWavelengthBasis,\n \"unspecified\"\n >;\n inputQuantity:\n \"outgoing-spectral-radiance\";\n inputUnit:\n \"W/m^2/sr/nm\";\n outputQuantity:\n \"sensor-plane-spectral-irradiance\";\n outputUnit:\n \"W/m^2/nm\";\n sceneSpectralRadianceWattsPerSquareMeterSteradianNanometer:\n number;\n focalLengthMm: number;\n nominalFNumber: number;\n workingFNumber: number;\n focusModel:\n OpticalBridgeFocusContext[\"kind\"];\n paraxialGeometricAcceptanceSolidAngleSteradians:\n number;\n transmissionPath:\n OpticalTransmissionModel[\"kind\"];\n spectralTransmissionFactor:\n number | null;\n effectiveWorkingTStop:\n number | null;\n tStopEquivalentTransmissionFactor:\n number | null;\n frontOfLensFilterCount: number;\n frontOfLensFilterTransmissionFactor:\n number;\n frontOfLensFilterAttenuationStops:\n number;\n frontOfLensFilterTransmissionAppliedExactlyOnce:\n true;\n /**\n * True when a front-of-lens linear polarizer was applied. Scene radiance is unpolarized in this\n * bridge, so a polarizer transmits (k1 + k2) / 2.\n */\n frontOfLensFilterPolarizationModeled:\n boolean;\n fieldThroughputFactor: number;\n effectiveAcceptanceAfterTransmissionAndFieldSteradians:\n number;\n sensorPlaneSpectralIrradianceWattsPerSquareMeterNanometer:\n number;\n scientificStatus: \"approximation\";\n sceneRadianceScientificStatus:\n SceneRadianceEvaluationResult[\"scientificStatus\"];\n opticalProfileScientificStatus:\n OpticalBridgeScientificStatus;\n sceneRadianceUncertainty:\n SceneRadianceEvaluationResult[\"uncertainty\"];\n opticalUncertainty:\n OpticalBridgeUncertainty;\n radianceAmplificationApplied: false;\n fieldThroughputAppliedExactlyOnce:\n true;\n universalCos4FalloffApplied: false;\n distortionAreaCorrectionApplied: false;\n psfRedistributionApplied: false;\n sensorOpticalStackApplied: false;\n cfaApplied: false;\n quantumEfficiencyApplied: false;\n spectralResponsivityApplied: false;\n strayLightApplied: false;\n polarizationModeled: false;\n wavelengthChangingBehaviorModeled:\n false;\n volumetricScatteringModeled: false;\n calibratedSensorPlaneIrradianceClaimAuthorized:\n false;\n componentEvidence: {\n sceneRadiance:\n readonly EvidenceProvenance[];\n opticalProfile:\n readonly EvidenceProvenance[];\n transmission:\n readonly EvidenceProvenance[];\n workingFNumber:\n readonly EvidenceProvenance[];\n frontOfLensFilters:\n readonly EvidenceProvenance[];\n };\n limitations:\n readonly string[];\n}\n\nfunction requireRecord(\n value: unknown,\n path: string,\n allowedKeys: readonly string[] | null\n): UnknownRecord {\n if (\n typeof value !== \"object\" ||\n value === null ||\n Array.isArray(value)\n ) {\n throw new InvalidConfigurationError(\n path + \" must be an object.\"\n );\n }\n if (allowedKeys !== null) rejectUnknownFields(value, allowedKeys, path);\n return value as UnknownRecord;\n}\n\nfunction requireNonEmptyString(\n value: unknown,\n path: string\n): string {\n if (\n typeof value !== \"string\" ||\n value.trim().length === 0\n ) {\n throw new InvalidConfigurationError(\n path + \" must be a non-empty string.\"\n );\n }\n return value.trim();\n}\n\nfunction requireFinite(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isFinite(value)\n ) {\n throw new InvalidConfigurationError(\n path + \" must be finite.\"\n );\n }\n return value;\n}\n\nfunction requirePositiveFinite(\n value: unknown,\n path: string\n): number {\n const parsed =\n requireFinite(value, path);\n if (parsed <= 0) {\n throw new InvalidConfigurationError(\n path +\n \" must be greater than zero.\"\n );\n }\n return parsed;\n}\n\nfunction requireFraction(\n value: unknown,\n path: string\n): number {\n const parsed =\n requireFinite(value, path);\n if (\n parsed < 0 ||\n parsed > 1\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must be from zero through one.\"\n );\n }\n return parsed;\n}\n\nfunction parseRange(\n value: unknown,\n path: string\n): NumericRange {\n const record =\n requireRecord(value, path, [\"minimum\", \"maximum\"]);\n const minimum =\n requirePositiveFinite(\n record.minimum,\n path + \".minimum\"\n );\n const maximum =\n requirePositiveFinite(\n record.maximum,\n path + \".maximum\"\n );\n if (minimum > maximum) {\n throw new InvalidConfigurationError(\n path +\n \".minimum must be less than or equal to maximum.\"\n );\n }\n return {\n minimum,\n maximum\n };\n}\n\nfunction parseScientificStatus(\n value: unknown,\n path: string\n): OpticalBridgeScientificStatus {\n if (\n value !== \"calibrated\" &&\n value !== \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n path + \" is invalid.\"\n );\n }\n return value;\n}\n\nfunction parseUncertainty(\n value: unknown,\n path: string\n): OpticalBridgeUncertainty {\n const record =\n requireRecord(value, path, null);\n rejectUnknownVariantFields(record, \"kind\", UNCERTAINTY_VARIANT_FIELDS, path);\n\n if (record.kind === \"relative\") {\n return {\n kind: \"relative\",\n fraction:\n requireFraction(\n record.fraction,\n path + \".fraction\"\n ),\n basis:\n requireNonEmptyString(\n record.basis,\n path + \".basis\"\n )\n };\n }\n\n if (\n record.kind ===\n \"not-quantified\"\n ) {\n return {\n kind: \"not-quantified\",\n limitation:\n requireNonEmptyString(\n record.limitation,\n path + \".limitation\"\n )\n };\n }\n\n throw new InvalidConfigurationError(\n path + \".kind is invalid.\"\n );\n}\n\nfunction parseLimitations(\n value: unknown,\n path: string\n): readonly string[] {\n if (!Array.isArray(value)) {\n throw new InvalidConfigurationError(\n path + \" must be an array.\"\n );\n }\n const parsed = value.map(\n (entry, index) =>\n requireNonEmptyString(\n entry,\n path +\n \"[\" +\n index +\n \"]\"\n )\n );\n if (\n new Set(parsed).size !==\n parsed.length\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must not contain duplicates.\"\n );\n }\n return parsed;\n}\n\nfunction parseReusableNumericEvidence(\n value: unknown,\n path: string\n): readonly EvidenceProvenance[] {\n const evidence =\n parseEvidenceList(value, path);\n\n if (\n evidence.some(\n (entry) =>\n entry.reuseStatus ===\n \"factual-reference-only\"\n )\n ) {\n throw new InvalidConfigurationError(\n path +\n \" for numeric optical curve data must be reusable-data or photivra-owned.\"\n );\n }\n return evidence;\n}\n\nfunction parseSpectralSamples(\n value: unknown,\n path: string\n): readonly SpectralTransmissionSample[] {\n if (\n !Array.isArray(value) ||\n value.length < 2\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must contain at least two samples.\"\n );\n }\n\n let previous =\n Number.NEGATIVE_INFINITY;\n return value.map(\n (entry, index) => {\n const samplePath =\n path +\n \"[\" +\n index +\n \"]\";\n const record =\n requireRecord(\n entry,\n samplePath, [\"wavelengthNanometers\", \"linearTransmissionFactor\"]\n );\n const wavelengthNanometers =\n requirePositiveFinite(\n record.wavelengthNanometers,\n samplePath +\n \".wavelengthNanometers\"\n );\n if (\n wavelengthNanometers <=\n previous\n ) {\n throw new InvalidConfigurationError(\n path +\n \" wavelengths must be strictly increasing.\"\n );\n }\n previous =\n wavelengthNanometers;\n return {\n wavelengthNanometers,\n linearTransmissionFactor:\n requireFraction(\n record.linearTransmissionFactor,\n samplePath +\n \".linearTransmissionFactor\"\n )\n };\n }\n );\n}\n\nfunction parseTransmission(\n value: unknown,\n path: string\n): OpticalTransmissionModel {\n const record =\n requireRecord(value, path, null);\n rejectUnknownVariantFields(record, \"kind\", {\n // workingTStop and wavelengthRangeNanometers (spectral) and samples (working T-stop) are rejected below with their own messages.\n \"spectral-transmission\": [\"kind\", \"scientificStatus\", \"wavelengthBasis\", \"samples\", \"uncertainty\", \"workingTStop\", \"wavelengthRangeNanometers\"],\n \"effective-working-t-stop-approximation\": [\"kind\", \"scientificStatus\", \"workingTStop\", \"wavelengthBasis\", \"wavelengthRangeNanometers\",\n \"uncertainty\", \"limitation\", \"samples\"]\n }, path);\n\n if (\n record.kind ===\n \"spectral-transmission\"\n ) {\n if (\n record.workingTStop !==\n undefined ||\n record.wavelengthRangeNanometers !==\n undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" spectral-transmission must not include T-stop-only fields.\"\n );\n }\n\n const status =\n parseScientificStatus(\n record.scientificStatus,\n path +\n \".scientificStatus\"\n );\n const samplesFact =\n requireRecord(\n record.samples,\n path + \".samples\", [\"value\", \"evidence\"]\n );\n const uncertainty =\n parseUncertainty(\n record.uncertainty,\n path + \".uncertainty\"\n );\n\n if (\n status === \"calibrated\" &&\n uncertainty.kind !==\n \"relative\"\n ) {\n throw new InvalidConfigurationError(\n path +\n \" calibrated spectral transmission requires quantified relative uncertainty.\"\n );\n }\n\n return {\n kind:\n \"spectral-transmission\",\n scientificStatus: status,\n wavelengthBasis:\n parseResolvedBasis(\n record.wavelengthBasis,\n path +\n \".wavelengthBasis\"\n ),\n samples: {\n value:\n parseSpectralSamples(\n samplesFact.value,\n path +\n \".samples.value\"\n ),\n evidence:\n parseReusableNumericEvidence(\n samplesFact.evidence,\n path +\n \".samples.evidence\"\n )\n },\n uncertainty\n };\n }\n\n if (\n record.kind ===\n \"effective-working-t-stop-approximation\"\n ) {\n if (\n record.samples !==\n undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" T-stop approximation must not include spectral transmission samples.\"\n );\n }\n if (\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n path +\n ' T-stop scientificStatus must be \"approximation\".'\n );\n }\n const workingTStopFact =\n requireRecord(\n record.workingTStop,\n path +\n \".workingTStop\", [\"value\", \"evidence\"]\n );\n return {\n kind:\n \"effective-working-t-stop-approximation\",\n scientificStatus:\n \"approximation\",\n workingTStop: {\n value:\n requirePositiveFinite(\n workingTStopFact.value,\n path +\n \".workingTStop.value\"\n ),\n evidence:\n parseEvidenceList(\n workingTStopFact.evidence,\n path +\n \".workingTStop.evidence\"\n )\n },\n wavelengthBasis:\n parseResolvedBasis(\n record.wavelengthBasis,\n path +\n \".wavelengthBasis\"\n ),\n wavelengthRangeNanometers:\n parseRange(\n record.wavelengthRangeNanometers,\n path +\n \".wavelengthRangeNanometers\"\n ),\n uncertainty:\n parseUncertainty(\n record.uncertainty,\n path + \".uncertainty\"\n ),\n limitation:\n requireNonEmptyString(\n record.limitation,\n path +\n \".limitation\"\n )\n };\n }\n\n throw new InvalidConfigurationError(\n path + \".kind is invalid.\"\n );\n}\n\nfunction parseResolvedBasis(\n value: unknown,\n path: string\n): Exclude<\n SpectralWavelengthBasis,\n \"unspecified\"\n> {\n const parsed =\n parseSpectralWavelengthBasis(\n value,\n path\n );\n if (parsed === \"unspecified\") {\n throw new InvalidConfigurationError(\n path +\n ' must resolve to \"air\" or \"vacuum\".'\n );\n }\n return parsed;\n}\n\nfunction parseFocusApplicability(\n value: unknown,\n path: string\n): OpticalBridgeFocusApplicability {\n const record =\n requireRecord(value, path, null);\n rejectUnknownVariantFields(record, \"kind\", { any: [\"kind\"], \"finite-distance-range\": [\"kind\", \"objectDistanceM\"] }, path);\n\n if (record.kind === \"any\") {\n return {\n kind: \"any\"\n };\n }\n if (\n record.kind ===\n \"finite-distance-range\"\n ) {\n return {\n kind:\n \"finite-distance-range\",\n objectDistanceM:\n parseRange(\n record.objectDistanceM,\n path +\n \".objectDistanceM\"\n )\n };\n }\n throw new InvalidConfigurationError(\n path + \".kind is invalid.\"\n );\n}\n\n/**\n * Validates an untrusted scene-to-sensor profile and returns the normalized typed contract. Unknown\n * enum values, missing required fields and incompatible scientific data are rejected here.\n *\n * The optical bridge turns outgoing scene radiance (W/m²/sr/nm) into irradiance (W/m²/nm) arriving\n * at the front of the sensor stack.\n * - Circular paraxial acceptance is pi/(4*Nworking²). At finite focus with unit pupil magnification,\n * the working f-number is N*(1+m).\n * - Spectral transmission and an effective working T-stop are alternative paths; use one.\n * - Field throughput is applied exactly once.\n * - Sensor response and PSF (point-spread function) blur happen later in the pipeline.\n * @param value - unknown. Untrusted input; TypeScript types alone are not validation.\n * @returns SceneToSensorIrradianceProfile. The result states its shape and scientific status; a successful call does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the root export and exact type graph.\n */\nexport function parseSceneToSensorIrradianceProfile(\n value: unknown\n): SceneToSensorIrradianceProfile {\n const record =\n requireRecord(\n value,\n \"sceneToSensorIrradianceProfile\", [\"schemaVersion\", \"applicability\", \"scientificStatus\", \"transmission\", \"distortionAreaMappingOwnership\", \"psfRedistributionOwnership\", \"sensorOpticalStackIncluded\", \"strayLightIncluded\", \"polarizationModeled\", \"wavelengthChangingBehaviorModeled\", \"volumetricScatteringModeled\", \"profileId\", \"profileVersion\", \"lensProfileId\", \"evidence\", \"limitations\"]\n );\n if (\n record.schemaVersion !==\n SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION\n ) {\n throw new InvalidConfigurationError(\n 'sceneToSensorIrradianceProfile.schemaVersion must be \"' +\n SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION +\n '\".'\n );\n }\n\n const applicability =\n requireRecord(\n record.applicability,\n \"sceneToSensorIrradianceProfile.applicability\", [\"focalLengthMm\", \"nominalFNumber\", \"focus\"]\n );\n const status =\n parseScientificStatus(\n record.scientificStatus,\n \"sceneToSensorIrradianceProfile.scientificStatus\"\n );\n const transmission =\n parseTransmission(\n record.transmission,\n \"sceneToSensorIrradianceProfile.transmission\"\n );\n\n if (\n status === \"calibrated\" &&\n transmission.scientificStatus !==\n \"calibrated\"\n ) {\n throw new InvalidConfigurationError(\n \"A calibrated optical bridge profile requires calibrated spectral transmission.\"\n );\n }\n\n if (\n record.distortionAreaMappingOwnership !==\n \"not-applied-by-bridge\" ||\n record.psfRedistributionOwnership !==\n \"downstream-normalized-energy-redistribution\" ||\n record.sensorOpticalStackIncluded !==\n false ||\n record.strayLightIncluded !==\n false ||\n record.polarizationModeled !==\n false ||\n record.wavelengthChangingBehaviorModeled !==\n false ||\n record.volumetricScatteringModeled !==\n false\n ) {\n throw new InvalidConfigurationError(\n \"sceneToSensorIrradianceProfile ownership/completeness flags are invalid for schema 0.1.0.\"\n );\n }\n\n return {\n schemaVersion:\n SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION,\n profileId:\n requireNonEmptyString(\n record.profileId,\n \"sceneToSensorIrradianceProfile.profileId\"\n ),\n profileVersion:\n requireNonEmptyString(\n record.profileVersion,\n \"sceneToSensorIrradianceProfile.profileVersion\"\n ),\n lensProfileId:\n requireNonEmptyString(\n record.lensProfileId,\n \"sceneToSensorIrradianceProfile.lensProfileId\"\n ),\n scientificStatus: status,\n applicability: {\n focalLengthMm:\n parseRange(\n applicability.focalLengthMm,\n \"sceneToSensorIrradianceProfile.applicability.focalLengthMm\"\n ),\n nominalFNumber:\n parseRange(\n applicability.nominalFNumber,\n \"sceneToSensorIrradianceProfile.applicability.nominalFNumber\"\n ),\n focus:\n parseFocusApplicability(\n applicability.focus,\n \"sceneToSensorIrradianceProfile.applicability.focus\"\n )\n },\n transmission,\n distortionAreaMappingOwnership:\n \"not-applied-by-bridge\",\n psfRedistributionOwnership:\n \"downstream-normalized-energy-redistribution\",\n sensorOpticalStackIncluded:\n false,\n strayLightIncluded: false,\n polarizationModeled: false,\n wavelengthChangingBehaviorModeled:\n false,\n volumetricScatteringModeled:\n false,\n evidence:\n parseEvidenceList(\n record.evidence,\n \"sceneToSensorIrradianceProfile.evidence\"\n ),\n limitations:\n parseLimitations(\n record.limitations,\n \"sceneToSensorIrradianceProfile.limitations\"\n )\n };\n}\n\nfunction ensureWithin(\n value: number,\n range: NumericRange,\n path: string\n): void {\n if (\n value < range.minimum ||\n value > range.maximum\n ) {\n throw new InvalidScientificInputError(\n path +\n \" lies outside the optical profile applicability range.\"\n );\n }\n}\n\n/**\n * Internal (2.6; not exported from the package): N_w / N = 1 + m for an ideal symmetric thin lens with unity pupil\n * magnification focused at `objectDistanceM`, m = v/s from `calculateThinLensImageDistance`. The irradiance bridge's\n * `ideal-symmetric-thin-lens` focus uses exactly this factor, and the framing reports derive their close-up light\n * factor 1/(1 + m)² from it, so the two cannot drift apart.\n */\nexport function thinLensWorkingFNumberFactor(focalLengthMm: number, objectDistanceM: number): number {\n return 1 + calculateThinLensImageDistance({ focalLengthMm, objectDistanceM }).value.magnification;\n}\n\nfunction workingFNumber(\n focus:\n OpticalBridgeFocusContext,\n focalLengthMm: number,\n nominalFNumber: number\n): {\n value: number;\n evidence:\n readonly EvidenceProvenance[];\n} {\n if (\n focus.kind === \"infinity-focus\"\n ) {\n return {\n value: nominalFNumber,\n evidence: []\n };\n }\n\n if (\n focus.kind ===\n \"ideal-symmetric-thin-lens\"\n ) {\n if (\n focus\n .pupilMagnificationAssumption !==\n \"unity\"\n ) {\n throw new InvalidScientificInputError(\n 'Ideal symmetric thin-lens focus requires pupilMagnificationAssumption \"unity\".'\n );\n }\n return {\n value:\n nominalFNumber *\n thinLensWorkingFNumberFactor(focalLengthMm, focus.objectDistanceM),\n evidence: []\n };\n }\n\n if (\n focus.kind ===\n \"supplied-working-f-number\"\n ) {\n const value =\n requirePositiveFiniteScientific(\n focus.workingFNumber.value,\n \"focus.workingFNumber.value\"\n );\n return {\n value,\n evidence:\n parseEvidenceList(\n focus.workingFNumber.evidence,\n \"focus.workingFNumber.evidence\"\n )\n };\n }\n\n throw new InvalidConfigurationError(\n \"focus.kind is invalid.\"\n );\n}\n\nfunction requirePositiveFiniteScientific(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isFinite(value) ||\n value <= 0\n ) {\n throw new InvalidScientificInputError(\n path +\n \" must be finite and greater than zero.\"\n );\n }\n return value;\n}\n\nfunction finiteFocusDistance(\n focus:\n OpticalBridgeFocusContext\n): number | null {\n if (\n focus.kind ===\n \"ideal-symmetric-thin-lens\"\n ) {\n return requirePositiveFiniteScientific(\n focus.objectDistanceM,\n \"focus.objectDistanceM\"\n );\n }\n if (\n focus.kind ===\n \"supplied-working-f-number\" &&\n focus.focus.kind === \"finite\"\n ) {\n return requirePositiveFiniteScientific(\n focus.focus.objectDistanceM,\n \"focus.focus.objectDistanceM\"\n );\n }\n return null;\n}\n\nfunction interpolateTransmission(\n model:\n Extract<\n OpticalTransmissionModel,\n { kind: \"spectral-transmission\" }\n >,\n wavelengthNanometers: number\n): number {\n const samples =\n model.samples.value;\n const first =\n samples[0]!;\n const last =\n samples[\n samples.length - 1\n ]!;\n\n if (\n wavelengthNanometers <\n first.wavelengthNanometers ||\n wavelengthNanometers >\n last.wavelengthNanometers\n ) {\n throw new InvalidScientificInputError(\n \"Scene wavelength lies outside the spectral transmission sample range.\"\n );\n }\n\n if (\n wavelengthNanometers ===\n first.wavelengthNanometers\n ) {\n return first\n .linearTransmissionFactor;\n }\n\n for (\n let index = 1;\n index < samples.length;\n index += 1\n ) {\n const right =\n samples[index]!;\n if (\n wavelengthNanometers <=\n right.wavelengthNanometers\n ) {\n const left =\n samples[index - 1]!;\n if (\n wavelengthNanometers ===\n right.wavelengthNanometers\n ) {\n return right\n .linearTransmissionFactor;\n }\n const phase =\n (wavelengthNanometers -\n left.wavelengthNanometers) /\n (right.wavelengthNanometers -\n left.wavelengthNanometers);\n return (\n left\n .linearTransmissionFactor +\n phase *\n (right\n .linearTransmissionFactor -\n left\n .linearTransmissionFactor)\n );\n }\n }\n\n return last\n .linearTransmissionFactor;\n}\n\nfunction fieldFactor(\n field:\n OpticalBridgeFieldThroughput,\n imagePointMm: {\n x: number;\n y: number;\n }\n): number {\n if (field.kind === \"unity\") {\n return 1;\n }\n if (\n field.kind ===\n \"illumination-vignetting-result\"\n ) {\n const result =\n field.result;\n const scale = Math.max(\n 1,\n Math.abs(imagePointMm.x),\n Math.abs(imagePointMm.y),\n Math.abs(result.imagePointMm.x),\n Math.abs(result.imagePointMm.y)\n );\n const tolerance =\n Number.EPSILON *\n 64 *\n scale;\n if (\n Math.abs(\n result.imagePointMm.x -\n imagePointMm.x\n ) > tolerance ||\n Math.abs(\n result.imagePointMm.y -\n imagePointMm.y\n ) > tolerance\n ) {\n throw new InvalidScientificInputError(\n \"Illumination-vignetting result imagePointMm must match the optical bridge imagePointMm.\"\n );\n }\n const factor =\n result.linearThroughputFactor;\n if (\n !Number.isFinite(factor) ||\n factor <= 0 ||\n factor > 1\n ) {\n throw new InvalidScientificInputError(\n \"Field throughput factor must be finite and in (0, 1].\"\n );\n }\n return factor;\n }\n throw new InvalidConfigurationError(\n \"fieldThroughput.kind is invalid.\"\n );\n}\n\nfunction validateSceneIdentity(\n request:\n SceneRadianceEvaluationRequest,\n result:\n SceneRadianceEvaluationResult\n): void {\n if (\n request.sampleId !==\n result.sampleId ||\n request.providerProfileId !==\n result.providerProfileId ||\n request.sceneId !==\n result.sceneId ||\n request.wavelengthNanometers !==\n result.wavelengthNanometers ||\n request.wavelengthBasis !==\n result.wavelengthBasis\n ) {\n throw new InvalidScientificInputError(\n \"Scene-radiance request/result identity must match exactly.\"\n );\n }\n}\n\n/**\n * Applies the declared pupil acceptance and exactly one transmission or field-throughput path to\n * spectral scene radiance. Returns the irradiance before the sensor stack, with conservative\n * evidence.\n *\n * The optical bridge turns outgoing scene radiance (W/m²/sr/nm) into irradiance (W/m²/nm) arriving\n * at the front of the sensor stack.\n * - Circular paraxial acceptance is pi/(4*Nworking²). At finite focus with unit pupil magnification,\n * the working f-number is N*(1+m).\n * - Spectral transmission and an effective working T-stop are alternative paths; use one.\n * - Field throughput is applied exactly once.\n * - Sensor response and PSF (point-spread function) blur happen later in the pipeline.\n * @param input - CalculateSceneRadianceToSensorIrradianceInput. See that type for coordinates, units and profile binding.\n * @returns CalculationResult<\n SceneToSensorIrradianceResult\n>. The result states its shape and scientific status; a successful call does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the root export and exact type graph.\n */\nexport function calculateSceneRadianceToSensorIrradiance(\n input:\n CalculateSceneRadianceToSensorIrradianceInput\n): CalculationResult<\n SceneToSensorIrradianceResult\n> {\n requireInputKeys(input, \"calculateSceneRadianceToSensorIrradiance\", [\"sceneRadianceRequest\", \"sceneRadianceResult\", \"profile\", \"focalLengthMm\", \"nominalFNumber\", \"focus\", \"imagePointMm\", \"fieldThroughput\", \"frontOfLensFilters\"]);\n const request =\n parseSceneRadianceEvaluationRequest(\n input.sceneRadianceRequest\n );\n const sceneResult =\n parseSceneRadianceEvaluationResult(\n input.sceneRadianceResult\n );\n validateSceneIdentity(\n request,\n sceneResult\n );\n\n const profile =\n parseSceneToSensorIrradianceProfile(\n input.profile\n );\n const focalLengthMm =\n requirePositiveFiniteScientific(\n input.focalLengthMm,\n \"focalLengthMm\"\n );\n const nominalFNumber =\n requirePositiveFiniteScientific(\n input.nominalFNumber,\n \"nominalFNumber\"\n );\n\n ensureWithin(\n focalLengthMm,\n profile.applicability\n .focalLengthMm,\n \"focalLengthMm\"\n );\n ensureWithin(\n nominalFNumber,\n profile.applicability\n .nominalFNumber,\n \"nominalFNumber\"\n );\n\n const focusDistanceM =\n finiteFocusDistance(input.focus);\n if (\n profile.applicability.focus.kind ===\n \"finite-distance-range\"\n ) {\n if (focusDistanceM === null) {\n throw new InvalidScientificInputError(\n \"Optical profile requires a finite focus distance.\"\n );\n }\n ensureWithin(\n focusDistanceM,\n profile.applicability\n .focus.objectDistanceM,\n \"focus object distance\"\n );\n }\n\n const working =\n workingFNumber(\n input.focus,\n focalLengthMm,\n nominalFNumber\n );\n const workingFNumberValue =\n working.value;\n const geometricAcceptance =\n Math.PI /\n (4 *\n workingFNumberValue *\n workingFNumberValue);\n\n if (\n !Number.isFinite(\n geometricAcceptance\n ) ||\n geometricAcceptance <= 0\n ) {\n throw new InvalidScientificInputError(\n \"Paraxial geometric acceptance must remain finite and greater than zero.\"\n );\n }\n\n const transmission =\n profile.transmission;\n\n if (\n request.wavelengthBasis !==\n transmission.wavelengthBasis\n ) {\n throw new InvalidScientificInputError(\n \"Scene-radiance and optical-transmission wavelength bases must match; implicit air/vacuum conversion is not performed.\"\n );\n }\n\n let spectralTransmissionFactor:\n number | null = null;\n let effectiveWorkingTStop:\n number | null = null;\n let tStopEquivalentTransmissionFactor:\n number | null = null;\n let effectiveAcceptance =\n geometricAcceptance;\n let transmissionEvidence:\n readonly EvidenceProvenance[];\n\n if (\n transmission.kind ===\n \"spectral-transmission\"\n ) {\n spectralTransmissionFactor =\n interpolateTransmission(\n transmission,\n request.wavelengthNanometers\n );\n effectiveAcceptance *=\n spectralTransmissionFactor;\n transmissionEvidence =\n transmission.samples.evidence;\n } else {\n ensureWithin(\n request.wavelengthNanometers,\n transmission\n .wavelengthRangeNanometers,\n \"scene wavelength\"\n );\n effectiveWorkingTStop =\n transmission.workingTStop.value;\n if (\n effectiveWorkingTStop <\n workingFNumberValue\n ) {\n throw new InvalidScientificInputError(\n \"effective working T-stop must not be smaller than the geometric working f-number for a passive optical approximation.\"\n );\n }\n tStopEquivalentTransmissionFactor =\n (workingFNumberValue /\n effectiveWorkingTStop) **\n 2;\n effectiveAcceptance =\n Math.PI /\n (4 *\n effectiveWorkingTStop *\n effectiveWorkingTStop);\n transmissionEvidence =\n transmission\n .workingTStop.evidence;\n }\n\n let frontOfLensFilterCount = 0;\n let frontOfLensFilterPolarizationModeled = false;\n let frontOfLensFilterTransmissionFactor = 1;\n let frontOfLensFilterAttenuationStops = 0;\n let frontOfLensFilterEvidence:\n readonly EvidenceProvenance[] = [];\n let frontOfLensFilterLimitations:\n readonly string[] = [];\n\n if (\n input.frontOfLensFilters !==\n undefined\n ) {\n const composed =\n composeFrontOfLensFilterTransmission({\n filters:\n input.frontOfLensFilters,\n wavelengthNanometers:\n request.wavelengthNanometers,\n wavelengthBasis:\n request.wavelengthBasis\n });\n frontOfLensFilterCount =\n composed.appliedFilterCount;\n frontOfLensFilterPolarizationModeled =\n composed.polarizationModeled;\n frontOfLensFilterTransmissionFactor =\n composed\n .combinedLinearTransmissionFactor;\n frontOfLensFilterAttenuationStops =\n composed\n .combinedAttenuationStops;\n frontOfLensFilterEvidence =\n composed.components.flatMap(\n (component) =>\n component.evidence\n );\n frontOfLensFilterLimitations =\n composed.components.flatMap(\n (component) =>\n component.limitations\n );\n effectiveAcceptance *=\n frontOfLensFilterTransmissionFactor;\n }\n\n const fieldThroughputFactor =\n fieldFactor(\n input.fieldThroughput,\n input.imagePointMm\n );\n effectiveAcceptance *=\n fieldThroughputFactor;\n\n const sceneRadiance =\n sceneResult\n .spectralRadianceWattsPerSquareMeterSteradianNanometer;\n const irradiance =\n sceneRadiance *\n effectiveAcceptance;\n\n if (\n !Number.isFinite(irradiance) ||\n irradiance < 0\n ) {\n throw new InvalidScientificInputError(\n \"Sensor-plane spectral irradiance must remain finite and non-negative.\"\n );\n }\n\n const limitations = [\n ...profile.limitations,\n ...frontOfLensFilterLimitations,\n \"Front-of-lens filter transmission is applied outside the lens throughput path exactly once and does not change geometric working f-number.\",\n \"Paraxial circular-pupil acceptance uses pi/(4*N_working^2); no universal cos^4 field falloff is added.\",\n \"Field throughput is supplied separately and applied exactly once.\",\n \"Geometric distortion area-density/Jacobian correction is not applied by this bridge.\",\n \"PSF/diffraction energy redistribution is downstream and must preserve normalized energy apart from separately owned throughput losses.\",\n \"Sensor optical stack, microlens, CFA, QE/responsivity and charge conversion are downstream.\",\n frontOfLensFilterPolarizationModeled\n ? \"Flare, ghosting, veiling glare, scene-radiance polarization (scene light is treated as unpolarized; the front-of-lens linear polarizer transmits (k1 + k2) / 2), volumetric scattering and wavelength-changing behavior are not modeled.\"\n : \"Flare, ghosting, veiling glare, polarization, volumetric scattering and wavelength-changing behavior are not modeled.\"\n ];\n\n return approximationResult(\n {\n schemaVersion:\n SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION,\n profileId: profile.profileId,\n profileVersion:\n profile.profileVersion,\n lensProfileId:\n profile.lensProfileId,\n sampleId: request.sampleId,\n providerProfileId:\n request.providerProfileId,\n sceneId: request.sceneId,\n timeSecondsFromExposureStart:\n request\n .timeSecondsFromExposureStart,\n wavelengthNanometers:\n request.wavelengthNanometers,\n wavelengthBasis:\n request.wavelengthBasis,\n inputQuantity:\n \"outgoing-spectral-radiance\",\n inputUnit:\n \"W/m^2/sr/nm\",\n outputQuantity:\n \"sensor-plane-spectral-irradiance\",\n outputUnit: \"W/m^2/nm\",\n sceneSpectralRadianceWattsPerSquareMeterSteradianNanometer:\n sceneRadiance,\n focalLengthMm,\n nominalFNumber,\n workingFNumber:\n workingFNumberValue,\n focusModel: input.focus.kind,\n paraxialGeometricAcceptanceSolidAngleSteradians:\n geometricAcceptance,\n transmissionPath:\n transmission.kind,\n spectralTransmissionFactor,\n effectiveWorkingTStop,\n tStopEquivalentTransmissionFactor,\n frontOfLensFilterCount,\n frontOfLensFilterTransmissionFactor,\n frontOfLensFilterAttenuationStops,\n frontOfLensFilterTransmissionAppliedExactlyOnce:\n true,\n frontOfLensFilterPolarizationModeled,\n fieldThroughputFactor,\n effectiveAcceptanceAfterTransmissionAndFieldSteradians:\n effectiveAcceptance,\n sensorPlaneSpectralIrradianceWattsPerSquareMeterNanometer:\n irradiance,\n scientificStatus:\n \"approximation\",\n sceneRadianceScientificStatus:\n sceneResult.scientificStatus,\n opticalProfileScientificStatus:\n profile.scientificStatus,\n sceneRadianceUncertainty:\n sceneResult.uncertainty,\n opticalUncertainty:\n transmission.uncertainty,\n radianceAmplificationApplied:\n false,\n fieldThroughputAppliedExactlyOnce:\n true,\n universalCos4FalloffApplied:\n false,\n distortionAreaCorrectionApplied:\n false,\n psfRedistributionApplied:\n false,\n sensorOpticalStackApplied:\n false,\n cfaApplied: false,\n quantumEfficiencyApplied: false,\n spectralResponsivityApplied:\n false,\n strayLightApplied: false,\n polarizationModeled: false,\n wavelengthChangingBehaviorModeled:\n false,\n volumetricScatteringModeled:\n false,\n calibratedSensorPlaneIrradianceClaimAuthorized:\n false,\n componentEvidence: {\n sceneRadiance:\n sceneResult.evidence,\n opticalProfile:\n profile.evidence,\n transmission:\n transmissionEvidence,\n workingFNumber:\n working.evidence,\n frontOfLensFilters:\n frontOfLensFilterEvidence\n },\n limitations\n },\n \"scene-radiance-to-sensor-irradiance\",\n \"1.1.0\",\n limitations\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"scene-to-sensor-irradiance.js","sourceRoot":"","sources":["../../src/optics/scene-to-sensor-irradiance.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EACL,mBAAmB,EAEpB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EACL,iBAAiB,EAGlB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,4BAA4B,EAE7B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,EACL,oCAAoC,EAErC,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,8BAA8B,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EACL,mCAAmC,EACnC,kCAAkC,EAGnC,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAI7I,MAAM,CAAC,MAAM,iDAAiD,GAC5D,OAAgB,CAAC;AAoQnB,SAAS,aAAa,CACpB,KAAc,EACd,IAAY,EACZ,WAAqC;IAErC,IACE,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EACpB,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,IAAI,GAAG,qBAAqB,CAC7B,CAAC;IACJ,CAAC;IACD,IAAI,WAAW,KAAK,IAAI;QAAE,mBAAmB,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;IACxE,OAAO,KAAsB,CAAC;AAChC,CAAC;AAED,SAAS,qBAAqB,CAC5B,KAAc,EACd,IAAY;IAEZ,IACE,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EACzB,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,IAAI,GAAG,8BAA8B,CACtC,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;AACtB,CAAC;AAED,SAAS,aAAa,CACpB,KAAc,EACd,IAAY;IAEZ,IACE,OAAO,KAAK,KAAK,QAAQ;QACzB,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EACvB,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,IAAI,GAAG,kBAAkB,CAC1B,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,qBAAqB,CAC5B,KAAc,EACd,IAAY;IAEZ,MAAM,MAAM,GACV,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC7B,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;QAChB,MAAM,IAAI,yBAAyB,CACjC,IAAI;YACF,6BAA6B,CAChC,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,eAAe,CACtB,KAAc,EACd,IAAY;IAEZ,MAAM,MAAM,GACV,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC7B,IACE,MAAM,GAAG,CAAC;QACV,MAAM,GAAG,CAAC,EACV,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,IAAI;YACF,iCAAiC,CACpC,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,UAAU,CACjB,KAAc,EACd,IAAY;IAEZ,MAAM,MAAM,GACV,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;IACrD,MAAM,OAAO,GACX,qBAAqB,CACnB,MAAM,CAAC,OAAO,EACd,IAAI,GAAG,UAAU,CAClB,CAAC;IACJ,MAAM,OAAO,GACX,qBAAqB,CACnB,MAAM,CAAC,OAAO,EACd,IAAI,GAAG,UAAU,CAClB,CAAC;IACJ,IAAI,OAAO,GAAG,OAAO,EAAE,CAAC;QACtB,MAAM,IAAI,yBAAyB,CACjC,IAAI;YACF,iDAAiD,CACpD,CAAC;IACJ,CAAC;IACD,OAAO;QACL,OAAO;QACP,OAAO;KACR,CAAC;AACJ,CAAC;AAED,SAAS,qBAAqB,CAC5B,KAAc,EACd,IAAY;IAEZ,IACE,KAAK,KAAK,YAAY;QACtB,KAAK,KAAK,eAAe,EACzB,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,IAAI,GAAG,cAAc,CACtB,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,gBAAgB,CACvB,KAAc,EACd,IAAY;IAEZ,MAAM,MAAM,GACV,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACnC,0BAA0B,CAAC,MAAM,EAAE,MAAM,EAAE,0BAA0B,EAAE,IAAI,CAAC,CAAC;IAE7E,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QAC/B,OAAO;YACL,IAAI,EAAE,UAAU;YAChB,QAAQ,EACN,eAAe,CACb,MAAM,CAAC,QAAQ,EACf,IAAI,GAAG,WAAW,CACnB;YACH,KAAK,EACH,qBAAqB,CACnB,MAAM,CAAC,KAAK,EACZ,IAAI,GAAG,QAAQ,CAChB;SACJ,CAAC;IACJ,CAAC;IAED,IACE,MAAM,CAAC,IAAI;QACX,gBAAgB,EAChB,CAAC;QACD,OAAO;YACL,IAAI,EAAE,gBAAgB;YACtB,UAAU,EACR,qBAAqB,CACnB,MAAM,CAAC,UAAU,EACjB,IAAI,GAAG,aAAa,CACrB;SACJ,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,yBAAyB,CACjC,IAAI,GAAG,mBAAmB,CAC3B,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CACvB,KAAc,EACd,IAAY;IAEZ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,yBAAyB,CACjC,IAAI,GAAG,oBAAoB,CAC5B,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CACtB,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CACf,qBAAqB,CACnB,KAAK,EACL,IAAI;QACF,GAAG;QACH,KAAK;QACL,GAAG,CACN,CACJ,CAAC;IACF,IACE,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI;QACpB,MAAM,CAAC,MAAM,EACb,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,IAAI;YACF,+BAA+B,CAClC,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,4BAA4B,CACnC,KAAc,EACd,IAAY;IAEZ,MAAM,QAAQ,GACZ,iBAAiB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAEjC,IACE,QAAQ,CAAC,IAAI,CACX,CAAC,KAAK,EAAE,EAAE,CACR,KAAK,CAAC,WAAW;QACjB,wBAAwB,CAC3B,EACD,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,IAAI;YACF,0EAA0E,CAC7E,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,oBAAoB,CAC3B,KAAc,EACd,IAAY;IAEZ,IACE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACrB,KAAK,CAAC,MAAM,GAAG,CAAC,EAChB,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,IAAI;YACF,qCAAqC,CACxC,CAAC;IACJ,CAAC;IAED,IAAI,QAAQ,GACV,MAAM,CAAC,iBAAiB,CAAC;IAC3B,OAAO,KAAK,CAAC,GAAG,CACd,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QACf,MAAM,UAAU,GACd,IAAI;YACJ,GAAG;YACH,KAAK;YACL,GAAG,CAAC;QACN,MAAM,MAAM,GACV,aAAa,CACX,KAAK,EACL,UAAU,EAAE,CAAC,sBAAsB,EAAE,0BAA0B,CAAC,CACjE,CAAC;QACJ,MAAM,oBAAoB,GACxB,qBAAqB,CACnB,MAAM,CAAC,oBAAoB,EAC3B,UAAU;YACR,uBAAuB,CAC1B,CAAC;QACJ,IACE,oBAAoB;YACpB,QAAQ,EACR,CAAC;YACD,MAAM,IAAI,yBAAyB,CACjC,IAAI;gBACF,2CAA2C,CAC9C,CAAC;QACJ,CAAC;QACD,QAAQ;YACN,oBAAoB,CAAC;QACvB,OAAO;YACL,oBAAoB;YACpB,wBAAwB,EACtB,eAAe,CACb,MAAM,CAAC,wBAAwB,EAC/B,UAAU;gBACR,2BAA2B,CAC9B;SACJ,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CACxB,KAAc,EACd,IAAY;IAEZ,MAAM,MAAM,GACV,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACnC,0BAA0B,CAAC,MAAM,EAAE,MAAM,EAAE;QACzC,iIAAiI;QACjI,uBAAuB,EAAE,CAAC,MAAM,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,SAAS,EAAE,aAAa,EAAE,cAAc,EAAE,2BAA2B,CAAC;QAC/I,wCAAwC,EAAE,CAAC,MAAM,EAAE,kBAAkB,EAAE,cAAc,EAAE,iBAAiB,EAAE,2BAA2B;YACnI,aAAa,EAAE,YAAY,EAAE,SAAS,CAAC;KAC1C,EAAE,IAAI,CAAC,CAAC;IAET,IACE,MAAM,CAAC,IAAI;QACX,uBAAuB,EACvB,CAAC;QACD,IACE,MAAM,CAAC,YAAY;YACnB,SAAS;YACT,MAAM,CAAC,yBAAyB;gBAChC,SAAS,EACT,CAAC;YACD,MAAM,IAAI,yBAAyB,CACjC,IAAI;gBACF,6DAA6D,CAChE,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GACV,qBAAqB,CACnB,MAAM,CAAC,gBAAgB,EACvB,IAAI;YACF,mBAAmB,CACtB,CAAC;QACJ,MAAM,WAAW,GACf,aAAa,CACX,MAAM,CAAC,OAAO,EACd,IAAI,GAAG,UAAU,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,CACzC,CAAC;QACJ,MAAM,WAAW,GACf,gBAAgB,CACd,MAAM,CAAC,WAAW,EAClB,IAAI,GAAG,cAAc,CACtB,CAAC;QAEJ,IACE,MAAM,KAAK,YAAY;YACvB,WAAW,CAAC,IAAI;gBACd,UAAU,EACZ,CAAC;YACD,MAAM,IAAI,yBAAyB,CACjC,IAAI;gBACF,6EAA6E,CAChF,CAAC;QACJ,CAAC;QAED,OAAO;YACL,IAAI,EACF,uBAAuB;YACzB,gBAAgB,EAAE,MAAM;YACxB,eAAe,EACb,kBAAkB,CAChB,MAAM,CAAC,eAAe,EACtB,IAAI;gBACF,kBAAkB,CACrB;YACH,OAAO,EAAE;gBACP,KAAK,EACH,oBAAoB,CAClB,WAAW,CAAC,KAAK,EACjB,IAAI;oBACF,gBAAgB,CACnB;gBACH,QAAQ,EACN,4BAA4B,CAC1B,WAAW,CAAC,QAAQ,EACpB,IAAI;oBACF,mBAAmB,CACtB;aACJ;YACD,WAAW;SACZ,CAAC;IACJ,CAAC;IAED,IACE,MAAM,CAAC,IAAI;QACX,wCAAwC,EACxC,CAAC;QACD,IACE,MAAM,CAAC,OAAO;YACd,SAAS,EACT,CAAC;YACD,MAAM,IAAI,yBAAyB,CACjC,IAAI;gBACF,uEAAuE,CAC1E,CAAC;QACJ,CAAC;QACD,IACE,MAAM,CAAC,gBAAgB;YACvB,eAAe,EACf,CAAC;YACD,MAAM,IAAI,yBAAyB,CACjC,IAAI;gBACF,mDAAmD,CACtD,CAAC;QACJ,CAAC;QACD,MAAM,gBAAgB,GACpB,aAAa,CACX,MAAM,CAAC,YAAY,EACnB,IAAI;YACF,eAAe,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,CACzC,CAAC;QACJ,OAAO;YACL,IAAI,EACF,wCAAwC;YAC1C,gBAAgB,EACd,eAAe;YACjB,YAAY,EAAE;gBACZ,KAAK,EACH,qBAAqB,CACnB,gBAAgB,CAAC,KAAK,EACtB,IAAI;oBACF,qBAAqB,CACxB;gBACH,QAAQ,EACN,iBAAiB,CACf,gBAAgB,CAAC,QAAQ,EACzB,IAAI;oBACF,wBAAwB,CAC3B;aACJ;YACD,eAAe,EACb,kBAAkB,CAChB,MAAM,CAAC,eAAe,EACtB,IAAI;gBACF,kBAAkB,CACrB;YACH,yBAAyB,EACvB,UAAU,CACR,MAAM,CAAC,yBAAyB,EAChC,IAAI;gBACF,4BAA4B,CAC/B;YACH,WAAW,EACT,gBAAgB,CACd,MAAM,CAAC,WAAW,EAClB,IAAI,GAAG,cAAc,CACtB;YACH,UAAU,EACR,qBAAqB,CACnB,MAAM,CAAC,UAAU,EACjB,IAAI;gBACF,aAAa,CAChB;SACJ,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,yBAAyB,CACjC,IAAI,GAAG,mBAAmB,CAC3B,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB,CACzB,KAAc,EACd,IAAY;IAKZ,MAAM,MAAM,GACV,4BAA4B,CAC1B,KAAK,EACL,IAAI,CACL,CAAC;IACJ,IAAI,MAAM,KAAK,aAAa,EAAE,CAAC;QAC7B,MAAM,IAAI,yBAAyB,CACjC,IAAI;YACF,qCAAqC,CACxC,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,uBAAuB,CAC9B,KAAc,EACd,IAAY;IAEZ,MAAM,MAAM,GACV,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACnC,0BAA0B,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,uBAAuB,EAAE,CAAC,MAAM,EAAE,iBAAiB,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAE1H,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC1B,OAAO;YACL,IAAI,EAAE,KAAK;SACZ,CAAC;IACJ,CAAC;IACD,IACE,MAAM,CAAC,IAAI;QACX,uBAAuB,EACvB,CAAC;QACD,OAAO;YACL,IAAI,EACF,uBAAuB;YACzB,eAAe,EACb,UAAU,CACR,MAAM,CAAC,eAAe,EACtB,IAAI;gBACF,kBAAkB,CACrB;SACJ,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,yBAAyB,CACjC,IAAI,GAAG,mBAAmB,CAC3B,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,mCAAmC,CACjD,KAAc;IAEd,MAAM,MAAM,GACV,aAAa,CACX,KAAK,EACL,gCAAgC,EAAE,CAAC,eAAe,EAAE,eAAe,EAAE,kBAAkB,EAAE,cAAc,EAAE,gCAAgC,EAAE,4BAA4B,EAAE,4BAA4B,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,mCAAmC,EAAE,6BAA6B,EAAE,WAAW,EAAE,gBAAgB,EAAE,eAAe,EAAE,UAAU,EAAE,aAAa,CAAC,CACnY,CAAC;IACJ,IACE,MAAM,CAAC,aAAa;QACpB,iDAAiD,EACjD,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,wDAAwD;YACtD,iDAAiD;YACjD,IAAI,CACP,CAAC;IACJ,CAAC;IAED,MAAM,aAAa,GACjB,aAAa,CACX,MAAM,CAAC,aAAa,EACpB,8CAA8C,EAAE,CAAC,eAAe,EAAE,gBAAgB,EAAE,OAAO,CAAC,CAC7F,CAAC;IACJ,MAAM,MAAM,GACV,qBAAqB,CACnB,MAAM,CAAC,gBAAgB,EACvB,iDAAiD,CAClD,CAAC;IACJ,MAAM,YAAY,GAChB,iBAAiB,CACf,MAAM,CAAC,YAAY,EACnB,6CAA6C,CAC9C,CAAC;IAEJ,IACE,MAAM,KAAK,YAAY;QACvB,YAAY,CAAC,gBAAgB;YAC3B,YAAY,EACd,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,gFAAgF,CACjF,CAAC;IACJ,CAAC;IAED,IACE,MAAM,CAAC,8BAA8B;QACnC,uBAAuB;QACzB,MAAM,CAAC,0BAA0B;YAC/B,6CAA6C;QAC/C,MAAM,CAAC,0BAA0B;YAC/B,KAAK;QACP,MAAM,CAAC,kBAAkB;YACvB,KAAK;QACP,MAAM,CAAC,mBAAmB;YACxB,KAAK;QACP,MAAM,CAAC,iCAAiC;YACtC,KAAK;QACP,MAAM,CAAC,2BAA2B;YAChC,KAAK,EACP,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,2FAA2F,CAC5F,CAAC;IACJ,CAAC;IAED,OAAO;QACL,aAAa,EACX,iDAAiD;QACnD,SAAS,EACP,qBAAqB,CACnB,MAAM,CAAC,SAAS,EAChB,0CAA0C,CAC3C;QACH,cAAc,EACZ,qBAAqB,CACnB,MAAM,CAAC,cAAc,EACrB,+CAA+C,CAChD;QACH,aAAa,EACX,qBAAqB,CACnB,MAAM,CAAC,aAAa,EACpB,8CAA8C,CAC/C;QACH,gBAAgB,EAAE,MAAM;QACxB,aAAa,EAAE;YACb,aAAa,EACX,UAAU,CACR,aAAa,CAAC,aAAa,EAC3B,4DAA4D,CAC7D;YACH,cAAc,EACZ,UAAU,CACR,aAAa,CAAC,cAAc,EAC5B,6DAA6D,CAC9D;YACH,KAAK,EACH,uBAAuB,CACrB,aAAa,CAAC,KAAK,EACnB,oDAAoD,CACrD;SACJ;QACD,YAAY;QACZ,8BAA8B,EAC5B,uBAAuB;QACzB,0BAA0B,EACxB,6CAA6C;QAC/C,0BAA0B,EACxB,KAAK;QACP,kBAAkB,EAAE,KAAK;QACzB,mBAAmB,EAAE,KAAK;QAC1B,iCAAiC,EAC/B,KAAK;QACP,2BAA2B,EACzB,KAAK;QACP,QAAQ,EACN,iBAAiB,CACf,MAAM,CAAC,QAAQ,EACf,yCAAyC,CAC1C;QACH,WAAW,EACT,gBAAgB,CACd,MAAM,CAAC,WAAW,EAClB,4CAA4C,CAC7C;KACJ,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CACnB,KAAa,EACb,KAAmB,EACnB,IAAY;IAEZ,IACE,KAAK,GAAG,KAAK,CAAC,OAAO;QACrB,KAAK,GAAG,KAAK,CAAC,OAAO,EACrB,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,IAAI;YACF,wDAAwD,CAC3D,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,4BAA4B,CAAC,aAAqB,EAAE,eAAuB;IACzF,OAAO,CAAC,GAAG,8BAA8B,CAAC,EAAE,aAAa,EAAE,eAAe,EAAE,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC;AACpG,CAAC;AAED,SAAS,cAAc,CACrB,KAC2B,EAC3B,aAAqB,EACrB,cAAsB;IAMtB,IACE,KAAK,CAAC,IAAI,KAAK,gBAAgB,EAC/B,CAAC;QACD,OAAO;YACL,KAAK,EAAE,cAAc;YACrB,QAAQ,EAAE,EAAE;SACb,CAAC;IACJ,CAAC;IAED,IACE,KAAK,CAAC,IAAI;QACV,2BAA2B,EAC3B,CAAC;QACD,IACE,KAAK;aACF,4BAA4B;YAC/B,OAAO,EACP,CAAC;YACD,MAAM,IAAI,2BAA2B,CACnC,gFAAgF,CACjF,CAAC;QACJ,CAAC;QACD,OAAO;YACL,KAAK,EACH,cAAc;gBACd,4BAA4B,CAAC,aAAa,EAAE,KAAK,CAAC,eAAe,CAAC;YACpE,QAAQ,EAAE,EAAE;SACb,CAAC;IACJ,CAAC;IAED,IACE,KAAK,CAAC,IAAI;QACV,2BAA2B,EAC3B,CAAC;QACD,MAAM,KAAK,GACT,+BAA+B,CAC7B,KAAK,CAAC,cAAc,CAAC,KAAK,EAC1B,4BAA4B,CAC7B,CAAC;QACJ,OAAO;YACL,KAAK;YACL,QAAQ,EACN,iBAAiB,CACf,KAAK,CAAC,cAAc,CAAC,QAAQ,EAC7B,+BAA+B,CAChC;SACJ,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,yBAAyB,CACjC,wBAAwB,CACzB,CAAC;AACJ,CAAC;AAED,SAAS,+BAA+B,CACtC,KAAc,EACd,IAAY;IAEZ,IACE,OAAO,KAAK,KAAK,QAAQ;QACzB,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QACvB,KAAK,IAAI,CAAC,EACV,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,IAAI;YACF,wCAAwC,CAC3C,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,mBAAmB,CAC1B,KAC2B;IAE3B,IACE,KAAK,CAAC,IAAI;QACV,2BAA2B,EAC3B,CAAC;QACD,OAAO,+BAA+B,CACpC,KAAK,CAAC,eAAe,EACrB,uBAAuB,CACxB,CAAC;IACJ,CAAC;IACD,IACE,KAAK,CAAC,IAAI;QACR,2BAA2B;QAC7B,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,EAC7B,CAAC;QACD,OAAO,+BAA+B,CACpC,KAAK,CAAC,KAAK,CAAC,eAAe,EAC3B,6BAA6B,CAC9B,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,uBAAuB,CAC9B,KAIG,EACH,oBAA4B;IAE5B,MAAM,OAAO,GACX,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;IACtB,MAAM,KAAK,GACT,OAAO,CAAC,CAAC,CAAE,CAAC;IACd,MAAM,IAAI,GACR,OAAO,CACL,OAAO,CAAC,MAAM,GAAG,CAAC,CAClB,CAAC;IAEL,IACE,oBAAoB;QAClB,KAAK,CAAC,oBAAoB;QAC5B,oBAAoB;YAClB,IAAI,CAAC,oBAAoB,EAC3B,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,uEAAuE,CACxE,CAAC;IACJ,CAAC;IAED,IACE,oBAAoB;QACpB,KAAK,CAAC,oBAAoB,EAC1B,CAAC;QACD,OAAO,KAAK;aACT,wBAAwB,CAAC;IAC9B,CAAC;IAED,KACE,IAAI,KAAK,GAAG,CAAC,EACb,KAAK,GAAG,OAAO,CAAC,MAAM,EACtB,KAAK,IAAI,CAAC,EACV,CAAC;QACD,MAAM,KAAK,GACT,OAAO,CAAC,KAAK,CAAE,CAAC;QAClB,IACE,oBAAoB;YACpB,KAAK,CAAC,oBAAoB,EAC1B,CAAC;YACD,MAAM,IAAI,GACR,OAAO,CAAC,KAAK,GAAG,CAAC,CAAE,CAAC;YACtB,IACE,oBAAoB;gBACpB,KAAK,CAAC,oBAAoB,EAC1B,CAAC;gBACD,OAAO,KAAK;qBACT,wBAAwB,CAAC;YAC9B,CAAC;YACD,MAAM,KAAK,GACT,CAAC,oBAAoB;gBACnB,IAAI,CAAC,oBAAoB,CAAC;gBAC5B,CAAC,KAAK,CAAC,oBAAoB;oBACzB,IAAI,CAAC,oBAAoB,CAAC,CAAC;YAC/B,OAAO,CACL,IAAI;iBACD,wBAAwB;gBAC3B,KAAK;oBACH,CAAC,KAAK;yBACD,wBAAwB;wBAC3B,IAAI;6BACD,wBAAwB,CAAC,CACjC,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,IAAI;SACR,wBAAwB,CAAC;AAC9B,CAAC;AAED,SAAS,WAAW,CAClB,KAC8B,EAC9B,YAGC;IAED,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC3B,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IACE,KAAK,CAAC,IAAI;QACV,gCAAgC,EAChC,CAAC;QACD,MAAM,MAAM,GACV,KAAK,CAAC,MAAM,CAAC;QACf,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CACpB,CAAC,EACD,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EACxB,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EACxB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,EAC/B,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAChC,CAAC;QACF,MAAM,SAAS,GACb,MAAM,CAAC,OAAO;YACd,EAAE;YACF,KAAK,CAAC;QACR,IACE,IAAI,CAAC,GAAG,CACN,MAAM,CAAC,YAAY,CAAC,CAAC;YACnB,YAAY,CAAC,CAAC,CACjB,GAAG,SAAS;YACb,IAAI,CAAC,GAAG,CACN,MAAM,CAAC,YAAY,CAAC,CAAC;gBACnB,YAAY,CAAC,CAAC,CACjB,GAAG,SAAS,EACb,CAAC;YACD,MAAM,IAAI,2BAA2B,CACnC,yFAAyF,CAC1F,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GACV,MAAM,CAAC,sBAAsB,CAAC;QAChC,IACE,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;YACxB,MAAM,IAAI,CAAC;YACX,MAAM,GAAG,CAAC,EACV,CAAC;YACD,MAAM,IAAI,2BAA2B,CACnC,uDAAuD,CACxD,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,MAAM,IAAI,yBAAyB,CACjC,kCAAkC,CACnC,CAAC;AACJ,CAAC;AAED,SAAS,qBAAqB,CAC5B,OACgC,EAChC,MAC+B;IAE/B,IACE,OAAO,CAAC,QAAQ;QACd,MAAM,CAAC,QAAQ;QACjB,OAAO,CAAC,iBAAiB;YACvB,MAAM,CAAC,iBAAiB;QAC1B,OAAO,CAAC,OAAO;YACb,MAAM,CAAC,OAAO;QAChB,OAAO,CAAC,oBAAoB;YAC1B,MAAM,CAAC,oBAAoB;QAC7B,OAAO,CAAC,eAAe;YACrB,MAAM,CAAC,eAAe,EACxB,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,4DAA4D,CAC7D,CAAC;IACJ,CAAC;AACH,CAAC;AAgBD,SAAS,sCAAsC,CAC7C,KAAoH;IAEpH,MAAM,OAAO,GACX,mCAAmC,CACjC,KAAK,CAAC,OAAO,CACd,CAAC;IACJ,MAAM,aAAa,GACjB,+BAA+B,CAC7B,KAAK,CAAC,aAAa,EACnB,eAAe,CAChB,CAAC;IACJ,MAAM,cAAc,GAClB,+BAA+B,CAC7B,KAAK,CAAC,cAAc,EACpB,gBAAgB,CACjB,CAAC;IAEJ,YAAY,CACV,aAAa,EACb,OAAO,CAAC,aAAa;SAClB,aAAa,EAChB,eAAe,CAChB,CAAC;IACF,YAAY,CACV,cAAc,EACd,OAAO,CAAC,aAAa;SAClB,cAAc,EACjB,gBAAgB,CACjB,CAAC;IAEF,MAAM,cAAc,GAClB,mBAAmB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACnC,IACE,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI;QAC9B,uBAAuB,EACzB,CAAC;QACD,IAAI,cAAc,KAAK,IAAI,EAAE,CAAC;YAC5B,MAAM,IAAI,2BAA2B,CACnC,mDAAmD,CACpD,CAAC;QACJ,CAAC;QACD,YAAY,CACV,cAAc,EACd,OAAO,CAAC,aAAa;aAClB,KAAK,CAAC,eAAe,EACxB,uBAAuB,CACxB,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GACX,cAAc,CACZ,KAAK,CAAC,KAAK,EACX,aAAa,EACb,cAAc,CACf,CAAC;IACJ,MAAM,mBAAmB,GACvB,OAAO,CAAC,KAAK,CAAC;IAChB,MAAM,mBAAmB,GACvB,IAAI,CAAC,EAAE;QACP,CAAC,CAAC;YACA,mBAAmB;YACnB,mBAAmB,CAAC,CAAC;IAEzB,IACE,CAAC,MAAM,CAAC,QAAQ,CACd,mBAAmB,CACpB;QACD,mBAAmB,IAAI,CAAC,EACxB,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,yEAAyE,CAC1E,CAAC;IACJ,CAAC;IAED,MAAM,YAAY,GAChB,OAAO,CAAC,YAAY,CAAC;IACvB,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,YAAY,EAAE,CAAC;AACrH,CAAC;AAmBD;;;;GAIG;AACH,SAAS,oCAAoC,CAC3C,QAAiD,EACjD,KAC0E,EAC1E,OAAuC,EACvC,WAA0C;IAE1C,MAAM,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,YAAY,EAAE,GAAG,QAAQ,CAAC;IAC5E,IACE,OAAO,CAAC,eAAe;QACvB,YAAY,CAAC,eAAe,EAC5B,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,uHAAuH,CACxH,CAAC;IACJ,CAAC;IAED,IAAI,0BAA0B,GACZ,IAAI,CAAC;IACvB,IAAI,qBAAqB,GACP,IAAI,CAAC;IACvB,IAAI,iCAAiC,GACnB,IAAI,CAAC;IACvB,IAAI,mBAAmB,GACrB,mBAAmB,CAAC;IACtB,IAAI,oBAC2B,CAAC;IAEhC,IACE,YAAY,CAAC,IAAI;QACjB,uBAAuB,EACvB,CAAC;QACD,0BAA0B;YACxB,uBAAuB,CACrB,YAAY,EACZ,OAAO,CAAC,oBAAoB,CAC7B,CAAC;QACJ,mBAAmB;YACjB,0BAA0B,CAAC;QAC7B,oBAAoB;YAClB,YAAY,CAAC,OAAO,CAAC,QAAQ,CAAC;IAClC,CAAC;SAAM,CAAC;QACN,YAAY,CACV,OAAO,CAAC,oBAAoB,EAC5B,YAAY;aACT,yBAAyB,EAC5B,kBAAkB,CACnB,CAAC;QACF,qBAAqB;YACnB,YAAY,CAAC,YAAY,CAAC,KAAK,CAAC;QAClC,IACE,qBAAqB;YACrB,mBAAmB,EACnB,CAAC;YACD,MAAM,IAAI,2BAA2B,CACnC,uHAAuH,CACxH,CAAC;QACJ,CAAC;QACD,iCAAiC;YAC/B,CAAC,mBAAmB;gBAClB,qBAAqB,CAAC;gBACxB,CAAC,CAAC;QACJ,mBAAmB;YACjB,IAAI,CAAC,EAAE;gBACP,CAAC,CAAC;oBACA,qBAAqB;oBACrB,qBAAqB,CAAC,CAAC;QAC3B,oBAAoB;YAClB,YAAY;iBACT,YAAY,CAAC,QAAQ,CAAC;IAC7B,CAAC;IAED,IAAI,sBAAsB,GAAG,CAAC,CAAC;IAC/B,IAAI,oCAAoC,GAAG,KAAK,CAAC;IACjD,IAAI,mCAAmC,GAAG,CAAC,CAAC;IAC5C,IAAI,iCAAiC,GAAG,CAAC,CAAC;IAC1C,IAAI,yBAAyB,GACK,EAAE,CAAC;IACrC,IAAI,4BAA4B,GACV,EAAE,CAAC;IAEzB,IACE,KAAK,CAAC,kBAAkB;QACxB,SAAS,EACT,CAAC;QACD,MAAM,QAAQ,GACZ,oCAAoC,CAAC;YACnC,OAAO,EACL,KAAK,CAAC,kBAAkB;YAC1B,oBAAoB,EAClB,OAAO,CAAC,oBAAoB;YAC9B,eAAe,EACb,OAAO,CAAC,eAAe;SAC1B,CAAC,CAAC;QACL,sBAAsB;YACpB,QAAQ,CAAC,kBAAkB,CAAC;QAC9B,oCAAoC;YAClC,QAAQ,CAAC,mBAAmB,CAAC;QAC/B,mCAAmC;YACjC,QAAQ;iBACL,gCAAgC,CAAC;QACtC,iCAAiC;YAC/B,QAAQ;iBACL,wBAAwB,CAAC;QAC9B,yBAAyB;YACvB,QAAQ,CAAC,UAAU,CAAC,OAAO,CACzB,CAAC,SAAS,EAAE,EAAE,CACZ,SAAS,CAAC,QAAQ,CACrB,CAAC;QACJ,4BAA4B;YAC1B,QAAQ,CAAC,UAAU,CAAC,OAAO,CACzB,CAAC,SAAS,EAAE,EAAE,CACZ,SAAS,CAAC,WAAW,CACxB,CAAC;QACJ,mBAAmB;YACjB,mCAAmC,CAAC;IACxC,CAAC;IAED,MAAM,qBAAqB,GACzB,WAAW,CACT,KAAK,CAAC,eAAe,EACrB,KAAK,CAAC,YAAY,CACnB,CAAC;IACJ,mBAAmB;QACjB,qBAAqB,CAAC;IAExB,MAAM,aAAa,GACjB,WAAW;SACR,qDAAqD,CAAC;IAC3D,MAAM,UAAU,GACd,aAAa;QACb,mBAAmB,CAAC;IAEtB,IACE,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC;QAC5B,UAAU,GAAG,CAAC,EACd,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,uEAAuE,CACxE,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,iCAAiC,EAAE,mBAAmB,EAAE,oBAAoB;QACtI,sBAAsB,EAAE,oCAAoC,EAAE,mCAAmC,EAAE,iCAAiC;QACpI,yBAAyB,EAAE,4BAA4B,EAAE,qBAAqB,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC;AAChH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,2CAA2C,CACzD,MAA4I;IAG5I,MAAM,QAAQ,GAAG,sCAAsC,CAAC,MAAM,CAAC,CAAC;IAChE,MAAM,kBAAkB,GAAG,MAAM,CAAC,kBAAkB,CAAC;IACrD,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,eAAe,EAAE,EAAE;QACxD,qBAAqB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACvC,OAAO,oCAAoC,CAAC,QAAQ,EAAE,EAAE,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,UAAU,CAAC;IAC3I,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,wCAAwC,CACtD,KAC+C;IAI/C,gBAAgB,CAAC,KAAK,EAAE,0CAA0C,EAAE,CAAC,sBAAsB,EAAE,qBAAqB,EAAE,SAAS,EAAE,eAAe,EAAE,gBAAgB,EAAE,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,oBAAoB,CAAC,CAAC,CAAC;IACrO,MAAM,OAAO,GACX,mCAAmC,CACjC,KAAK,CAAC,oBAAoB,CAC3B,CAAC;IACJ,MAAM,WAAW,GACf,kCAAkC,CAChC,KAAK,CAAC,mBAAmB,CAC1B,CAAC;IACJ,qBAAqB,CACnB,OAAO,EACP,WAAW,CACZ,CAAC;IAEF,MAAM,QAAQ,GAAG,sCAAsC,CAAC,KAAK,CAAC,CAAC;IAC/D,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,YAAY,EAAE,GAAG,QAAQ,CAAC;IAC7H,MAAM,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,iCAAiC,EAAE,mBAAmB,EAAE,oBAAoB,EACrI,sBAAsB,EAAE,oCAAoC,EAAE,mCAAmC,EAAE,iCAAiC,EACpI,yBAAyB,EAAE,4BAA4B,EAAE,qBAAqB,EAAE,aAAa,EAAE,UAAU,EAAE,GAC3G,oCAAoC,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC;IAE9E,MAAM,WAAW,GAAG;QAClB,GAAG,OAAO,CAAC,WAAW;QACtB,GAAG,4BAA4B;QAC/B,4IAA4I;QAC5I,wGAAwG;QACxG,mEAAmE;QACnE,sFAAsF;QACtF,wIAAwI;QACxI,6FAA6F;QAC7F,oCAAoC;YAClC,CAAC,CAAC,0OAA0O;YAC5O,CAAC,CAAC,uHAAuH;KAC5H,CAAC;IAEF,OAAO,mBAAmB,CACxB;QACE,aAAa,EACX,iDAAiD;QACnD,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,cAAc,EACZ,OAAO,CAAC,cAAc;QACxB,aAAa,EACX,OAAO,CAAC,aAAa;QACvB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,iBAAiB,EACf,OAAO,CAAC,iBAAiB;QAC3B,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,4BAA4B,EAC1B,OAAO;aACJ,4BAA4B;QACjC,oBAAoB,EAClB,OAAO,CAAC,oBAAoB;QAC9B,eAAe,EACb,OAAO,CAAC,eAAe;QACzB,aAAa,EACX,4BAA4B;QAC9B,SAAS,EACP,aAAa;QACf,cAAc,EACZ,kCAAkC;QACpC,UAAU,EAAE,UAAU;QACtB,0DAA0D,EACxD,aAAa;QACf,aAAa;QACb,cAAc;QACd,cAAc,EACZ,mBAAmB;QACrB,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI;QAC5B,+CAA+C,EAC7C,mBAAmB;QACrB,gBAAgB,EACd,YAAY,CAAC,IAAI;QACnB,0BAA0B;QAC1B,qBAAqB;QACrB,iCAAiC;QACjC,sBAAsB;QACtB,mCAAmC;QACnC,iCAAiC;QACjC,+CAA+C,EAC7C,IAAI;QACN,oCAAoC;QACpC,qBAAqB;QACrB,sDAAsD,EACpD,mBAAmB;QACrB,yDAAyD,EACvD,UAAU;QACZ,gBAAgB,EACd,eAAe;QACjB,6BAA6B,EAC3B,WAAW,CAAC,gBAAgB;QAC9B,8BAA8B,EAC5B,OAAO,CAAC,gBAAgB;QAC1B,wBAAwB,EACtB,WAAW,CAAC,WAAW;QACzB,kBAAkB,EAChB,YAAY,CAAC,WAAW;QAC1B,4BAA4B,EAC1B,KAAK;QACP,iCAAiC,EAC/B,IAAI;QACN,2BAA2B,EACzB,KAAK;QACP,+BAA+B,EAC7B,KAAK;QACP,wBAAwB,EACtB,KAAK;QACP,yBAAyB,EACvB,KAAK;QACP,UAAU,EAAE,KAAK;QACjB,wBAAwB,EAAE,KAAK;QAC/B,2BAA2B,EACzB,KAAK;QACP,iBAAiB,EAAE,KAAK;QACxB,mBAAmB,EAAE,KAAK;QAC1B,iCAAiC,EAC/B,KAAK;QACP,2BAA2B,EACzB,KAAK;QACP,8CAA8C,EAC5C,KAAK;QACP,iBAAiB,EAAE;YACjB,aAAa,EACX,WAAW,CAAC,QAAQ;YACtB,cAAc,EACZ,OAAO,CAAC,QAAQ;YAClB,YAAY,EACV,oBAAoB;YACtB,cAAc,EACZ,OAAO,CAAC,QAAQ;YAClB,kBAAkB,EAChB,yBAAyB;SAC5B;QACD,WAAW;KACZ,EACD,qCAAqC,EACrC,OAAO,EACP,WAAW,CACZ,CAAC;AACJ,CAAC","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Scene-to-sensor optical bridge: from spectral scene radiance to the irradiance that reaches the\n * sensor.\n *\n * The profile parser validates untrusted input and returns the normalized typed contract. Unknown\n * enum values, missing required fields and incompatible scientific data are rejected here.\n *\n * The calculation applies the declared pupil acceptance and exactly one transmission or\n * field-throughput path. It returns the irradiance before the sensor stack, with conservative\n * evidence.\n *\n * The optical bridge turns outgoing scene radiance (W/m²/sr/nm) into irradiance (W/m²/nm) arriving\n * at the front of the sensor stack.\n * - Circular paraxial acceptance is pi/(4*Nworking²). At finite focus with unit pupil magnification,\n * the working f-number is N*(1+m).\n * - Spectral transmission and an effective working T-stop are alternative paths; use one.\n * - Field throughput is applied exactly once.\n * - Sensor response and PSF (point-spread function) blur happen later in the pipeline.\n * @see docs/SCENE_SENSOR_QUADRATURE.md for the equations, units, coordinate conventions, known\n * blockers and support limits.\n */\n\nimport {\n approximationResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport {\n parseEvidenceList,\n type EvidenceBackedFact,\n type EvidenceProvenance\n} from \"../core/evidence-provenance.js\";\nimport {\n parseSpectralWavelengthBasis,\n type SpectralWavelengthBasis\n} from \"../core/spectral.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport {\n composeFrontOfLensFilterTransmission,\n type FrontOfLensFilterProfile\n} from \"./front-of-lens-filter.js\";\nimport type { IlluminationVignetting } from \"./illumination-vignetting.js\";\nimport { calculateThinLensImageDistance } from \"./thin-lens.js\";\nimport {\n parseSceneRadianceEvaluationRequest,\n parseSceneRadianceEvaluationResult,\n type SceneRadianceEvaluationRequest,\n type SceneRadianceEvaluationResult\n} from \"../schema/scene-radiance.js\";\nimport { requireInputKeys, rejectUnknownFields, rejectUnknownVariantFields, UNCERTAINTY_VARIANT_FIELDS } from \"../core/record-validation.js\";\n\ntype UnknownRecord = Record<string, unknown>;\n\nexport const SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION =\n \"0.1.0\" as const;\n\nexport type OpticalBridgeScientificStatus =\n | \"calibrated\"\n | \"approximation\";\n\nexport type OpticalBridgeUncertainty =\n | {\n kind: \"relative\";\n fraction: number;\n basis: string;\n }\n | {\n kind: \"not-quantified\";\n limitation: string;\n };\n\nexport interface NumericRange {\n minimum: number;\n maximum: number;\n}\n\nexport type OpticalBridgeFocusApplicability =\n | {\n kind: \"any\";\n }\n | {\n kind: \"finite-distance-range\";\n objectDistanceM: NumericRange;\n };\n\nexport interface SpectralTransmissionSample {\n wavelengthNanometers: number;\n linearTransmissionFactor: number;\n}\n\nexport type OpticalTransmissionModel =\n | {\n kind: \"spectral-transmission\";\n scientificStatus:\n OpticalBridgeScientificStatus;\n wavelengthBasis:\n Exclude<\n SpectralWavelengthBasis,\n \"unspecified\"\n >;\n samples:\n EvidenceBackedFact<\n readonly SpectralTransmissionSample[]\n >;\n uncertainty:\n OpticalBridgeUncertainty;\n }\n | {\n kind:\n \"effective-working-t-stop-approximation\";\n scientificStatus: \"approximation\";\n workingTStop:\n EvidenceBackedFact<number>;\n wavelengthBasis:\n Exclude<\n SpectralWavelengthBasis,\n \"unspecified\"\n >;\n wavelengthRangeNanometers:\n NumericRange;\n uncertainty:\n OpticalBridgeUncertainty;\n limitation: string;\n };\n\nexport interface SceneToSensorIrradianceProfile {\n schemaVersion:\n typeof SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION;\n profileId: string;\n profileVersion: string;\n lensProfileId: string;\n scientificStatus:\n OpticalBridgeScientificStatus;\n applicability: {\n focalLengthMm: NumericRange;\n nominalFNumber: NumericRange;\n focus:\n OpticalBridgeFocusApplicability;\n };\n transmission:\n OpticalTransmissionModel;\n distortionAreaMappingOwnership:\n \"not-applied-by-bridge\";\n psfRedistributionOwnership:\n \"downstream-normalized-energy-redistribution\";\n sensorOpticalStackIncluded: false;\n strayLightIncluded: false;\n polarizationModeled: false;\n wavelengthChangingBehaviorModeled: false;\n volumetricScatteringModeled: false;\n evidence: readonly EvidenceProvenance[];\n limitations: readonly string[];\n}\n\nexport type OpticalBridgeFocusContext =\n | {\n kind: \"infinity-focus\";\n }\n | {\n kind:\n \"ideal-symmetric-thin-lens\";\n objectDistanceM: number;\n pupilMagnificationAssumption:\n \"unity\";\n }\n | {\n kind:\n \"supplied-working-f-number\";\n focus:\n | {\n kind: \"infinity\";\n }\n | {\n kind: \"finite\";\n objectDistanceM: number;\n };\n workingFNumber:\n EvidenceBackedFact<number>;\n basis: string;\n };\n\nexport type OpticalBridgeFieldThroughput =\n | {\n kind: \"unity\";\n }\n | {\n kind:\n \"illumination-vignetting-result\";\n result:\n IlluminationVignetting;\n };\n\nexport interface CalculateSceneRadianceToSensorIrradianceInput {\n sceneRadianceRequest:\n SceneRadianceEvaluationRequest;\n sceneRadianceResult:\n SceneRadianceEvaluationResult;\n profile:\n SceneToSensorIrradianceProfile;\n focalLengthMm: number;\n nominalFNumber: number;\n focus:\n OpticalBridgeFocusContext;\n imagePointMm: {\n x: number;\n y: number;\n };\n fieldThroughput:\n OpticalBridgeFieldThroughput;\n frontOfLensFilters?:\n readonly FrontOfLensFilterProfile[];\n}\n\nexport interface SceneToSensorIrradianceResult {\n schemaVersion:\n typeof SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION;\n profileId: string;\n profileVersion: string;\n lensProfileId: string;\n sampleId: string;\n providerProfileId: string;\n sceneId: string;\n timeSecondsFromExposureStart: number;\n wavelengthNanometers: number;\n wavelengthBasis:\n Exclude<\n SpectralWavelengthBasis,\n \"unspecified\"\n >;\n inputQuantity:\n \"outgoing-spectral-radiance\";\n inputUnit:\n \"W/m^2/sr/nm\";\n outputQuantity:\n \"sensor-plane-spectral-irradiance\";\n outputUnit:\n \"W/m^2/nm\";\n sceneSpectralRadianceWattsPerSquareMeterSteradianNanometer:\n number;\n focalLengthMm: number;\n nominalFNumber: number;\n workingFNumber: number;\n focusModel:\n OpticalBridgeFocusContext[\"kind\"];\n paraxialGeometricAcceptanceSolidAngleSteradians:\n number;\n transmissionPath:\n OpticalTransmissionModel[\"kind\"];\n spectralTransmissionFactor:\n number | null;\n effectiveWorkingTStop:\n number | null;\n tStopEquivalentTransmissionFactor:\n number | null;\n frontOfLensFilterCount: number;\n frontOfLensFilterTransmissionFactor:\n number;\n frontOfLensFilterAttenuationStops:\n number;\n frontOfLensFilterTransmissionAppliedExactlyOnce:\n true;\n /**\n * True when a front-of-lens linear polarizer was applied. Scene radiance is unpolarized in this\n * bridge, so a polarizer transmits (k1 + k2) / 2.\n */\n frontOfLensFilterPolarizationModeled:\n boolean;\n fieldThroughputFactor: number;\n effectiveAcceptanceAfterTransmissionAndFieldSteradians:\n number;\n sensorPlaneSpectralIrradianceWattsPerSquareMeterNanometer:\n number;\n scientificStatus: \"approximation\";\n sceneRadianceScientificStatus:\n SceneRadianceEvaluationResult[\"scientificStatus\"];\n opticalProfileScientificStatus:\n OpticalBridgeScientificStatus;\n sceneRadianceUncertainty:\n SceneRadianceEvaluationResult[\"uncertainty\"];\n opticalUncertainty:\n OpticalBridgeUncertainty;\n radianceAmplificationApplied: false;\n fieldThroughputAppliedExactlyOnce:\n true;\n universalCos4FalloffApplied: false;\n distortionAreaCorrectionApplied: false;\n psfRedistributionApplied: false;\n sensorOpticalStackApplied: false;\n cfaApplied: false;\n quantumEfficiencyApplied: false;\n spectralResponsivityApplied: false;\n strayLightApplied: false;\n polarizationModeled: false;\n wavelengthChangingBehaviorModeled:\n false;\n volumetricScatteringModeled: false;\n calibratedSensorPlaneIrradianceClaimAuthorized:\n false;\n componentEvidence: {\n sceneRadiance:\n readonly EvidenceProvenance[];\n opticalProfile:\n readonly EvidenceProvenance[];\n transmission:\n readonly EvidenceProvenance[];\n workingFNumber:\n readonly EvidenceProvenance[];\n frontOfLensFilters:\n readonly EvidenceProvenance[];\n };\n limitations:\n readonly string[];\n}\n\nfunction requireRecord(\n value: unknown,\n path: string,\n allowedKeys: readonly string[] | null\n): UnknownRecord {\n if (\n typeof value !== \"object\" ||\n value === null ||\n Array.isArray(value)\n ) {\n throw new InvalidConfigurationError(\n path + \" must be an object.\"\n );\n }\n if (allowedKeys !== null) rejectUnknownFields(value, allowedKeys, path);\n return value as UnknownRecord;\n}\n\nfunction requireNonEmptyString(\n value: unknown,\n path: string\n): string {\n if (\n typeof value !== \"string\" ||\n value.trim().length === 0\n ) {\n throw new InvalidConfigurationError(\n path + \" must be a non-empty string.\"\n );\n }\n return value.trim();\n}\n\nfunction requireFinite(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isFinite(value)\n ) {\n throw new InvalidConfigurationError(\n path + \" must be finite.\"\n );\n }\n return value;\n}\n\nfunction requirePositiveFinite(\n value: unknown,\n path: string\n): number {\n const parsed =\n requireFinite(value, path);\n if (parsed <= 0) {\n throw new InvalidConfigurationError(\n path +\n \" must be greater than zero.\"\n );\n }\n return parsed;\n}\n\nfunction requireFraction(\n value: unknown,\n path: string\n): number {\n const parsed =\n requireFinite(value, path);\n if (\n parsed < 0 ||\n parsed > 1\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must be from zero through one.\"\n );\n }\n return parsed;\n}\n\nfunction parseRange(\n value: unknown,\n path: string\n): NumericRange {\n const record =\n requireRecord(value, path, [\"minimum\", \"maximum\"]);\n const minimum =\n requirePositiveFinite(\n record.minimum,\n path + \".minimum\"\n );\n const maximum =\n requirePositiveFinite(\n record.maximum,\n path + \".maximum\"\n );\n if (minimum > maximum) {\n throw new InvalidConfigurationError(\n path +\n \".minimum must be less than or equal to maximum.\"\n );\n }\n return {\n minimum,\n maximum\n };\n}\n\nfunction parseScientificStatus(\n value: unknown,\n path: string\n): OpticalBridgeScientificStatus {\n if (\n value !== \"calibrated\" &&\n value !== \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n path + \" is invalid.\"\n );\n }\n return value;\n}\n\nfunction parseUncertainty(\n value: unknown,\n path: string\n): OpticalBridgeUncertainty {\n const record =\n requireRecord(value, path, null);\n rejectUnknownVariantFields(record, \"kind\", UNCERTAINTY_VARIANT_FIELDS, path);\n\n if (record.kind === \"relative\") {\n return {\n kind: \"relative\",\n fraction:\n requireFraction(\n record.fraction,\n path + \".fraction\"\n ),\n basis:\n requireNonEmptyString(\n record.basis,\n path + \".basis\"\n )\n };\n }\n\n if (\n record.kind ===\n \"not-quantified\"\n ) {\n return {\n kind: \"not-quantified\",\n limitation:\n requireNonEmptyString(\n record.limitation,\n path + \".limitation\"\n )\n };\n }\n\n throw new InvalidConfigurationError(\n path + \".kind is invalid.\"\n );\n}\n\nfunction parseLimitations(\n value: unknown,\n path: string\n): readonly string[] {\n if (!Array.isArray(value)) {\n throw new InvalidConfigurationError(\n path + \" must be an array.\"\n );\n }\n const parsed = value.map(\n (entry, index) =>\n requireNonEmptyString(\n entry,\n path +\n \"[\" +\n index +\n \"]\"\n )\n );\n if (\n new Set(parsed).size !==\n parsed.length\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must not contain duplicates.\"\n );\n }\n return parsed;\n}\n\nfunction parseReusableNumericEvidence(\n value: unknown,\n path: string\n): readonly EvidenceProvenance[] {\n const evidence =\n parseEvidenceList(value, path);\n\n if (\n evidence.some(\n (entry) =>\n entry.reuseStatus ===\n \"factual-reference-only\"\n )\n ) {\n throw new InvalidConfigurationError(\n path +\n \" for numeric optical curve data must be reusable-data or photivra-owned.\"\n );\n }\n return evidence;\n}\n\nfunction parseSpectralSamples(\n value: unknown,\n path: string\n): readonly SpectralTransmissionSample[] {\n if (\n !Array.isArray(value) ||\n value.length < 2\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must contain at least two samples.\"\n );\n }\n\n let previous =\n Number.NEGATIVE_INFINITY;\n return value.map(\n (entry, index) => {\n const samplePath =\n path +\n \"[\" +\n index +\n \"]\";\n const record =\n requireRecord(\n entry,\n samplePath, [\"wavelengthNanometers\", \"linearTransmissionFactor\"]\n );\n const wavelengthNanometers =\n requirePositiveFinite(\n record.wavelengthNanometers,\n samplePath +\n \".wavelengthNanometers\"\n );\n if (\n wavelengthNanometers <=\n previous\n ) {\n throw new InvalidConfigurationError(\n path +\n \" wavelengths must be strictly increasing.\"\n );\n }\n previous =\n wavelengthNanometers;\n return {\n wavelengthNanometers,\n linearTransmissionFactor:\n requireFraction(\n record.linearTransmissionFactor,\n samplePath +\n \".linearTransmissionFactor\"\n )\n };\n }\n );\n}\n\nfunction parseTransmission(\n value: unknown,\n path: string\n): OpticalTransmissionModel {\n const record =\n requireRecord(value, path, null);\n rejectUnknownVariantFields(record, \"kind\", {\n // workingTStop and wavelengthRangeNanometers (spectral) and samples (working T-stop) are rejected below with their own messages.\n \"spectral-transmission\": [\"kind\", \"scientificStatus\", \"wavelengthBasis\", \"samples\", \"uncertainty\", \"workingTStop\", \"wavelengthRangeNanometers\"],\n \"effective-working-t-stop-approximation\": [\"kind\", \"scientificStatus\", \"workingTStop\", \"wavelengthBasis\", \"wavelengthRangeNanometers\",\n \"uncertainty\", \"limitation\", \"samples\"]\n }, path);\n\n if (\n record.kind ===\n \"spectral-transmission\"\n ) {\n if (\n record.workingTStop !==\n undefined ||\n record.wavelengthRangeNanometers !==\n undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" spectral-transmission must not include T-stop-only fields.\"\n );\n }\n\n const status =\n parseScientificStatus(\n record.scientificStatus,\n path +\n \".scientificStatus\"\n );\n const samplesFact =\n requireRecord(\n record.samples,\n path + \".samples\", [\"value\", \"evidence\"]\n );\n const uncertainty =\n parseUncertainty(\n record.uncertainty,\n path + \".uncertainty\"\n );\n\n if (\n status === \"calibrated\" &&\n uncertainty.kind !==\n \"relative\"\n ) {\n throw new InvalidConfigurationError(\n path +\n \" calibrated spectral transmission requires quantified relative uncertainty.\"\n );\n }\n\n return {\n kind:\n \"spectral-transmission\",\n scientificStatus: status,\n wavelengthBasis:\n parseResolvedBasis(\n record.wavelengthBasis,\n path +\n \".wavelengthBasis\"\n ),\n samples: {\n value:\n parseSpectralSamples(\n samplesFact.value,\n path +\n \".samples.value\"\n ),\n evidence:\n parseReusableNumericEvidence(\n samplesFact.evidence,\n path +\n \".samples.evidence\"\n )\n },\n uncertainty\n };\n }\n\n if (\n record.kind ===\n \"effective-working-t-stop-approximation\"\n ) {\n if (\n record.samples !==\n undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" T-stop approximation must not include spectral transmission samples.\"\n );\n }\n if (\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n path +\n ' T-stop scientificStatus must be \"approximation\".'\n );\n }\n const workingTStopFact =\n requireRecord(\n record.workingTStop,\n path +\n \".workingTStop\", [\"value\", \"evidence\"]\n );\n return {\n kind:\n \"effective-working-t-stop-approximation\",\n scientificStatus:\n \"approximation\",\n workingTStop: {\n value:\n requirePositiveFinite(\n workingTStopFact.value,\n path +\n \".workingTStop.value\"\n ),\n evidence:\n parseEvidenceList(\n workingTStopFact.evidence,\n path +\n \".workingTStop.evidence\"\n )\n },\n wavelengthBasis:\n parseResolvedBasis(\n record.wavelengthBasis,\n path +\n \".wavelengthBasis\"\n ),\n wavelengthRangeNanometers:\n parseRange(\n record.wavelengthRangeNanometers,\n path +\n \".wavelengthRangeNanometers\"\n ),\n uncertainty:\n parseUncertainty(\n record.uncertainty,\n path + \".uncertainty\"\n ),\n limitation:\n requireNonEmptyString(\n record.limitation,\n path +\n \".limitation\"\n )\n };\n }\n\n throw new InvalidConfigurationError(\n path + \".kind is invalid.\"\n );\n}\n\nfunction parseResolvedBasis(\n value: unknown,\n path: string\n): Exclude<\n SpectralWavelengthBasis,\n \"unspecified\"\n> {\n const parsed =\n parseSpectralWavelengthBasis(\n value,\n path\n );\n if (parsed === \"unspecified\") {\n throw new InvalidConfigurationError(\n path +\n ' must resolve to \"air\" or \"vacuum\".'\n );\n }\n return parsed;\n}\n\nfunction parseFocusApplicability(\n value: unknown,\n path: string\n): OpticalBridgeFocusApplicability {\n const record =\n requireRecord(value, path, null);\n rejectUnknownVariantFields(record, \"kind\", { any: [\"kind\"], \"finite-distance-range\": [\"kind\", \"objectDistanceM\"] }, path);\n\n if (record.kind === \"any\") {\n return {\n kind: \"any\"\n };\n }\n if (\n record.kind ===\n \"finite-distance-range\"\n ) {\n return {\n kind:\n \"finite-distance-range\",\n objectDistanceM:\n parseRange(\n record.objectDistanceM,\n path +\n \".objectDistanceM\"\n )\n };\n }\n throw new InvalidConfigurationError(\n path + \".kind is invalid.\"\n );\n}\n\n/**\n * Validates an untrusted scene-to-sensor profile and returns the normalized typed contract. Unknown\n * enum values, missing required fields and incompatible scientific data are rejected here.\n *\n * The optical bridge turns outgoing scene radiance (W/m²/sr/nm) into irradiance (W/m²/nm) arriving\n * at the front of the sensor stack.\n * - Circular paraxial acceptance is pi/(4*Nworking²). At finite focus with unit pupil magnification,\n * the working f-number is N*(1+m).\n * - Spectral transmission and an effective working T-stop are alternative paths; use one.\n * - Field throughput is applied exactly once.\n * - Sensor response and PSF (point-spread function) blur happen later in the pipeline.\n * @param value - unknown. Untrusted input; TypeScript types alone are not validation.\n * @returns SceneToSensorIrradianceProfile. The result states its shape and scientific status; a successful call does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the root export and exact type graph.\n */\nexport function parseSceneToSensorIrradianceProfile(\n value: unknown\n): SceneToSensorIrradianceProfile {\n const record =\n requireRecord(\n value,\n \"sceneToSensorIrradianceProfile\", [\"schemaVersion\", \"applicability\", \"scientificStatus\", \"transmission\", \"distortionAreaMappingOwnership\", \"psfRedistributionOwnership\", \"sensorOpticalStackIncluded\", \"strayLightIncluded\", \"polarizationModeled\", \"wavelengthChangingBehaviorModeled\", \"volumetricScatteringModeled\", \"profileId\", \"profileVersion\", \"lensProfileId\", \"evidence\", \"limitations\"]\n );\n if (\n record.schemaVersion !==\n SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION\n ) {\n throw new InvalidConfigurationError(\n 'sceneToSensorIrradianceProfile.schemaVersion must be \"' +\n SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION +\n '\".'\n );\n }\n\n const applicability =\n requireRecord(\n record.applicability,\n \"sceneToSensorIrradianceProfile.applicability\", [\"focalLengthMm\", \"nominalFNumber\", \"focus\"]\n );\n const status =\n parseScientificStatus(\n record.scientificStatus,\n \"sceneToSensorIrradianceProfile.scientificStatus\"\n );\n const transmission =\n parseTransmission(\n record.transmission,\n \"sceneToSensorIrradianceProfile.transmission\"\n );\n\n if (\n status === \"calibrated\" &&\n transmission.scientificStatus !==\n \"calibrated\"\n ) {\n throw new InvalidConfigurationError(\n \"A calibrated optical bridge profile requires calibrated spectral transmission.\"\n );\n }\n\n if (\n record.distortionAreaMappingOwnership !==\n \"not-applied-by-bridge\" ||\n record.psfRedistributionOwnership !==\n \"downstream-normalized-energy-redistribution\" ||\n record.sensorOpticalStackIncluded !==\n false ||\n record.strayLightIncluded !==\n false ||\n record.polarizationModeled !==\n false ||\n record.wavelengthChangingBehaviorModeled !==\n false ||\n record.volumetricScatteringModeled !==\n false\n ) {\n throw new InvalidConfigurationError(\n \"sceneToSensorIrradianceProfile ownership/completeness flags are invalid for schema 0.1.0.\"\n );\n }\n\n return {\n schemaVersion:\n SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION,\n profileId:\n requireNonEmptyString(\n record.profileId,\n \"sceneToSensorIrradianceProfile.profileId\"\n ),\n profileVersion:\n requireNonEmptyString(\n record.profileVersion,\n \"sceneToSensorIrradianceProfile.profileVersion\"\n ),\n lensProfileId:\n requireNonEmptyString(\n record.lensProfileId,\n \"sceneToSensorIrradianceProfile.lensProfileId\"\n ),\n scientificStatus: status,\n applicability: {\n focalLengthMm:\n parseRange(\n applicability.focalLengthMm,\n \"sceneToSensorIrradianceProfile.applicability.focalLengthMm\"\n ),\n nominalFNumber:\n parseRange(\n applicability.nominalFNumber,\n \"sceneToSensorIrradianceProfile.applicability.nominalFNumber\"\n ),\n focus:\n parseFocusApplicability(\n applicability.focus,\n \"sceneToSensorIrradianceProfile.applicability.focus\"\n )\n },\n transmission,\n distortionAreaMappingOwnership:\n \"not-applied-by-bridge\",\n psfRedistributionOwnership:\n \"downstream-normalized-energy-redistribution\",\n sensorOpticalStackIncluded:\n false,\n strayLightIncluded: false,\n polarizationModeled: false,\n wavelengthChangingBehaviorModeled:\n false,\n volumetricScatteringModeled:\n false,\n evidence:\n parseEvidenceList(\n record.evidence,\n \"sceneToSensorIrradianceProfile.evidence\"\n ),\n limitations:\n parseLimitations(\n record.limitations,\n \"sceneToSensorIrradianceProfile.limitations\"\n )\n };\n}\n\nfunction ensureWithin(\n value: number,\n range: NumericRange,\n path: string\n): void {\n if (\n value < range.minimum ||\n value > range.maximum\n ) {\n throw new InvalidScientificInputError(\n path +\n \" lies outside the optical profile applicability range.\"\n );\n }\n}\n\n/**\n * Internal (2.6; not exported from the package): N_w / N = 1 + m for an ideal symmetric thin lens with unity pupil\n * magnification focused at `objectDistanceM`, m = v/s from `calculateThinLensImageDistance`. The irradiance bridge's\n * `ideal-symmetric-thin-lens` focus uses exactly this factor, and the framing reports derive their close-up light\n * factor 1/(1 + m)² from it, so the two cannot drift apart.\n */\nexport function thinLensWorkingFNumberFactor(focalLengthMm: number, objectDistanceM: number): number {\n return 1 + calculateThinLensImageDistance({ focalLengthMm, objectDistanceM }).value.magnification;\n}\n\nfunction workingFNumber(\n focus:\n OpticalBridgeFocusContext,\n focalLengthMm: number,\n nominalFNumber: number\n): {\n value: number;\n evidence:\n readonly EvidenceProvenance[];\n} {\n if (\n focus.kind === \"infinity-focus\"\n ) {\n return {\n value: nominalFNumber,\n evidence: []\n };\n }\n\n if (\n focus.kind ===\n \"ideal-symmetric-thin-lens\"\n ) {\n if (\n focus\n .pupilMagnificationAssumption !==\n \"unity\"\n ) {\n throw new InvalidScientificInputError(\n 'Ideal symmetric thin-lens focus requires pupilMagnificationAssumption \"unity\".'\n );\n }\n return {\n value:\n nominalFNumber *\n thinLensWorkingFNumberFactor(focalLengthMm, focus.objectDistanceM),\n evidence: []\n };\n }\n\n if (\n focus.kind ===\n \"supplied-working-f-number\"\n ) {\n const value =\n requirePositiveFiniteScientific(\n focus.workingFNumber.value,\n \"focus.workingFNumber.value\"\n );\n return {\n value,\n evidence:\n parseEvidenceList(\n focus.workingFNumber.evidence,\n \"focus.workingFNumber.evidence\"\n )\n };\n }\n\n throw new InvalidConfigurationError(\n \"focus.kind is invalid.\"\n );\n}\n\nfunction requirePositiveFiniteScientific(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isFinite(value) ||\n value <= 0\n ) {\n throw new InvalidScientificInputError(\n path +\n \" must be finite and greater than zero.\"\n );\n }\n return value;\n}\n\nfunction finiteFocusDistance(\n focus:\n OpticalBridgeFocusContext\n): number | null {\n if (\n focus.kind ===\n \"ideal-symmetric-thin-lens\"\n ) {\n return requirePositiveFiniteScientific(\n focus.objectDistanceM,\n \"focus.objectDistanceM\"\n );\n }\n if (\n focus.kind ===\n \"supplied-working-f-number\" &&\n focus.focus.kind === \"finite\"\n ) {\n return requirePositiveFiniteScientific(\n focus.focus.objectDistanceM,\n \"focus.focus.objectDistanceM\"\n );\n }\n return null;\n}\n\nfunction interpolateTransmission(\n model:\n Extract<\n OpticalTransmissionModel,\n { kind: \"spectral-transmission\" }\n >,\n wavelengthNanometers: number\n): number {\n const samples =\n model.samples.value;\n const first =\n samples[0]!;\n const last =\n samples[\n samples.length - 1\n ]!;\n\n if (\n wavelengthNanometers <\n first.wavelengthNanometers ||\n wavelengthNanometers >\n last.wavelengthNanometers\n ) {\n throw new InvalidScientificInputError(\n \"Scene wavelength lies outside the spectral transmission sample range.\"\n );\n }\n\n if (\n wavelengthNanometers ===\n first.wavelengthNanometers\n ) {\n return first\n .linearTransmissionFactor;\n }\n\n for (\n let index = 1;\n index < samples.length;\n index += 1\n ) {\n const right =\n samples[index]!;\n if (\n wavelengthNanometers <=\n right.wavelengthNanometers\n ) {\n const left =\n samples[index - 1]!;\n if (\n wavelengthNanometers ===\n right.wavelengthNanometers\n ) {\n return right\n .linearTransmissionFactor;\n }\n const phase =\n (wavelengthNanometers -\n left.wavelengthNanometers) /\n (right.wavelengthNanometers -\n left.wavelengthNanometers);\n return (\n left\n .linearTransmissionFactor +\n phase *\n (right\n .linearTransmissionFactor -\n left\n .linearTransmissionFactor)\n );\n }\n }\n\n return last\n .linearTransmissionFactor;\n}\n\nfunction fieldFactor(\n field:\n OpticalBridgeFieldThroughput,\n imagePointMm: {\n x: number;\n y: number;\n }\n): number {\n if (field.kind === \"unity\") {\n return 1;\n }\n if (\n field.kind ===\n \"illumination-vignetting-result\"\n ) {\n const result =\n field.result;\n const scale = Math.max(\n 1,\n Math.abs(imagePointMm.x),\n Math.abs(imagePointMm.y),\n Math.abs(result.imagePointMm.x),\n Math.abs(result.imagePointMm.y)\n );\n const tolerance =\n Number.EPSILON *\n 64 *\n scale;\n if (\n Math.abs(\n result.imagePointMm.x -\n imagePointMm.x\n ) > tolerance ||\n Math.abs(\n result.imagePointMm.y -\n imagePointMm.y\n ) > tolerance\n ) {\n throw new InvalidScientificInputError(\n \"Illumination-vignetting result imagePointMm must match the optical bridge imagePointMm.\"\n );\n }\n const factor =\n result.linearThroughputFactor;\n if (\n !Number.isFinite(factor) ||\n factor <= 0 ||\n factor > 1\n ) {\n throw new InvalidScientificInputError(\n \"Field throughput factor must be finite and in (0, 1].\"\n );\n }\n return factor;\n }\n throw new InvalidConfigurationError(\n \"fieldThroughput.kind is invalid.\"\n );\n}\n\nfunction validateSceneIdentity(\n request:\n SceneRadianceEvaluationRequest,\n result:\n SceneRadianceEvaluationResult\n): void {\n if (\n request.sampleId !==\n result.sampleId ||\n request.providerProfileId !==\n result.providerProfileId ||\n request.sceneId !==\n result.sceneId ||\n request.wavelengthNanometers !==\n result.wavelengthNanometers ||\n request.wavelengthBasis !==\n result.wavelengthBasis\n ) {\n throw new InvalidScientificInputError(\n \"Scene-radiance request/result identity must match exactly.\"\n );\n }\n}\n\n/**\n * The request-independent part of {@link calculateSceneRadianceToSensorIrradiance}: the profile, focal length, f-number,\n * applicability, focus and working f-number checks and the paraxial acceptance, in the public function's order.\n */\ninterface PreparedSceneRadianceToSensorIrradiance {\n profile: ReturnType<typeof parseSceneToSensorIrradianceProfile>;\n focalLengthMm: number;\n nominalFNumber: number;\n working: ReturnType<typeof workingFNumber>;\n workingFNumberValue: number;\n geometricAcceptance: number;\n transmission: ReturnType<typeof parseSceneToSensorIrradianceProfile>[\"transmission\"];\n}\n\nfunction prepareSceneRadianceToSensorIrradiance(\n input: Pick<CalculateSceneRadianceToSensorIrradianceInput, \"profile\" | \"focalLengthMm\" | \"nominalFNumber\" | \"focus\">\n): PreparedSceneRadianceToSensorIrradiance {\n const profile =\n parseSceneToSensorIrradianceProfile(\n input.profile\n );\n const focalLengthMm =\n requirePositiveFiniteScientific(\n input.focalLengthMm,\n \"focalLengthMm\"\n );\n const nominalFNumber =\n requirePositiveFiniteScientific(\n input.nominalFNumber,\n \"nominalFNumber\"\n );\n\n ensureWithin(\n focalLengthMm,\n profile.applicability\n .focalLengthMm,\n \"focalLengthMm\"\n );\n ensureWithin(\n nominalFNumber,\n profile.applicability\n .nominalFNumber,\n \"nominalFNumber\"\n );\n\n const focusDistanceM =\n finiteFocusDistance(input.focus);\n if (\n profile.applicability.focus.kind ===\n \"finite-distance-range\"\n ) {\n if (focusDistanceM === null) {\n throw new InvalidScientificInputError(\n \"Optical profile requires a finite focus distance.\"\n );\n }\n ensureWithin(\n focusDistanceM,\n profile.applicability\n .focus.objectDistanceM,\n \"focus object distance\"\n );\n }\n\n const working =\n workingFNumber(\n input.focus,\n focalLengthMm,\n nominalFNumber\n );\n const workingFNumberValue =\n working.value;\n const geometricAcceptance =\n Math.PI /\n (4 *\n workingFNumberValue *\n workingFNumberValue);\n\n if (\n !Number.isFinite(\n geometricAcceptance\n ) ||\n geometricAcceptance <= 0\n ) {\n throw new InvalidScientificInputError(\n \"Paraxial geometric acceptance must remain finite and greater than zero.\"\n );\n }\n\n const transmission =\n profile.transmission;\n return { profile, focalLengthMm, nominalFNumber, working, workingFNumberValue, geometricAcceptance, transmission };\n}\n\ninterface AppliedSceneRadianceToSensorIrradiance {\n spectralTransmissionFactor: number | null;\n effectiveWorkingTStop: number | null;\n tStopEquivalentTransmissionFactor: number | null;\n effectiveAcceptance: number;\n transmissionEvidence: readonly EvidenceProvenance[];\n frontOfLensFilterCount: number;\n frontOfLensFilterPolarizationModeled: boolean;\n frontOfLensFilterTransmissionFactor: number;\n frontOfLensFilterAttenuationStops: number;\n frontOfLensFilterEvidence: readonly EvidenceProvenance[];\n frontOfLensFilterLimitations: readonly string[];\n fieldThroughputFactor: number;\n sceneRadiance: number;\n irradiance: number;\n}\n\n/**\n * The per-query part of {@link calculateSceneRadianceToSensorIrradiance}, after the request and result are parsed and their\n * identities matched: the wavelength-basis check, the transmission or T-stop path, the front-of-lens filters, the field\n * throughput and the irradiance, with the same operations in the same order.\n */\nfunction applySceneRadianceToSensorIrradiance(\n prepared: PreparedSceneRadianceToSensorIrradiance,\n input: Pick<CalculateSceneRadianceToSensorIrradianceInput, \"imagePointMm\" | \"fieldThroughput\"> &\n { frontOfLensFilters?: readonly FrontOfLensFilterProfile[] | undefined },\n request: SceneRadianceEvaluationRequest,\n sceneResult: SceneRadianceEvaluationResult\n): AppliedSceneRadianceToSensorIrradiance {\n const { workingFNumberValue, geometricAcceptance, transmission } = prepared;\n if (\n request.wavelengthBasis !==\n transmission.wavelengthBasis\n ) {\n throw new InvalidScientificInputError(\n \"Scene-radiance and optical-transmission wavelength bases must match; implicit air/vacuum conversion is not performed.\"\n );\n }\n\n let spectralTransmissionFactor:\n number | null = null;\n let effectiveWorkingTStop:\n number | null = null;\n let tStopEquivalentTransmissionFactor:\n number | null = null;\n let effectiveAcceptance =\n geometricAcceptance;\n let transmissionEvidence:\n readonly EvidenceProvenance[];\n\n if (\n transmission.kind ===\n \"spectral-transmission\"\n ) {\n spectralTransmissionFactor =\n interpolateTransmission(\n transmission,\n request.wavelengthNanometers\n );\n effectiveAcceptance *=\n spectralTransmissionFactor;\n transmissionEvidence =\n transmission.samples.evidence;\n } else {\n ensureWithin(\n request.wavelengthNanometers,\n transmission\n .wavelengthRangeNanometers,\n \"scene wavelength\"\n );\n effectiveWorkingTStop =\n transmission.workingTStop.value;\n if (\n effectiveWorkingTStop <\n workingFNumberValue\n ) {\n throw new InvalidScientificInputError(\n \"effective working T-stop must not be smaller than the geometric working f-number for a passive optical approximation.\"\n );\n }\n tStopEquivalentTransmissionFactor =\n (workingFNumberValue /\n effectiveWorkingTStop) **\n 2;\n effectiveAcceptance =\n Math.PI /\n (4 *\n effectiveWorkingTStop *\n effectiveWorkingTStop);\n transmissionEvidence =\n transmission\n .workingTStop.evidence;\n }\n\n let frontOfLensFilterCount = 0;\n let frontOfLensFilterPolarizationModeled = false;\n let frontOfLensFilterTransmissionFactor = 1;\n let frontOfLensFilterAttenuationStops = 0;\n let frontOfLensFilterEvidence:\n readonly EvidenceProvenance[] = [];\n let frontOfLensFilterLimitations:\n readonly string[] = [];\n\n if (\n input.frontOfLensFilters !==\n undefined\n ) {\n const composed =\n composeFrontOfLensFilterTransmission({\n filters:\n input.frontOfLensFilters,\n wavelengthNanometers:\n request.wavelengthNanometers,\n wavelengthBasis:\n request.wavelengthBasis\n });\n frontOfLensFilterCount =\n composed.appliedFilterCount;\n frontOfLensFilterPolarizationModeled =\n composed.polarizationModeled;\n frontOfLensFilterTransmissionFactor =\n composed\n .combinedLinearTransmissionFactor;\n frontOfLensFilterAttenuationStops =\n composed\n .combinedAttenuationStops;\n frontOfLensFilterEvidence =\n composed.components.flatMap(\n (component) =>\n component.evidence\n );\n frontOfLensFilterLimitations =\n composed.components.flatMap(\n (component) =>\n component.limitations\n );\n effectiveAcceptance *=\n frontOfLensFilterTransmissionFactor;\n }\n\n const fieldThroughputFactor =\n fieldFactor(\n input.fieldThroughput,\n input.imagePointMm\n );\n effectiveAcceptance *=\n fieldThroughputFactor;\n\n const sceneRadiance =\n sceneResult\n .spectralRadianceWattsPerSquareMeterSteradianNanometer;\n const irradiance =\n sceneRadiance *\n effectiveAcceptance;\n\n if (\n !Number.isFinite(irradiance) ||\n irradiance < 0\n ) {\n throw new InvalidScientificInputError(\n \"Sensor-plane spectral irradiance must remain finite and non-negative.\"\n );\n }\n\n return { spectralTransmissionFactor, effectiveWorkingTStop, tStopEquivalentTransmissionFactor, effectiveAcceptance, transmissionEvidence,\n frontOfLensFilterCount, frontOfLensFilterPolarizationModeled, frontOfLensFilterTransmissionFactor, frontOfLensFilterAttenuationStops,\n frontOfLensFilterEvidence, frontOfLensFilterLimitations, fieldThroughputFactor, sceneRadiance, irradiance };\n}\n\n/**\n * Internal (#453; not exported from the package): the sensor-plane spectral irradiance that\n * {@link calculateSceneRadianceToSensorIrradiance} returns, for repeated queries of one optical state. This call runs the\n * request-independent checks once (the public function's own code, in its order); each call of the returned function then\n * runs the public function's per-query steps on an engine-built request and a result already parsed by the caller: the identity\n * match, the wavelength-basis check, the transmission or T-stop path, the front-of-lens filters, the field throughput and\n * the finite, non-negative irradiance, with the same operations, order and errors. It skips only the re-validation of the\n * unchanged optical inputs, the re-parse of the request and result, and the result envelope.\n */\nexport function prepareSceneRadianceToSensorIrradianceValue(\n optics: Pick<CalculateSceneRadianceToSensorIrradianceInput, \"profile\" | \"focalLengthMm\" | \"nominalFNumber\" | \"focus\" | \"frontOfLensFilters\">\n): (request: SceneRadianceEvaluationRequest, result: SceneRadianceEvaluationResult, imagePointMm: { x: number; y: number },\n fieldThroughput: OpticalBridgeFieldThroughput) => number {\n const prepared = prepareSceneRadianceToSensorIrradiance(optics);\n const frontOfLensFilters = optics.frontOfLensFilters;\n return (request, result, imagePointMm, fieldThroughput) => {\n validateSceneIdentity(request, result);\n return applySceneRadianceToSensorIrradiance(prepared, { imagePointMm, fieldThroughput, frontOfLensFilters }, request, result).irradiance;\n };\n}\n\n/**\n * Applies the declared pupil acceptance and exactly one transmission or field-throughput path to\n * spectral scene radiance. Returns the irradiance before the sensor stack, with conservative\n * evidence.\n *\n * The optical bridge turns outgoing scene radiance (W/m²/sr/nm) into irradiance (W/m²/nm) arriving\n * at the front of the sensor stack.\n * - Circular paraxial acceptance is pi/(4*Nworking²). At finite focus with unit pupil magnification,\n * the working f-number is N*(1+m).\n * - Spectral transmission and an effective working T-stop are alternative paths; use one.\n * - Field throughput is applied exactly once.\n * - Sensor response and PSF (point-spread function) blur happen later in the pipeline.\n * @param input - CalculateSceneRadianceToSensorIrradianceInput. See that type for coordinates, units and profile binding.\n * @returns CalculationResult<\n SceneToSensorIrradianceResult\n>. The result states its shape and scientific status; a successful call does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the root export and exact type graph.\n */\nexport function calculateSceneRadianceToSensorIrradiance(\n input:\n CalculateSceneRadianceToSensorIrradianceInput\n): CalculationResult<\n SceneToSensorIrradianceResult\n> {\n requireInputKeys(input, \"calculateSceneRadianceToSensorIrradiance\", [\"sceneRadianceRequest\", \"sceneRadianceResult\", \"profile\", \"focalLengthMm\", \"nominalFNumber\", \"focus\", \"imagePointMm\", \"fieldThroughput\", \"frontOfLensFilters\"]);\n const request =\n parseSceneRadianceEvaluationRequest(\n input.sceneRadianceRequest\n );\n const sceneResult =\n parseSceneRadianceEvaluationResult(\n input.sceneRadianceResult\n );\n validateSceneIdentity(\n request,\n sceneResult\n );\n\n const prepared = prepareSceneRadianceToSensorIrradiance(input);\n const { profile, focalLengthMm, nominalFNumber, working, workingFNumberValue, geometricAcceptance, transmission } = prepared;\n const { spectralTransmissionFactor, effectiveWorkingTStop, tStopEquivalentTransmissionFactor, effectiveAcceptance, transmissionEvidence,\n frontOfLensFilterCount, frontOfLensFilterPolarizationModeled, frontOfLensFilterTransmissionFactor, frontOfLensFilterAttenuationStops,\n frontOfLensFilterEvidence, frontOfLensFilterLimitations, fieldThroughputFactor, sceneRadiance, irradiance } =\n applySceneRadianceToSensorIrradiance(prepared, input, request, sceneResult);\n\n const limitations = [\n ...profile.limitations,\n ...frontOfLensFilterLimitations,\n \"Front-of-lens filter transmission is applied outside the lens throughput path exactly once and does not change geometric working f-number.\",\n \"Paraxial circular-pupil acceptance uses pi/(4*N_working^2); no universal cos^4 field falloff is added.\",\n \"Field throughput is supplied separately and applied exactly once.\",\n \"Geometric distortion area-density/Jacobian correction is not applied by this bridge.\",\n \"PSF/diffraction energy redistribution is downstream and must preserve normalized energy apart from separately owned throughput losses.\",\n \"Sensor optical stack, microlens, CFA, QE/responsivity and charge conversion are downstream.\",\n frontOfLensFilterPolarizationModeled\n ? \"Flare, ghosting, veiling glare, scene-radiance polarization (scene light is treated as unpolarized; the front-of-lens linear polarizer transmits (k1 + k2) / 2), volumetric scattering and wavelength-changing behavior are not modeled.\"\n : \"Flare, ghosting, veiling glare, polarization, volumetric scattering and wavelength-changing behavior are not modeled.\"\n ];\n\n return approximationResult(\n {\n schemaVersion:\n SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION,\n profileId: profile.profileId,\n profileVersion:\n profile.profileVersion,\n lensProfileId:\n profile.lensProfileId,\n sampleId: request.sampleId,\n providerProfileId:\n request.providerProfileId,\n sceneId: request.sceneId,\n timeSecondsFromExposureStart:\n request\n .timeSecondsFromExposureStart,\n wavelengthNanometers:\n request.wavelengthNanometers,\n wavelengthBasis:\n request.wavelengthBasis,\n inputQuantity:\n \"outgoing-spectral-radiance\",\n inputUnit:\n \"W/m^2/sr/nm\",\n outputQuantity:\n \"sensor-plane-spectral-irradiance\",\n outputUnit: \"W/m^2/nm\",\n sceneSpectralRadianceWattsPerSquareMeterSteradianNanometer:\n sceneRadiance,\n focalLengthMm,\n nominalFNumber,\n workingFNumber:\n workingFNumberValue,\n focusModel: input.focus.kind,\n paraxialGeometricAcceptanceSolidAngleSteradians:\n geometricAcceptance,\n transmissionPath:\n transmission.kind,\n spectralTransmissionFactor,\n effectiveWorkingTStop,\n tStopEquivalentTransmissionFactor,\n frontOfLensFilterCount,\n frontOfLensFilterTransmissionFactor,\n frontOfLensFilterAttenuationStops,\n frontOfLensFilterTransmissionAppliedExactlyOnce:\n true,\n frontOfLensFilterPolarizationModeled,\n fieldThroughputFactor,\n effectiveAcceptanceAfterTransmissionAndFieldSteradians:\n effectiveAcceptance,\n sensorPlaneSpectralIrradianceWattsPerSquareMeterNanometer:\n irradiance,\n scientificStatus:\n \"approximation\",\n sceneRadianceScientificStatus:\n sceneResult.scientificStatus,\n opticalProfileScientificStatus:\n profile.scientificStatus,\n sceneRadianceUncertainty:\n sceneResult.uncertainty,\n opticalUncertainty:\n transmission.uncertainty,\n radianceAmplificationApplied:\n false,\n fieldThroughputAppliedExactlyOnce:\n true,\n universalCos4FalloffApplied:\n false,\n distortionAreaCorrectionApplied:\n false,\n psfRedistributionApplied:\n false,\n sensorOpticalStackApplied:\n false,\n cfaApplied: false,\n quantumEfficiencyApplied: false,\n spectralResponsivityApplied:\n false,\n strayLightApplied: false,\n polarizationModeled: false,\n wavelengthChangingBehaviorModeled:\n false,\n volumetricScatteringModeled:\n false,\n calibratedSensorPlaneIrradianceClaimAuthorized:\n false,\n componentEvidence: {\n sceneRadiance:\n sceneResult.evidence,\n opticalProfile:\n profile.evidence,\n transmission:\n transmissionEvidence,\n workingFNumber:\n working.evidence,\n frontOfLensFilters:\n frontOfLensFilterEvidence\n },\n limitations\n },\n \"scene-radiance-to-sensor-irradiance\",\n \"1.1.0\",\n limitations\n );\n}\n"]}
|
|
@@ -9,6 +9,7 @@ import { InvalidScientificInputError, requirePositiveFinite } from "../core/vali
|
|
|
9
9
|
import { calculateFocusPlaneImageDistance, parseFocusPlane } from "./focus-state.js";
|
|
10
10
|
import { applyPhysicalCameraRotation, calculateInverseCameraRotationImageMapping } from "../motion/camera-rotation.js";
|
|
11
11
|
import { requireInputKeys } from "../core/record-validation.js";
|
|
12
|
+
import { hypot3 } from "../core/portable-math.js";
|
|
12
13
|
/**
|
|
13
14
|
* Polar midpoint rule with equal-area cells. Weights are normalized as an average, so they never
|
|
14
15
|
* apply the f-number a second time.
|
|
@@ -59,7 +60,7 @@ export function sensorApertureRayValues(owned, pupil, focus) {
|
|
|
59
60
|
const x = owned.sourcePointNativeSensorMm.x / v, y = -owned.sourcePointNativeSensorMm.y / v;
|
|
60
61
|
const direction = focus.kind === "finite" ? { x: x * focus.distanceM - origin.x, y: y * focus.distanceM - origin.y, z: focus.distanceM }
|
|
61
62
|
: { x, y, z: 1 };
|
|
62
|
-
const length =
|
|
63
|
+
const length = hypot3(direction.x, direction.y, direction.z);
|
|
63
64
|
const unit = { x: direction.x / length, y: direction.y / length, z: direction.z / length };
|
|
64
65
|
// Same physical right-hand camera rotation as the inverse mapping above.
|
|
65
66
|
const rotatedOrigin = applyPhysicalCameraRotation(origin, displacement, "camera-to-reference"), rotatedDirection = applyPhysicalCameraRotation(unit, displacement, "camera-to-reference");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sensor-aperture-rays.js","sourceRoot":"","sources":["../../src/optics/sensor-aperture-rays.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n/**\n * Ideal rays through a symmetric thin-lens pupil. This module does not intersect rays with the\n * scene and does no radiometry.\n */\nimport { approximationResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { parseEvidenceList, type EvidenceProvenance } from \"../core/evidence-provenance.js\";\nimport { InvalidScientificInputError, requirePositiveFinite } from \"../core/validation.js\";\nimport { calculateFocusPlaneImageDistance, parseFocusPlane, type FocusPlane } from \"./focus-state.js\";\nimport { applyPhysicalCameraRotation, calculateInverseCameraRotationImageMapping,\n type CameraAngularVelocityRadPerSec } from \"../motion/camera-rotation.js\";\nimport { requireInputKeys } from \"../core/record-validation.js\";\nexport interface IdealCircularPupil {\n kind: \"ideal-uniform-circular-pupil\";\n radialSampleCount: number;\n angularSampleCount: number;\n evidence: readonly EvidenceProvenance[];\n limitation: string;\n}\nexport interface SensorApertureRay {\n /** Lens principal-plane origin and look direction in exposure-opening camera axes. */\n originM: { x: number; y: number; z: number };\n directionUnitVector: { x: number; y: number; z: number };\n pupilPointMm: { x: number; y: number };\n weight: number;\n pupilSampleIndex: number;\n}\nexport interface CalculateSensorApertureRaysInput {\n sourcePointNativeSensorMm: { x: number; y: number };\n focalLengthMm: number;\n nominalFNumber: number;\n focus: FocusPlane;\n pupil: IdealCircularPupil;\n angularVelocityRadPerSec: CameraAngularVelocityRadPerSec;\n timeSecondsFromOpeningReference: number;\n}\nexport interface SensorApertureRays {\n rays: readonly SensorApertureRay[];\n projectionDistanceMm: number;\n pupilRadiusMm: number;\n focus: FocusPlane;\n pupil: IdealCircularPupil;\n sceneVisibilityCalculated: false;\n radiometryCalculated: false;\n apertureAreaApplied: false;\n}\n/**\n * Polar midpoint rule with equal-area cells. Weights are normalized as an average, so they never\n * apply the f-number a second time.\n */\nexport function calculateSensorApertureRays(input: CalculateSensorApertureRaysInput): CalculationResult<SensorApertureRays> {\n requireInputKeys(input, \"calculateSensorApertureRays\", [\"sourcePointNativeSensorMm\", \"focalLengthMm\", \"nominalFNumber\", \"focus\", \"pupil\", \"angularVelocityRadPerSec\", \"timeSecondsFromOpeningReference\"]);\n const owned = structuredClone(input), pupil = owned.pupil;\n requirePositiveFinite(\"nominalFNumber\", owned.nominalFNumber);\n if (!pupil || pupil.kind !== \"ideal-uniform-circular-pupil\" ||\n !Number.isSafeInteger(pupil.radialSampleCount) || pupil.radialSampleCount < 1 || pupil.radialSampleCount > 32 ||\n !Number.isSafeInteger(pupil.angularSampleCount) || pupil.angularSampleCount < 4 || pupil.angularSampleCount > 64 ||\n pupil.radialSampleCount*pupil.angularSampleCount > 256 || typeof pupil.limitation !== \"string\" || !pupil.limitation.trim()) {\n throw new InvalidScientificInputError(\"Circular pupil requires 1..32 radial, 4..64 angular midpoints, at most 256 rays and explicit limitations.\");\n }\n pupil.evidence = parseEvidenceList(pupil.evidence, \"circularPupil.evidence\");\n const focus = parseFocusPlane(owned.focus);\n const { rays, v, radius } = sensorApertureRayValues(owned, pupil, focus);\n return approximationResult({ rays, projectionDistanceMm: v, pupilRadiusMm: radius, focus, pupil,\n sceneVisibilityCalculated: false, radiometryCalculated: false, apertureAreaApplied: false }, \"ideal-thin-lens-circular-pupil-rays\", \"0.1.0\", [\n \"Ideal symmetric paraxial thin lens, unity pupil magnification and uniform circular pupil; no diffraction, aberrations, lens housing or pupil clipping.\",\n \"Normalized pupil averaging is separate from the existing working-f-number radiometric factor; no aperture area or solid-angle factor is applied here.\",\n \"Pure camera rotation about the lens principal-plane origin; no translation or depth-dependent camera motion.\",\n \"Finite polar midpoint quadrature does not establish scene visibility, transport calibration or convergence.\"\n ]);\n}\n\n/**\n * Internal (2.0 performance): the rays of calculateSensorApertureRays, by the same code, for a pupil and focus it has\n * already validated and parsed. The lean native executor calls it per site after one full validation per template.\n * It still runs the rotation, time, image-distance and forward-hemisphere checks, and throws where the full function\n * would. Not exported from the package.\n */\nexport function sensorApertureRayValues(owned: Pick<CalculateSensorApertureRaysInput, \"sourcePointNativeSensorMm\" | \"focalLengthMm\" | \"nominalFNumber\" |\n \"angularVelocityRadPerSec\" | \"timeSecondsFromOpeningReference\">, pupil: Pick<IdealCircularPupil, \"radialSampleCount\" | \"angularSampleCount\">,\n focus: ReturnType<typeof parseFocusPlane>): { rays: SensorApertureRay[]; v: number; radius: number } {\n const v = calculateFocusPlaneImageDistance({ focalLengthMm: owned.focalLengthMm, focus }).value.imageDistanceMm;\n // Reuse the existing rotation and time validation, and the check that rays point forward.\n const inverse = calculateInverseCameraRotationImageMapping({ focalLengthMm: owned.focalLengthMm,\n imagePointMm: { x: owned.sourcePointNativeSensorMm.x, y: -owned.sourcePointNativeSensorMm.y },\n angularVelocityRadPerSec: owned.angularVelocityRadPerSec, timeSecondsFromExposureStart: owned.timeSecondsFromOpeningReference,\n ...(focus.kind === \"finite\" ? { focusDistanceM: focus.distanceM } : {}) }).value;\n const displacement = inverse.angularDisplacementRad;\n const radius = owned.focalLengthMm/(2*owned.nominalFNumber), count = pupil.radialSampleCount*pupil.angularSampleCount;\n if (!Number.isFinite(radius) || radius <= 0) throw new InvalidScientificInputError(\"Pupil radius must remain positive and finite.\");\n const rays = Array.from({ length: count }, (_, i): SensorApertureRay => {\n const r = radius*Math.sqrt((Math.floor(i/pupil.angularSampleCount)+.5)/pupil.radialSampleCount);\n const theta = 2*Math.PI*((i%pupil.angularSampleCount)+.5)/pupil.angularSampleCount;\n const point = { x: r*Math.cos(theta), y: r*Math.sin(theta) };\n const origin = { x: point.x/1000, y: point.y/1000, z: 0 };\n const x = owned.sourcePointNativeSensorMm.x/v, y = -owned.sourcePointNativeSensorMm.y/v;\n const direction = focus.kind === \"finite\" ? { x: x*focus.distanceM-origin.x, y: y*focus.distanceM-origin.y, z: focus.distanceM }\n : { x, y, z: 1 };\n const length = Math.hypot(direction.x,direction.y,direction.z);\n const unit = { x: direction.x/length, y: direction.y/length, z: direction.z/length };\n // Same physical right-hand camera rotation as the inverse mapping above.\n const rotatedOrigin = applyPhysicalCameraRotation(origin, displacement, \"camera-to-reference\"),\n rotatedDirection = applyPhysicalCameraRotation(unit, displacement, \"camera-to-reference\");\n if (![...Object.values(rotatedOrigin),...Object.values(rotatedDirection)].every(Number.isFinite) || rotatedDirection.z <= 0) {\n throw new InvalidScientificInputError(\"A pupil ray leaves the supported forward hemisphere or finite coordinate domain.\");\n }\n return { originM: rotatedOrigin, directionUnitVector: rotatedDirection, pupilPointMm: point, weight: 1/count, pupilSampleIndex: i };\n });\n return { rays, v, radius };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sensor-aperture-rays.js","sourceRoot":"","sources":["../../src/optics/sensor-aperture-rays.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n/**\n * Ideal rays through a symmetric thin-lens pupil. This module does not intersect rays with the\n * scene and does no radiometry.\n */\nimport { approximationResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { parseEvidenceList, type EvidenceProvenance } from \"../core/evidence-provenance.js\";\nimport { InvalidScientificInputError, requirePositiveFinite } from \"../core/validation.js\";\nimport { calculateFocusPlaneImageDistance, parseFocusPlane, type FocusPlane } from \"./focus-state.js\";\nimport { applyPhysicalCameraRotation, calculateInverseCameraRotationImageMapping,\n type CameraAngularVelocityRadPerSec } from \"../motion/camera-rotation.js\";\nimport { requireInputKeys } from \"../core/record-validation.js\";\nimport { hypot3 } from \"../core/portable-math.js\";\nexport interface IdealCircularPupil {\n kind: \"ideal-uniform-circular-pupil\";\n radialSampleCount: number;\n angularSampleCount: number;\n evidence: readonly EvidenceProvenance[];\n limitation: string;\n}\nexport interface SensorApertureRay {\n /** Lens principal-plane origin and look direction in exposure-opening camera axes. */\n originM: { x: number; y: number; z: number };\n directionUnitVector: { x: number; y: number; z: number };\n pupilPointMm: { x: number; y: number };\n weight: number;\n pupilSampleIndex: number;\n}\nexport interface CalculateSensorApertureRaysInput {\n sourcePointNativeSensorMm: { x: number; y: number };\n focalLengthMm: number;\n nominalFNumber: number;\n focus: FocusPlane;\n pupil: IdealCircularPupil;\n angularVelocityRadPerSec: CameraAngularVelocityRadPerSec;\n timeSecondsFromOpeningReference: number;\n}\nexport interface SensorApertureRays {\n rays: readonly SensorApertureRay[];\n projectionDistanceMm: number;\n pupilRadiusMm: number;\n focus: FocusPlane;\n pupil: IdealCircularPupil;\n sceneVisibilityCalculated: false;\n radiometryCalculated: false;\n apertureAreaApplied: false;\n}\n/**\n * Polar midpoint rule with equal-area cells. Weights are normalized as an average, so they never\n * apply the f-number a second time.\n */\nexport function calculateSensorApertureRays(input: CalculateSensorApertureRaysInput): CalculationResult<SensorApertureRays> {\n requireInputKeys(input, \"calculateSensorApertureRays\", [\"sourcePointNativeSensorMm\", \"focalLengthMm\", \"nominalFNumber\", \"focus\", \"pupil\", \"angularVelocityRadPerSec\", \"timeSecondsFromOpeningReference\"]);\n const owned = structuredClone(input), pupil = owned.pupil;\n requirePositiveFinite(\"nominalFNumber\", owned.nominalFNumber);\n if (!pupil || pupil.kind !== \"ideal-uniform-circular-pupil\" ||\n !Number.isSafeInteger(pupil.radialSampleCount) || pupil.radialSampleCount < 1 || pupil.radialSampleCount > 32 ||\n !Number.isSafeInteger(pupil.angularSampleCount) || pupil.angularSampleCount < 4 || pupil.angularSampleCount > 64 ||\n pupil.radialSampleCount*pupil.angularSampleCount > 256 || typeof pupil.limitation !== \"string\" || !pupil.limitation.trim()) {\n throw new InvalidScientificInputError(\"Circular pupil requires 1..32 radial, 4..64 angular midpoints, at most 256 rays and explicit limitations.\");\n }\n pupil.evidence = parseEvidenceList(pupil.evidence, \"circularPupil.evidence\");\n const focus = parseFocusPlane(owned.focus);\n const { rays, v, radius } = sensorApertureRayValues(owned, pupil, focus);\n return approximationResult({ rays, projectionDistanceMm: v, pupilRadiusMm: radius, focus, pupil,\n sceneVisibilityCalculated: false, radiometryCalculated: false, apertureAreaApplied: false }, \"ideal-thin-lens-circular-pupil-rays\", \"0.1.0\", [\n \"Ideal symmetric paraxial thin lens, unity pupil magnification and uniform circular pupil; no diffraction, aberrations, lens housing or pupil clipping.\",\n \"Normalized pupil averaging is separate from the existing working-f-number radiometric factor; no aperture area or solid-angle factor is applied here.\",\n \"Pure camera rotation about the lens principal-plane origin; no translation or depth-dependent camera motion.\",\n \"Finite polar midpoint quadrature does not establish scene visibility, transport calibration or convergence.\"\n ]);\n}\n\n/**\n * Internal (2.0 performance): the rays of calculateSensorApertureRays, by the same code, for a pupil and focus it has\n * already validated and parsed. The lean native executor calls it per site after one full validation per template.\n * It still runs the rotation, time, image-distance and forward-hemisphere checks, and throws where the full function\n * would. Not exported from the package.\n */\nexport function sensorApertureRayValues(owned: Pick<CalculateSensorApertureRaysInput, \"sourcePointNativeSensorMm\" | \"focalLengthMm\" | \"nominalFNumber\" |\n \"angularVelocityRadPerSec\" | \"timeSecondsFromOpeningReference\">, pupil: Pick<IdealCircularPupil, \"radialSampleCount\" | \"angularSampleCount\">,\n focus: ReturnType<typeof parseFocusPlane>): { rays: SensorApertureRay[]; v: number; radius: number } {\n const v = calculateFocusPlaneImageDistance({ focalLengthMm: owned.focalLengthMm, focus }).value.imageDistanceMm;\n // Reuse the existing rotation and time validation, and the check that rays point forward.\n const inverse = calculateInverseCameraRotationImageMapping({ focalLengthMm: owned.focalLengthMm,\n imagePointMm: { x: owned.sourcePointNativeSensorMm.x, y: -owned.sourcePointNativeSensorMm.y },\n angularVelocityRadPerSec: owned.angularVelocityRadPerSec, timeSecondsFromExposureStart: owned.timeSecondsFromOpeningReference,\n ...(focus.kind === \"finite\" ? { focusDistanceM: focus.distanceM } : {}) }).value;\n const displacement = inverse.angularDisplacementRad;\n const radius = owned.focalLengthMm/(2*owned.nominalFNumber), count = pupil.radialSampleCount*pupil.angularSampleCount;\n if (!Number.isFinite(radius) || radius <= 0) throw new InvalidScientificInputError(\"Pupil radius must remain positive and finite.\");\n const rays = Array.from({ length: count }, (_, i): SensorApertureRay => {\n const r = radius*Math.sqrt((Math.floor(i/pupil.angularSampleCount)+.5)/pupil.radialSampleCount);\n const theta = 2*Math.PI*((i%pupil.angularSampleCount)+.5)/pupil.angularSampleCount;\n const point = { x: r*Math.cos(theta), y: r*Math.sin(theta) };\n const origin = { x: point.x/1000, y: point.y/1000, z: 0 };\n const x = owned.sourcePointNativeSensorMm.x/v, y = -owned.sourcePointNativeSensorMm.y/v;\n const direction = focus.kind === \"finite\" ? { x: x*focus.distanceM-origin.x, y: y*focus.distanceM-origin.y, z: focus.distanceM }\n : { x, y, z: 1 };\n const length = hypot3(direction.x,direction.y,direction.z);\n const unit = { x: direction.x/length, y: direction.y/length, z: direction.z/length };\n // Same physical right-hand camera rotation as the inverse mapping above.\n const rotatedOrigin = applyPhysicalCameraRotation(origin, displacement, \"camera-to-reference\"),\n rotatedDirection = applyPhysicalCameraRotation(unit, displacement, \"camera-to-reference\");\n if (![...Object.values(rotatedOrigin),...Object.values(rotatedDirection)].every(Number.isFinite) || rotatedDirection.z <= 0) {\n throw new InvalidScientificInputError(\"A pupil ray leaves the supported forward hemisphere or finite coordinate domain.\");\n }\n return { originM: rotatedOrigin, directionUnitVector: rotatedDirection, pupilPointMm: point, weight: 1/count, pupilSampleIndex: i };\n });\n return { rays, v, radius };\n}\n"]}
|
|
@@ -29,6 +29,7 @@ import { parseSceneRadianceEvaluationRequest } from "../schema/scene-radiance.js
|
|
|
29
29
|
import { parseFocusPlane } from "./focus-state.js";
|
|
30
30
|
import { requireInputKeys } from "../core/record-validation.js";
|
|
31
31
|
import { InvalidConfigurationError } from "../core/configuration-error.js";
|
|
32
|
+
import { hypot3 } from "../core/portable-math.js";
|
|
32
33
|
/** @internal Strict parse and validation of a lens field mapping. Unknown fields are rejected. */
|
|
33
34
|
export function prepareEnvironmentLensFieldMapping(value) {
|
|
34
35
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
@@ -88,7 +89,7 @@ export function calculateSensorEnvironmentRadianceQuery(input) {
|
|
|
88
89
|
timeSecondsFromExposureStart: input.timeSecondsFromOpeningReference,
|
|
89
90
|
...(focus.kind === "finite" ? { focusDistanceM: focus.distanceM } : {}) });
|
|
90
91
|
const point = projection.value.referenceImagePointMm, z = projection.value.projectionDistanceMm;
|
|
91
|
-
const length =
|
|
92
|
+
const length = hypot3(point.x, point.y, z);
|
|
92
93
|
const referenceLookDirectionUnitVector = { x: point.x / length, y: point.y / length, z: z / length };
|
|
93
94
|
const ray = referenceLookDirectionUnitVector;
|
|
94
95
|
const request = parseSceneRadianceEvaluationRequest({ ...input.request,
|
|
@@ -144,7 +145,7 @@ export function calculateMappedSensorEnvironmentRadianceQuery(input, prepared) {
|
|
|
144
145
|
timeSecondsFromExposureStart: input.timeSecondsFromOpeningReference,
|
|
145
146
|
...(focus.kind === "finite" ? { focusDistanceM: focus.distanceM } : {}) });
|
|
146
147
|
const point = projection.value.referenceImagePointMm, z = projection.value.projectionDistanceMm;
|
|
147
|
-
const length =
|
|
148
|
+
const length = hypot3(point.x, point.y, z);
|
|
148
149
|
look = { x: point.x / length, y: point.y / length, z: z / length };
|
|
149
150
|
projectionScaleMm = z;
|
|
150
151
|
}
|
|
@@ -159,7 +160,7 @@ export function calculateMappedSensorEnvironmentRadianceQuery(input, prepared) {
|
|
|
159
160
|
}
|
|
160
161
|
const captured = lensProjectionDirectionValue(mapping.projectionModel, projectionScaleMm, ideal.x, ideal.y);
|
|
161
162
|
const reference = applyPhysicalCameraRotation(captured, { pitch: velocity.pitch * t, yaw: velocity.yaw * t, roll: velocity.roll * t }, "camera-to-reference");
|
|
162
|
-
const length =
|
|
163
|
+
const length = hypot3(reference.x, reference.y, reference.z);
|
|
163
164
|
if (!(length > 0) || !Number.isFinite(length))
|
|
164
165
|
throw new InvalidScientificInputError("Mapped reference ray must be finite.");
|
|
165
166
|
look = { x: reference.x / length, y: reference.y / length, z: reference.z / length };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sensor-environment-query.js","sourceRoot":"","sources":["../../src/optics/sensor-environment-query.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Builds an environment radiance request from points on the physical sensor. It uses the existing\n * focus-aware ideal projection and the analytic inverse camera rotation.\n *\n * Native Y (+Y down) is inverted explicitly. The direction sent to the provider points opposite the\n * look ray at exposure start, because radiance travels toward the camera. This module does not\n * intersect the scene, compute radiance or render anything.\n *\n * Optional lens field mapping (2.0):\n * 1. The physical sensor point is inverse-distorted (radial or Brown-Conrady) to the ideal image\n * point.\n * 2. That point is unprojected with the declared ideal projection (rectilinear, fisheye or\n * equirectangular), at the same focus-aware projection scale.\n * 3. The ray is rotated to the camera axes at exposure start.\n * The local area factor 1 / det(d distorted / d ideal) is reported. The caller applies it to\n * irradiance exactly once. Without the mapping, the existing rectilinear computation is unchanged.\n * @see docs/PHYSICS_FOUNDATION.md for the equations, units, coordinate conventions, known blockers\n * and support limits.\n */\n\nimport { approximationResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport { applyPhysicalCameraRotation, calculateInverseCameraRotationImageMapping,\n type CameraAngularVelocityRadPerSec } from \"../motion/camera-rotation.js\";\nimport { lensProjectionDirectionValue, parseLensProjectionModel, type LensProjectionModel } from \"../camera/lens-projection.js\";\nimport {\n calculateInverseRadialSourcePointValue, validateRadialDistortionProfile, type RadialDistortionProfile, type ValidatedRadialProfile\n} from \"./radial-distortion.js\";\nimport {\n brownConradyForwardValue, brownConradyInverseValue, validateBrownConradyDistortionProfile,\n type BrownConradyDistortionProfile, type ValidatedBrownConradyDistortionProfile\n} from \"./brown-conrady-distortion.js\";\nimport { calculateThinLensImageDistance } from \"./thin-lens.js\";\nimport { parseSceneRadianceEvaluationRequest, type SceneRadianceEvaluationRequest } from \"../schema/scene-radiance.js\";\nimport { parseFocusPlane, type FocusPlane } from \"./focus-state.js\";\nimport { requireInputKeys } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\n\n/**\n * Lens field mapping between the physical (distorted) sensor point and the scene ray (2.0).\n * `radiometricAreaCorrection` states who applies the distortion area factor. The environment\n * photo signal multiplies irradiance by 1 / det(d distorted / d ideal) exactly once. A supplied\n * field-throughput profile must therefore not include it as well.\n */\nexport interface EnvironmentLensFieldMapping {\n projectionModel: LensProjectionModel;\n distortion:\n | { kind: \"none\" }\n | { kind: \"radial\"; profile: RadialDistortionProfile }\n | { kind: \"brown-conrady\"; profile: BrownConradyDistortionProfile };\n radiometricAreaCorrection: \"inverse-distortion-jacobian\";\n}\n\n/** @internal Parsed mapping with validated profiles, reused across one plan's queries. */\nexport interface PreparedEnvironmentLensFieldMapping {\n mapping: EnvironmentLensFieldMapping;\n radial?: ValidatedRadialProfile;\n brownConrady?: ValidatedBrownConradyDistortionProfile;\n}\n\n/** @internal Strict parse and validation of a lens field mapping. Unknown fields are rejected. */\nexport function prepareEnvironmentLensFieldMapping(value: unknown): PreparedEnvironmentLensFieldMapping {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new InvalidConfigurationError(\"lensFieldMapping must be an object.\");\n }\n const record = value as Record<string, unknown>;\n for (const key of Object.keys(record)) {\n if (![\"projectionModel\", \"distortion\", \"radiometricAreaCorrection\"].includes(key)) {\n throw new InvalidConfigurationError(`lensFieldMapping has unknown field ${JSON.stringify(key)}.`);\n }\n }\n if (record.radiometricAreaCorrection !== \"inverse-distortion-jacobian\") {\n throw new InvalidConfigurationError(\"lensFieldMapping.radiometricAreaCorrection must be \\\"inverse-distortion-jacobian\\\".\");\n }\n const projectionModel = parseLensProjectionModel(record.projectionModel);\n const distortion = record.distortion as EnvironmentLensFieldMapping[\"distortion\"] | undefined;\n if (typeof distortion !== \"object\" || distortion === null) {\n throw new InvalidConfigurationError(\"lensFieldMapping.distortion must be an object.\");\n }\n if (distortion.kind === \"none\") {\n return { mapping: { projectionModel, distortion: { kind: \"none\" }, radiometricAreaCorrection: \"inverse-distortion-jacobian\" } };\n }\n if (distortion.kind === \"radial\") {\n const radial = validateRadialDistortionProfile(distortion.profile);\n return { mapping: { projectionModel, distortion: { kind: \"radial\", profile: distortion.profile },\n radiometricAreaCorrection: \"inverse-distortion-jacobian\" }, radial };\n }\n if (distortion.kind === \"brown-conrady\") {\n const brownConrady = validateBrownConradyDistortionProfile(distortion.profile);\n return { mapping: { projectionModel, distortion: { kind: \"brown-conrady\", profile: distortion.profile },\n radiometricAreaCorrection: \"inverse-distortion-jacobian\" }, brownConrady };\n }\n throw new InvalidConfigurationError(\"lensFieldMapping.distortion.kind must be none, radial or brown-conrady.\");\n}\n\nexport interface CalculateSensorEnvironmentRadianceQueryInput {\n /** Optical sample point before the anti-aliasing filter or PSF. Native +X right, +Y down, in mm from the optical axis. */\n sourcePointNativeSensorMm: { x: number; y: number };\n focalLengthMm: number;\n focus: FocusPlane;\n angularVelocityRadPerSec: CameraAngularVelocityRadPerSec;\n /** States that the moment the shutter opens is t=0 in the rotation model. */\n timeReference: \"first-opening-boundary-phase\";\n timeSecondsFromOpeningReference: number;\n /** The request uses the direction radiance travels: toward the camera, opposite the look ray. */\n environmentDirectionConvention: \"outgoing-radiance-toward-camera\";\n request: Omit<SceneRadianceEvaluationRequest, \"target\" | \"timeSecondsFromExposureStart\">;\n /** Optional 2.0 lens field mapping. Omit it for the ideal rectilinear, distortion-free query. */\n lensFieldMapping?: EnvironmentLensFieldMapping;\n}\n\nexport interface SensorEnvironmentLensFieldMappingResult {\n projectionModel: LensProjectionModel;\n distortionKind: \"none\" | \"radial\" | \"brown-conrady\";\n /** Ideal (undistorted) image-plane point, +X right / +Y up, mm. */\n idealImagePointMm: { x: number; y: number };\n /** Projection scale: thin-lens image distance at finite focus, focal length at infinity, mm. */\n projectionScaleMm: number;\n /** 1 / det(d distorted / d ideal) at this point; 1 without distortion. */\n distortionAreaFactor: number;\n}\n\nexport interface SensorEnvironmentRadianceQuery {\n request: SceneRadianceEvaluationRequest;\n sourcePointNativeSensorMm: { x: number; y: number };\n sourcePointImagePlaneMm: { x: number; y: number };\n referenceLookDirectionUnitVector: { x: number; y: number; z: number };\n /** Rectilinear rotation mapping. Null when a non-rectilinear lens field mapping is used. */\n projection: ReturnType<typeof calculateInverseCameraRotationImageMapping> | null;\n /** Present only when the input declared a lens field mapping. */\n lensFieldMapping?: SensorEnvironmentLensFieldMappingResult;\n targetMeaning: \"environment-direction-in-exposure-start-camera-axes\";\n directionConvention: \"outgoing-radiance-toward-camera\";\n geometricRayProjectionCalculated: true;\n sceneIntersectionCalculated: false;\n visibilityCalculated: false;\n providerExecuted: false;\n}\n\n/**\n * Builds an environment radiance request from points on the physical sensor,\n * using the existing focus-aware ideal projection and the analytic inverse\n * camera rotation.\n *\n * Native Y (+Y down) is inverted explicitly. The provider direction points\n * opposite the look ray at exposure start. Nothing intersects the scene,\n * computes radiance or renders.\n */\nexport function calculateSensorEnvironmentRadianceQuery(\n input: CalculateSensorEnvironmentRadianceQueryInput\n): CalculationResult<SensorEnvironmentRadianceQuery> {\n requireInputKeys(input, \"calculateSensorEnvironmentRadianceQuery\", [\"sourcePointNativeSensorMm\", \"focalLengthMm\", \"focus\", \"angularVelocityRadPerSec\", \"timeReference\", \"timeSecondsFromOpeningReference\", \"environmentDirectionConvention\", \"request\", \"lensFieldMapping\"]);\n if (input.lensFieldMapping !== undefined) {\n return calculateMappedSensorEnvironmentRadianceQuery(input, prepareEnvironmentLensFieldMapping(input.lensFieldMapping));\n }\n if (input.timeReference !== \"first-opening-boundary-phase\" ||\n input.environmentDirectionConvention !== \"outgoing-radiance-toward-camera\") {\n throw new InvalidScientificInputError(\"Environment projection requires explicit opening-boundary time and outgoing-toward-camera direction conventions.\");\n }\n const focus = parseFocusPlane(input.focus);\n const sourcePointImagePlaneMm = { x: input.sourcePointNativeSensorMm.x, y: -input.sourcePointNativeSensorMm.y };\n const projection = calculateInverseCameraRotationImageMapping({ focalLengthMm: input.focalLengthMm,\n imagePointMm: sourcePointImagePlaneMm, angularVelocityRadPerSec: input.angularVelocityRadPerSec,\n timeSecondsFromExposureStart: input.timeSecondsFromOpeningReference,\n ...(focus.kind === \"finite\" ? { focusDistanceM: focus.distanceM } : {}) });\n const point = projection.value.referenceImagePointMm, z = projection.value.projectionDistanceMm;\n const length = Math.hypot(point.x, point.y, z);\n const referenceLookDirectionUnitVector = { x: point.x/length, y: point.y/length, z: z/length };\n const ray = referenceLookDirectionUnitVector;\n const request = parseSceneRadianceEvaluationRequest({ ...input.request,\n timeSecondsFromExposureStart: input.timeSecondsFromOpeningReference,\n target: { kind: \"environment-direction\", outgoingDirectionUnitVector: { x: -ray.x, y: -ray.y, z: -ray.z } } });\n return approximationResult({ request, sourcePointNativeSensorMm: { ...input.sourcePointNativeSensorMm },\n sourcePointImagePlaneMm, referenceLookDirectionUnitVector, projection,\n targetMeaning: \"environment-direction-in-exposure-start-camera-axes\", directionConvention: \"outgoing-radiance-toward-camera\",\n geometricRayProjectionCalculated: true, sceneIntersectionCalculated: false, visibilityCalculated: false, providerExecuted: false\n }, \"sensor-environment-radiance-query\", \"0.1.0\", [\n \"Ideal focus-aware rectilinear projection and constant-axis pure camera rotation; reference ray must stay in the forward hemisphere.\",\n \"The environment and rotation reference axes coincide with camera axes at the first opening boundary by explicit convention.\",\n \"Lens distortion, breathing, chromatic field mapping, translation/parallax and scene intersections remain separate.\"\n ]);\n}\n\n/**\n * @internal Query with a lens field mapping that is already validated. The environment photo-signal\n * planner shares it, so profiles are validated once per plan.\n */\nexport function calculateMappedSensorEnvironmentRadianceQuery(\n input: CalculateSensorEnvironmentRadianceQueryInput,\n prepared: PreparedEnvironmentLensFieldMapping\n): CalculationResult<SensorEnvironmentRadianceQuery> {\n if (input.timeReference !== \"first-opening-boundary-phase\" ||\n input.environmentDirectionConvention !== \"outgoing-radiance-toward-camera\") {\n throw new InvalidScientificInputError(\"Environment projection requires explicit opening-boundary time and outgoing-toward-camera direction conventions.\");\n }\n const focus = parseFocusPlane(input.focus);\n const sourcePointImagePlaneMm = { x: input.sourcePointNativeSensorMm.x, y: -input.sourcePointNativeSensorMm.y };\n const mapping = prepared.mapping;\n let ideal = sourcePointImagePlaneMm;\n let distortionAreaFactor = 1;\n if (prepared.radial !== undefined) {\n ideal = calculateInverseRadialSourcePointValue(sourcePointImagePlaneMm, prepared.radial, \"sourcePointNativeSensorMm\").sourceImagePointMm;\n // Radial map p -> s(r) p: det = s (s + r ds/dr) with r ds/dr = 2 r^2 s'(r^2).\n const { k1, k2, k3 } = prepared.radial.coefficients;\n const r2 = (ideal.x*ideal.x+ideal.y*ideal.y)/prepared.radial.normalizationRadiusMm**2;\n const scale = 1+k1*r2+k2*r2*r2+k3*r2*r2*r2;\n const radialDerivative = 1+3*k1*r2+5*k2*r2*r2+7*k3*r2*r2*r2;\n distortionAreaFactor = 1/(scale*radialDerivative);\n } else if (prepared.brownConrady !== undefined) {\n ideal = brownConradyInverseValue(prepared.brownConrady, sourcePointImagePlaneMm);\n const j = brownConradyForwardValue(prepared.brownConrady, ideal).jacobian;\n distortionAreaFactor = 1/(j[0]*j[3]-j[1]*j[2]);\n }\n if (!(distortionAreaFactor > 0) || !Number.isFinite(distortionAreaFactor)) {\n throw new InvalidScientificInputError(\"Distortion area factor must remain finite and positive.\");\n }\n let projection: ReturnType<typeof calculateInverseCameraRotationImageMapping> | null = null;\n let look: { x: number; y: number; z: number };\n let projectionScaleMm: number;\n if (mapping.projectionModel === \"rectilinear\") {\n projection = calculateInverseCameraRotationImageMapping({ focalLengthMm: input.focalLengthMm,\n imagePointMm: ideal, angularVelocityRadPerSec: input.angularVelocityRadPerSec,\n timeSecondsFromExposureStart: input.timeSecondsFromOpeningReference,\n ...(focus.kind === \"finite\" ? { focusDistanceM: focus.distanceM } : {}) });\n const point = projection.value.referenceImagePointMm, z = projection.value.projectionDistanceMm;\n const length = Math.hypot(point.x, point.y, z);\n look = { x: point.x/length, y: point.y/length, z: z/length };\n projectionScaleMm = z;\n } else {\n projectionScaleMm = focus.kind === \"finite\"\n ? calculateThinLensImageDistance({ focalLengthMm: input.focalLengthMm, objectDistanceM: focus.distanceM }).value.imageDistanceMm\n : input.focalLengthMm;\n const velocity = input.angularVelocityRadPerSec;\n const t = input.timeSecondsFromOpeningReference;\n if (typeof t !== \"number\" || !Number.isFinite(t) || t < 0) {\n throw new InvalidScientificInputError(\"timeSecondsFromOpeningReference must be finite and non-negative.\");\n }\n const captured = lensProjectionDirectionValue(mapping.projectionModel, projectionScaleMm, ideal.x, ideal.y);\n const reference = applyPhysicalCameraRotation(captured,\n { pitch: velocity.pitch*t, yaw: velocity.yaw*t, roll: velocity.roll*t }, \"camera-to-reference\");\n const length = Math.hypot(reference.x, reference.y, reference.z);\n if (!(length > 0) || !Number.isFinite(length)) throw new InvalidScientificInputError(\"Mapped reference ray must be finite.\");\n look = { x: reference.x/length, y: reference.y/length, z: reference.z/length };\n }\n const request = parseSceneRadianceEvaluationRequest({ ...input.request,\n timeSecondsFromExposureStart: input.timeSecondsFromOpeningReference,\n target: { kind: \"environment-direction\", outgoingDirectionUnitVector: { x: -look.x, y: -look.y, z: -look.z } } });\n return approximationResult({ request, sourcePointNativeSensorMm: { ...input.sourcePointNativeSensorMm },\n sourcePointImagePlaneMm, referenceLookDirectionUnitVector: look, projection,\n lensFieldMapping: { projectionModel: mapping.projectionModel, distortionKind: mapping.distortion.kind,\n idealImagePointMm: { ...ideal }, projectionScaleMm, distortionAreaFactor },\n targetMeaning: \"environment-direction-in-exposure-start-camera-axes\", directionConvention: \"outgoing-radiance-toward-camera\",\n geometricRayProjectionCalculated: true, sceneIntersectionCalculated: false, visibilityCalculated: false, providerExecuted: false\n }, \"lens-mapped-sensor-environment-radiance-query\", \"0.1.0\", [\n \"The physical sensor point is inverse-distorted to the ideal image point, then unprojected with the declared ideal projection and rotated by the constant-axis pure camera rotation.\",\n \"Projection scale is the thin-lens image distance at finite focus (focal length at infinity) for every projection model; a calibrated finite-focus fisheye scale is not modeled.\",\n \"The distortion area factor 1/det(d distorted/d ideal) is reported for exactly-once application to irradiance; field throughput, PSF and pupil models are not changed by this query.\",\n \"Breathing, chromatic field mapping, translation/parallax and scene intersections remain separate.\"\n ]);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sensor-environment-query.js","sourceRoot":"","sources":["../../src/optics/sensor-environment-query.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Builds an environment radiance request from points on the physical sensor. It uses the existing\n * focus-aware ideal projection and the analytic inverse camera rotation.\n *\n * Native Y (+Y down) is inverted explicitly. The direction sent to the provider points opposite the\n * look ray at exposure start, because radiance travels toward the camera. This module does not\n * intersect the scene, compute radiance or render anything.\n *\n * Optional lens field mapping (2.0):\n * 1. The physical sensor point is inverse-distorted (radial or Brown-Conrady) to the ideal image\n * point.\n * 2. That point is unprojected with the declared ideal projection (rectilinear, fisheye or\n * equirectangular), at the same focus-aware projection scale.\n * 3. The ray is rotated to the camera axes at exposure start.\n * The local area factor 1 / det(d distorted / d ideal) is reported. The caller applies it to\n * irradiance exactly once. Without the mapping, the existing rectilinear computation is unchanged.\n * @see docs/PHYSICS_FOUNDATION.md for the equations, units, coordinate conventions, known blockers\n * and support limits.\n */\n\nimport { approximationResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport { applyPhysicalCameraRotation, calculateInverseCameraRotationImageMapping,\n type CameraAngularVelocityRadPerSec } from \"../motion/camera-rotation.js\";\nimport { lensProjectionDirectionValue, parseLensProjectionModel, type LensProjectionModel } from \"../camera/lens-projection.js\";\nimport {\n calculateInverseRadialSourcePointValue, validateRadialDistortionProfile, type RadialDistortionProfile, type ValidatedRadialProfile\n} from \"./radial-distortion.js\";\nimport {\n brownConradyForwardValue, brownConradyInverseValue, validateBrownConradyDistortionProfile,\n type BrownConradyDistortionProfile, type ValidatedBrownConradyDistortionProfile\n} from \"./brown-conrady-distortion.js\";\nimport { calculateThinLensImageDistance } from \"./thin-lens.js\";\nimport { parseSceneRadianceEvaluationRequest, type SceneRadianceEvaluationRequest } from \"../schema/scene-radiance.js\";\nimport { parseFocusPlane, type FocusPlane } from \"./focus-state.js\";\nimport { requireInputKeys } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { hypot3 } from \"../core/portable-math.js\";\n\n/**\n * Lens field mapping between the physical (distorted) sensor point and the scene ray (2.0).\n * `radiometricAreaCorrection` states who applies the distortion area factor. The environment\n * photo signal multiplies irradiance by 1 / det(d distorted / d ideal) exactly once. A supplied\n * field-throughput profile must therefore not include it as well.\n */\nexport interface EnvironmentLensFieldMapping {\n projectionModel: LensProjectionModel;\n distortion:\n | { kind: \"none\" }\n | { kind: \"radial\"; profile: RadialDistortionProfile }\n | { kind: \"brown-conrady\"; profile: BrownConradyDistortionProfile };\n radiometricAreaCorrection: \"inverse-distortion-jacobian\";\n}\n\n/** @internal Parsed mapping with validated profiles, reused across one plan's queries. */\nexport interface PreparedEnvironmentLensFieldMapping {\n mapping: EnvironmentLensFieldMapping;\n radial?: ValidatedRadialProfile;\n brownConrady?: ValidatedBrownConradyDistortionProfile;\n}\n\n/** @internal Strict parse and validation of a lens field mapping. Unknown fields are rejected. */\nexport function prepareEnvironmentLensFieldMapping(value: unknown): PreparedEnvironmentLensFieldMapping {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new InvalidConfigurationError(\"lensFieldMapping must be an object.\");\n }\n const record = value as Record<string, unknown>;\n for (const key of Object.keys(record)) {\n if (![\"projectionModel\", \"distortion\", \"radiometricAreaCorrection\"].includes(key)) {\n throw new InvalidConfigurationError(`lensFieldMapping has unknown field ${JSON.stringify(key)}.`);\n }\n }\n if (record.radiometricAreaCorrection !== \"inverse-distortion-jacobian\") {\n throw new InvalidConfigurationError(\"lensFieldMapping.radiometricAreaCorrection must be \\\"inverse-distortion-jacobian\\\".\");\n }\n const projectionModel = parseLensProjectionModel(record.projectionModel);\n const distortion = record.distortion as EnvironmentLensFieldMapping[\"distortion\"] | undefined;\n if (typeof distortion !== \"object\" || distortion === null) {\n throw new InvalidConfigurationError(\"lensFieldMapping.distortion must be an object.\");\n }\n if (distortion.kind === \"none\") {\n return { mapping: { projectionModel, distortion: { kind: \"none\" }, radiometricAreaCorrection: \"inverse-distortion-jacobian\" } };\n }\n if (distortion.kind === \"radial\") {\n const radial = validateRadialDistortionProfile(distortion.profile);\n return { mapping: { projectionModel, distortion: { kind: \"radial\", profile: distortion.profile },\n radiometricAreaCorrection: \"inverse-distortion-jacobian\" }, radial };\n }\n if (distortion.kind === \"brown-conrady\") {\n const brownConrady = validateBrownConradyDistortionProfile(distortion.profile);\n return { mapping: { projectionModel, distortion: { kind: \"brown-conrady\", profile: distortion.profile },\n radiometricAreaCorrection: \"inverse-distortion-jacobian\" }, brownConrady };\n }\n throw new InvalidConfigurationError(\"lensFieldMapping.distortion.kind must be none, radial or brown-conrady.\");\n}\n\nexport interface CalculateSensorEnvironmentRadianceQueryInput {\n /** Optical sample point before the anti-aliasing filter or PSF. Native +X right, +Y down, in mm from the optical axis. */\n sourcePointNativeSensorMm: { x: number; y: number };\n focalLengthMm: number;\n focus: FocusPlane;\n angularVelocityRadPerSec: CameraAngularVelocityRadPerSec;\n /** States that the moment the shutter opens is t=0 in the rotation model. */\n timeReference: \"first-opening-boundary-phase\";\n timeSecondsFromOpeningReference: number;\n /** The request uses the direction radiance travels: toward the camera, opposite the look ray. */\n environmentDirectionConvention: \"outgoing-radiance-toward-camera\";\n request: Omit<SceneRadianceEvaluationRequest, \"target\" | \"timeSecondsFromExposureStart\">;\n /** Optional 2.0 lens field mapping. Omit it for the ideal rectilinear, distortion-free query. */\n lensFieldMapping?: EnvironmentLensFieldMapping;\n}\n\nexport interface SensorEnvironmentLensFieldMappingResult {\n projectionModel: LensProjectionModel;\n distortionKind: \"none\" | \"radial\" | \"brown-conrady\";\n /** Ideal (undistorted) image-plane point, +X right / +Y up, mm. */\n idealImagePointMm: { x: number; y: number };\n /** Projection scale: thin-lens image distance at finite focus, focal length at infinity, mm. */\n projectionScaleMm: number;\n /** 1 / det(d distorted / d ideal) at this point; 1 without distortion. */\n distortionAreaFactor: number;\n}\n\nexport interface SensorEnvironmentRadianceQuery {\n request: SceneRadianceEvaluationRequest;\n sourcePointNativeSensorMm: { x: number; y: number };\n sourcePointImagePlaneMm: { x: number; y: number };\n referenceLookDirectionUnitVector: { x: number; y: number; z: number };\n /** Rectilinear rotation mapping. Null when a non-rectilinear lens field mapping is used. */\n projection: ReturnType<typeof calculateInverseCameraRotationImageMapping> | null;\n /** Present only when the input declared a lens field mapping. */\n lensFieldMapping?: SensorEnvironmentLensFieldMappingResult;\n targetMeaning: \"environment-direction-in-exposure-start-camera-axes\";\n directionConvention: \"outgoing-radiance-toward-camera\";\n geometricRayProjectionCalculated: true;\n sceneIntersectionCalculated: false;\n visibilityCalculated: false;\n providerExecuted: false;\n}\n\n/**\n * Builds an environment radiance request from points on the physical sensor,\n * using the existing focus-aware ideal projection and the analytic inverse\n * camera rotation.\n *\n * Native Y (+Y down) is inverted explicitly. The provider direction points\n * opposite the look ray at exposure start. Nothing intersects the scene,\n * computes radiance or renders.\n */\nexport function calculateSensorEnvironmentRadianceQuery(\n input: CalculateSensorEnvironmentRadianceQueryInput\n): CalculationResult<SensorEnvironmentRadianceQuery> {\n requireInputKeys(input, \"calculateSensorEnvironmentRadianceQuery\", [\"sourcePointNativeSensorMm\", \"focalLengthMm\", \"focus\", \"angularVelocityRadPerSec\", \"timeReference\", \"timeSecondsFromOpeningReference\", \"environmentDirectionConvention\", \"request\", \"lensFieldMapping\"]);\n if (input.lensFieldMapping !== undefined) {\n return calculateMappedSensorEnvironmentRadianceQuery(input, prepareEnvironmentLensFieldMapping(input.lensFieldMapping));\n }\n if (input.timeReference !== \"first-opening-boundary-phase\" ||\n input.environmentDirectionConvention !== \"outgoing-radiance-toward-camera\") {\n throw new InvalidScientificInputError(\"Environment projection requires explicit opening-boundary time and outgoing-toward-camera direction conventions.\");\n }\n const focus = parseFocusPlane(input.focus);\n const sourcePointImagePlaneMm = { x: input.sourcePointNativeSensorMm.x, y: -input.sourcePointNativeSensorMm.y };\n const projection = calculateInverseCameraRotationImageMapping({ focalLengthMm: input.focalLengthMm,\n imagePointMm: sourcePointImagePlaneMm, angularVelocityRadPerSec: input.angularVelocityRadPerSec,\n timeSecondsFromExposureStart: input.timeSecondsFromOpeningReference,\n ...(focus.kind === \"finite\" ? { focusDistanceM: focus.distanceM } : {}) });\n const point = projection.value.referenceImagePointMm, z = projection.value.projectionDistanceMm;\n const length = hypot3(point.x, point.y, z);\n const referenceLookDirectionUnitVector = { x: point.x/length, y: point.y/length, z: z/length };\n const ray = referenceLookDirectionUnitVector;\n const request = parseSceneRadianceEvaluationRequest({ ...input.request,\n timeSecondsFromExposureStart: input.timeSecondsFromOpeningReference,\n target: { kind: \"environment-direction\", outgoingDirectionUnitVector: { x: -ray.x, y: -ray.y, z: -ray.z } } });\n return approximationResult({ request, sourcePointNativeSensorMm: { ...input.sourcePointNativeSensorMm },\n sourcePointImagePlaneMm, referenceLookDirectionUnitVector, projection,\n targetMeaning: \"environment-direction-in-exposure-start-camera-axes\", directionConvention: \"outgoing-radiance-toward-camera\",\n geometricRayProjectionCalculated: true, sceneIntersectionCalculated: false, visibilityCalculated: false, providerExecuted: false\n }, \"sensor-environment-radiance-query\", \"0.1.0\", [\n \"Ideal focus-aware rectilinear projection and constant-axis pure camera rotation; reference ray must stay in the forward hemisphere.\",\n \"The environment and rotation reference axes coincide with camera axes at the first opening boundary by explicit convention.\",\n \"Lens distortion, breathing, chromatic field mapping, translation/parallax and scene intersections remain separate.\"\n ]);\n}\n\n/**\n * @internal Query with a lens field mapping that is already validated. The environment photo-signal\n * planner shares it, so profiles are validated once per plan.\n */\nexport function calculateMappedSensorEnvironmentRadianceQuery(\n input: CalculateSensorEnvironmentRadianceQueryInput,\n prepared: PreparedEnvironmentLensFieldMapping\n): CalculationResult<SensorEnvironmentRadianceQuery> {\n if (input.timeReference !== \"first-opening-boundary-phase\" ||\n input.environmentDirectionConvention !== \"outgoing-radiance-toward-camera\") {\n throw new InvalidScientificInputError(\"Environment projection requires explicit opening-boundary time and outgoing-toward-camera direction conventions.\");\n }\n const focus = parseFocusPlane(input.focus);\n const sourcePointImagePlaneMm = { x: input.sourcePointNativeSensorMm.x, y: -input.sourcePointNativeSensorMm.y };\n const mapping = prepared.mapping;\n let ideal = sourcePointImagePlaneMm;\n let distortionAreaFactor = 1;\n if (prepared.radial !== undefined) {\n ideal = calculateInverseRadialSourcePointValue(sourcePointImagePlaneMm, prepared.radial, \"sourcePointNativeSensorMm\").sourceImagePointMm;\n // Radial map p -> s(r) p: det = s (s + r ds/dr) with r ds/dr = 2 r^2 s'(r^2).\n const { k1, k2, k3 } = prepared.radial.coefficients;\n const r2 = (ideal.x*ideal.x+ideal.y*ideal.y)/prepared.radial.normalizationRadiusMm**2;\n const scale = 1+k1*r2+k2*r2*r2+k3*r2*r2*r2;\n const radialDerivative = 1+3*k1*r2+5*k2*r2*r2+7*k3*r2*r2*r2;\n distortionAreaFactor = 1/(scale*radialDerivative);\n } else if (prepared.brownConrady !== undefined) {\n ideal = brownConradyInverseValue(prepared.brownConrady, sourcePointImagePlaneMm);\n const j = brownConradyForwardValue(prepared.brownConrady, ideal).jacobian;\n distortionAreaFactor = 1/(j[0]*j[3]-j[1]*j[2]);\n }\n if (!(distortionAreaFactor > 0) || !Number.isFinite(distortionAreaFactor)) {\n throw new InvalidScientificInputError(\"Distortion area factor must remain finite and positive.\");\n }\n let projection: ReturnType<typeof calculateInverseCameraRotationImageMapping> | null = null;\n let look: { x: number; y: number; z: number };\n let projectionScaleMm: number;\n if (mapping.projectionModel === \"rectilinear\") {\n projection = calculateInverseCameraRotationImageMapping({ focalLengthMm: input.focalLengthMm,\n imagePointMm: ideal, angularVelocityRadPerSec: input.angularVelocityRadPerSec,\n timeSecondsFromExposureStart: input.timeSecondsFromOpeningReference,\n ...(focus.kind === \"finite\" ? { focusDistanceM: focus.distanceM } : {}) });\n const point = projection.value.referenceImagePointMm, z = projection.value.projectionDistanceMm;\n const length = hypot3(point.x, point.y, z);\n look = { x: point.x/length, y: point.y/length, z: z/length };\n projectionScaleMm = z;\n } else {\n projectionScaleMm = focus.kind === \"finite\"\n ? calculateThinLensImageDistance({ focalLengthMm: input.focalLengthMm, objectDistanceM: focus.distanceM }).value.imageDistanceMm\n : input.focalLengthMm;\n const velocity = input.angularVelocityRadPerSec;\n const t = input.timeSecondsFromOpeningReference;\n if (typeof t !== \"number\" || !Number.isFinite(t) || t < 0) {\n throw new InvalidScientificInputError(\"timeSecondsFromOpeningReference must be finite and non-negative.\");\n }\n const captured = lensProjectionDirectionValue(mapping.projectionModel, projectionScaleMm, ideal.x, ideal.y);\n const reference = applyPhysicalCameraRotation(captured,\n { pitch: velocity.pitch*t, yaw: velocity.yaw*t, roll: velocity.roll*t }, \"camera-to-reference\");\n const length = hypot3(reference.x, reference.y, reference.z);\n if (!(length > 0) || !Number.isFinite(length)) throw new InvalidScientificInputError(\"Mapped reference ray must be finite.\");\n look = { x: reference.x/length, y: reference.y/length, z: reference.z/length };\n }\n const request = parseSceneRadianceEvaluationRequest({ ...input.request,\n timeSecondsFromExposureStart: input.timeSecondsFromOpeningReference,\n target: { kind: \"environment-direction\", outgoingDirectionUnitVector: { x: -look.x, y: -look.y, z: -look.z } } });\n return approximationResult({ request, sourcePointNativeSensorMm: { ...input.sourcePointNativeSensorMm },\n sourcePointImagePlaneMm, referenceLookDirectionUnitVector: look, projection,\n lensFieldMapping: { projectionModel: mapping.projectionModel, distortionKind: mapping.distortion.kind,\n idealImagePointMm: { ...ideal }, projectionScaleMm, distortionAreaFactor },\n targetMeaning: \"environment-direction-in-exposure-start-camera-axes\", directionConvention: \"outgoing-radiance-toward-camera\",\n geometricRayProjectionCalculated: true, sceneIntersectionCalculated: false, visibilityCalculated: false, providerExecuted: false\n }, \"lens-mapped-sensor-environment-radiance-query\", \"0.1.0\", [\n \"The physical sensor point is inverse-distorted to the ideal image point, then unprojected with the declared ideal projection and rotated by the constant-axis pure camera rotation.\",\n \"Projection scale is the thin-lens image distance at finite focus (focal length at infinity) for every projection model; a calibrated finite-focus fisheye scale is not modeled.\",\n \"The distortion area factor 1/det(d distorted/d ideal) is reported for exactly-once application to irradiance; field throughput, PSF and pupil models are not changed by this query.\",\n \"Breathing, chromatic field mapping, translation/parallax and scene intersections remain separate.\"\n ]);\n}\n"]}
|
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import { resolveLensPsfConvolutionKernels } from "./lens-psf-kernels.js";
|
|
2
2
|
type Psf = Parameters<typeof resolveLensPsfConvolutionKernels>[0]["psf"];
|
|
3
|
+
/** PSF records kept by value: at most 20 kernels of (2·32 + 1)² doubles each (676 KB) per record at the default spectral sampling. */
|
|
4
|
+
export declare const SITE_DIFFRACTION_KERNEL_CACHE_SIZE = 4;
|
|
5
|
+
/**
|
|
6
|
+
* Records that `psf.profile` is the profile `createDiffractionLimitedPsfProfile(profileInput)` returned, unmodified, so that the
|
|
7
|
+
* kernels of `psf` are those of any record with the same `profileInput` and the same other fields. Call it right after building the
|
|
8
|
+
* record; neither the record nor `profileInput` may change afterwards.
|
|
9
|
+
*/
|
|
10
|
+
export declare function registerDiffractionPsfKey(psf: Psf, profileInput: object): void;
|
|
3
11
|
/** The (2·radius + 1)² kernel of `psf` at `wavelengthNm` on a square site grid of `pitchMicrometers`; `failure` is the error message when the kernel cannot sample the pitch exactly. */
|
|
4
12
|
export declare function siteDiffractionKernel(psf: Psf, wavelengthNm: number, pitchMicrometers: number, radius: number, failure: string): Float64Array;
|
|
13
|
+
/** Test hook: the number of PSF records kept by value, the kernels resolved so far, and whether `psf` is registered. */
|
|
14
|
+
export declare function siteDiffractionKernelCacheState(psf?: Psf): {
|
|
15
|
+
valueEntries: number;
|
|
16
|
+
resolves: number;
|
|
17
|
+
registered: boolean;
|
|
18
|
+
};
|
|
19
|
+
/** Test hook: forgets every kernel kept by value. */
|
|
20
|
+
export declare function clearSiteDiffractionKernelCache(): void;
|
|
5
21
|
export {};
|