@photivra/engine 2.8.1 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +144 -1
- package/CITATION.cff +1 -1
- package/README.md +8 -6
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +2 -0
- package/dist/capture/export-jpeg.d.ts +19 -0
- package/dist/capture/export-jpeg.js +190 -108
- package/dist/capture/export-jpeg.js.map +1 -1
- package/dist/capture/native-environment-lean.js +106 -12
- 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 +13 -0
- package/dist/core/portable-math.js +43 -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/diffraction-provider.js +50 -15
- package/dist/facade/diffraction-provider.js.map +1 -1
- package/dist/facade/display-view.d.ts +75 -0
- package/dist/facade/display-view.js +471 -109
- package/dist/facade/display-view.js.map +1 -1
- package/dist/facade/expected-signal-setup.js +2 -1
- package/dist/facade/expected-signal-setup.js.map +1 -1
- package/dist/facade/expected-signal.d.ts +36 -4
- package/dist/facade/expected-signal.js +331 -43
- package/dist/facade/expected-signal.js.map +1 -1
- package/dist/facade/scene-radiance.d.ts +4 -1
- package/dist/facade/scene-radiance.js +4 -4
- package/dist/facade/scene-radiance.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +77 -5
- package/dist/facade/simulate-capture.js +232 -40
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.d.ts +25 -2
- package/dist/facade/simulate-display-view.js +55 -21
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/image-kernel-execution.d.ts +5 -0
- package/dist/gpu/image-kernel-execution.js +2 -2
- package/dist/gpu/image-kernel-execution.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/optics/diffraction-psf-profile.d.ts +19 -17
- package/dist/optics/diffraction-psf-profile.js +49 -18
- package/dist/optics/diffraction-psf-profile.js.map +1 -1
- 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/output/print-jpeg.d.ts +28 -1
- package/dist/output/print-jpeg.js +79 -7
- package/dist/output/print-jpeg.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/dist/sensor/stochastic-seed.d.ts +12 -0
- package/dist/sensor/stochastic-seed.js +25 -0
- package/dist/sensor/stochastic-seed.js.map +1 -1
- package/docs/API_REFERENCE.md +23 -1
- package/docs/QUICK_START.md +77 -8
- package/docs/USAGE.md +30 -0
- package/docs/api/capture-export-jpeg.md +10 -0
- package/docs/api/core-version.md +1 -1
- package/docs/api/exports.json +77 -2
- package/docs/api/facade-display-view.md +50 -0
- package/docs/api/facade-scene-radiance.md +2 -1
- package/docs/api/facade-simulate-capture.md +88 -8
- package/docs/api/facade-simulate-display-view.md +23 -1
- package/docs/api/gpu-image-kernel-execution.md +5 -0
- package/docs/api/optics-diffraction-psf-profile.md +1 -1
- package/docs/api/output-print-jpeg.md +41 -0
- package/package.json +1 -1
|
@@ -484,30 +484,7 @@ function validateSceneIdentity(request, result) {
|
|
|
484
484
|
throw new InvalidScientificInputError("Scene-radiance request/result identity must match exactly.");
|
|
485
485
|
}
|
|
486
486
|
}
|
|
487
|
-
|
|
488
|
-
* Applies the declared pupil acceptance and exactly one transmission or field-throughput path to
|
|
489
|
-
* spectral scene radiance. Returns the irradiance before the sensor stack, with conservative
|
|
490
|
-
* evidence.
|
|
491
|
-
*
|
|
492
|
-
* The optical bridge turns outgoing scene radiance (W/m²/sr/nm) into irradiance (W/m²/nm) arriving
|
|
493
|
-
* at the front of the sensor stack.
|
|
494
|
-
* - Circular paraxial acceptance is pi/(4*Nworking²). At finite focus with unit pupil magnification,
|
|
495
|
-
* the working f-number is N*(1+m).
|
|
496
|
-
* - Spectral transmission and an effective working T-stop are alternative paths; use one.
|
|
497
|
-
* - Field throughput is applied exactly once.
|
|
498
|
-
* - Sensor response and PSF (point-spread function) blur happen later in the pipeline.
|
|
499
|
-
* @param input - CalculateSceneRadianceToSensorIrradianceInput. See that type for coordinates, units and profile binding.
|
|
500
|
-
* @returns CalculationResult<
|
|
501
|
-
SceneToSensorIrradianceResult
|
|
502
|
-
>. The result states its shape and scientific status; a successful call does not imply calibration.
|
|
503
|
-
*
|
|
504
|
-
* @see docs/API_REFERENCE.md for the root export and exact type graph.
|
|
505
|
-
*/
|
|
506
|
-
export function calculateSceneRadianceToSensorIrradiance(input) {
|
|
507
|
-
requireInputKeys(input, "calculateSceneRadianceToSensorIrradiance", ["sceneRadianceRequest", "sceneRadianceResult", "profile", "focalLengthMm", "nominalFNumber", "focus", "imagePointMm", "fieldThroughput", "frontOfLensFilters"]);
|
|
508
|
-
const request = parseSceneRadianceEvaluationRequest(input.sceneRadianceRequest);
|
|
509
|
-
const sceneResult = parseSceneRadianceEvaluationResult(input.sceneRadianceResult);
|
|
510
|
-
validateSceneIdentity(request, sceneResult);
|
|
487
|
+
function prepareSceneRadianceToSensorIrradiance(input) {
|
|
511
488
|
const profile = parseSceneToSensorIrradianceProfile(input.profile);
|
|
512
489
|
const focalLengthMm = requirePositiveFiniteScientific(input.focalLengthMm, "focalLengthMm");
|
|
513
490
|
const nominalFNumber = requirePositiveFiniteScientific(input.nominalFNumber, "nominalFNumber");
|
|
@@ -535,6 +512,15 @@ export function calculateSceneRadianceToSensorIrradiance(input) {
|
|
|
535
512
|
throw new InvalidScientificInputError("Paraxial geometric acceptance must remain finite and greater than zero.");
|
|
536
513
|
}
|
|
537
514
|
const transmission = profile.transmission;
|
|
515
|
+
return { profile, focalLengthMm, nominalFNumber, working, workingFNumberValue, geometricAcceptance, transmission };
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* The per-query part of {@link calculateSceneRadianceToSensorIrradiance}, after the request and result are parsed and their
|
|
519
|
+
* identities matched: the wavelength-basis check, the transmission or T-stop path, the front-of-lens filters, the field
|
|
520
|
+
* throughput and the irradiance, with the same operations in the same order.
|
|
521
|
+
*/
|
|
522
|
+
function applySceneRadianceToSensorIrradiance(prepared, input, request, sceneResult) {
|
|
523
|
+
const { workingFNumberValue, geometricAcceptance, transmission } = prepared;
|
|
538
524
|
if (request.wavelengthBasis !==
|
|
539
525
|
transmission.wavelengthBasis) {
|
|
540
526
|
throw new InvalidScientificInputError("Scene-radiance and optical-transmission wavelength bases must match; implicit air/vacuum conversion is not performed.");
|
|
@@ -616,6 +602,54 @@ export function calculateSceneRadianceToSensorIrradiance(input) {
|
|
|
616
602
|
irradiance < 0) {
|
|
617
603
|
throw new InvalidScientificInputError("Sensor-plane spectral irradiance must remain finite and non-negative.");
|
|
618
604
|
}
|
|
605
|
+
return { spectralTransmissionFactor, effectiveWorkingTStop, tStopEquivalentTransmissionFactor, effectiveAcceptance, transmissionEvidence,
|
|
606
|
+
frontOfLensFilterCount, frontOfLensFilterPolarizationModeled, frontOfLensFilterTransmissionFactor, frontOfLensFilterAttenuationStops,
|
|
607
|
+
frontOfLensFilterEvidence, frontOfLensFilterLimitations, fieldThroughputFactor, sceneRadiance, irradiance };
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Internal (#453; not exported from the package): the sensor-plane spectral irradiance that
|
|
611
|
+
* {@link calculateSceneRadianceToSensorIrradiance} returns, for repeated queries of one optical state. This call runs the
|
|
612
|
+
* request-independent checks once (the public function's own code, in its order); each call of the returned function then
|
|
613
|
+
* runs the public function's per-query steps on an engine-built request and a result already parsed by the caller: the identity
|
|
614
|
+
* match, the wavelength-basis check, the transmission or T-stop path, the front-of-lens filters, the field throughput and
|
|
615
|
+
* the finite, non-negative irradiance, with the same operations, order and errors. It skips only the re-validation of the
|
|
616
|
+
* unchanged optical inputs, the re-parse of the request and result, and the result envelope.
|
|
617
|
+
*/
|
|
618
|
+
export function prepareSceneRadianceToSensorIrradianceValue(optics) {
|
|
619
|
+
const prepared = prepareSceneRadianceToSensorIrradiance(optics);
|
|
620
|
+
const frontOfLensFilters = optics.frontOfLensFilters;
|
|
621
|
+
return (request, result, imagePointMm, fieldThroughput) => {
|
|
622
|
+
validateSceneIdentity(request, result);
|
|
623
|
+
return applySceneRadianceToSensorIrradiance(prepared, { imagePointMm, fieldThroughput, frontOfLensFilters }, request, result).irradiance;
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Applies the declared pupil acceptance and exactly one transmission or field-throughput path to
|
|
628
|
+
* spectral scene radiance. Returns the irradiance before the sensor stack, with conservative
|
|
629
|
+
* evidence.
|
|
630
|
+
*
|
|
631
|
+
* The optical bridge turns outgoing scene radiance (W/m²/sr/nm) into irradiance (W/m²/nm) arriving
|
|
632
|
+
* at the front of the sensor stack.
|
|
633
|
+
* - Circular paraxial acceptance is pi/(4*Nworking²). At finite focus with unit pupil magnification,
|
|
634
|
+
* the working f-number is N*(1+m).
|
|
635
|
+
* - Spectral transmission and an effective working T-stop are alternative paths; use one.
|
|
636
|
+
* - Field throughput is applied exactly once.
|
|
637
|
+
* - Sensor response and PSF (point-spread function) blur happen later in the pipeline.
|
|
638
|
+
* @param input - CalculateSceneRadianceToSensorIrradianceInput. See that type for coordinates, units and profile binding.
|
|
639
|
+
* @returns CalculationResult<
|
|
640
|
+
SceneToSensorIrradianceResult
|
|
641
|
+
>. The result states its shape and scientific status; a successful call does not imply calibration.
|
|
642
|
+
*
|
|
643
|
+
* @see docs/API_REFERENCE.md for the root export and exact type graph.
|
|
644
|
+
*/
|
|
645
|
+
export function calculateSceneRadianceToSensorIrradiance(input) {
|
|
646
|
+
requireInputKeys(input, "calculateSceneRadianceToSensorIrradiance", ["sceneRadianceRequest", "sceneRadianceResult", "profile", "focalLengthMm", "nominalFNumber", "focus", "imagePointMm", "fieldThroughput", "frontOfLensFilters"]);
|
|
647
|
+
const request = parseSceneRadianceEvaluationRequest(input.sceneRadianceRequest);
|
|
648
|
+
const sceneResult = parseSceneRadianceEvaluationResult(input.sceneRadianceResult);
|
|
649
|
+
validateSceneIdentity(request, sceneResult);
|
|
650
|
+
const prepared = prepareSceneRadianceToSensorIrradiance(input);
|
|
651
|
+
const { profile, focalLengthMm, nominalFNumber, working, workingFNumberValue, geometricAcceptance, transmission } = prepared;
|
|
652
|
+
const { spectralTransmissionFactor, effectiveWorkingTStop, tStopEquivalentTransmissionFactor, effectiveAcceptance, transmissionEvidence, frontOfLensFilterCount, frontOfLensFilterPolarizationModeled, frontOfLensFilterTransmissionFactor, frontOfLensFilterAttenuationStops, frontOfLensFilterEvidence, frontOfLensFilterLimitations, fieldThroughputFactor, sceneRadiance, irradiance } = applySceneRadianceToSensorIrradiance(prepared, input, request, sceneResult);
|
|
619
653
|
const limitations = [
|
|
620
654
|
...profile.limitations,
|
|
621
655
|
...frontOfLensFilterLimitations,
|
|
@@ -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":"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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 * 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 };
|