@photivra/engine 0.1.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/CITATION.cff +1 -1
  3. package/README.md +61 -12
  4. package/dist/camera/equivalent-focal-length.d.ts +34 -0
  5. package/dist/camera/equivalent-focal-length.d.ts.map +1 -0
  6. package/dist/camera/equivalent-focal-length.js +33 -0
  7. package/dist/camera/equivalent-focal-length.js.map +1 -0
  8. package/dist/camera/field-of-view.d.ts +40 -3
  9. package/dist/camera/field-of-view.d.ts.map +1 -1
  10. package/dist/camera/field-of-view.js +66 -24
  11. package/dist/camera/field-of-view.js.map +1 -1
  12. package/dist/core/calculation-result.d.ts.map +1 -1
  13. package/dist/core/calculation-result.js +14 -2
  14. package/dist/core/calculation-result.js.map +1 -1
  15. package/dist/core/configuration-error.d.ts +9 -0
  16. package/dist/core/configuration-error.d.ts.map +1 -0
  17. package/dist/core/configuration-error.js +13 -0
  18. package/dist/core/configuration-error.js.map +1 -0
  19. package/dist/core/evidence-provenance.d.ts +40 -0
  20. package/dist/core/evidence-provenance.d.ts.map +1 -0
  21. package/dist/core/evidence-provenance.js +79 -0
  22. package/dist/core/evidence-provenance.js.map +1 -0
  23. package/dist/core/image-formation.d.ts +70 -0
  24. package/dist/core/image-formation.d.ts.map +1 -0
  25. package/dist/core/image-formation.js +335 -0
  26. package/dist/core/image-formation.js.map +1 -0
  27. package/dist/exposure/exposure.d.ts +31 -0
  28. package/dist/exposure/exposure.d.ts.map +1 -1
  29. package/dist/exposure/exposure.js +35 -0
  30. package/dist/exposure/exposure.js.map +1 -1
  31. package/dist/index.d.ts +16 -4
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +16 -4
  34. package/dist/index.js.map +1 -1
  35. package/dist/motion/camera-rotation.d.ts +91 -0
  36. package/dist/motion/camera-rotation.d.ts.map +1 -0
  37. package/dist/motion/camera-rotation.js +157 -0
  38. package/dist/motion/camera-rotation.js.map +1 -0
  39. package/dist/optics/focus-breathing.d.ts +73 -0
  40. package/dist/optics/focus-breathing.d.ts.map +1 -0
  41. package/dist/optics/focus-breathing.js +77 -0
  42. package/dist/optics/focus-breathing.js.map +1 -0
  43. package/dist/optics/illumination-vignetting.d.ts +59 -0
  44. package/dist/optics/illumination-vignetting.d.ts.map +1 -0
  45. package/dist/optics/illumination-vignetting.js +107 -0
  46. package/dist/optics/illumination-vignetting.js.map +1 -0
  47. package/dist/optics/lateral-chromatic-aberration.d.ts +93 -0
  48. package/dist/optics/lateral-chromatic-aberration.d.ts.map +1 -0
  49. package/dist/optics/lateral-chromatic-aberration.js +174 -0
  50. package/dist/optics/lateral-chromatic-aberration.js.map +1 -0
  51. package/dist/optics/psf-foundation.d.ts +102 -0
  52. package/dist/optics/psf-foundation.d.ts.map +1 -0
  53. package/dist/optics/psf-foundation.js +164 -0
  54. package/dist/optics/psf-foundation.js.map +1 -0
  55. package/dist/optics/radial-distortion.d.ts +91 -0
  56. package/dist/optics/radial-distortion.d.ts.map +1 -0
  57. package/dist/optics/radial-distortion.js +198 -0
  58. package/dist/optics/radial-distortion.js.map +1 -0
  59. package/dist/output/capture-geometry.d.ts +225 -0
  60. package/dist/output/capture-geometry.d.ts.map +1 -0
  61. package/dist/output/capture-geometry.js +478 -0
  62. package/dist/output/capture-geometry.js.map +1 -0
  63. package/dist/schema/validation.d.ts +1 -8
  64. package/dist/schema/validation.d.ts.map +1 -1
  65. package/dist/schema/validation.js +2 -11
  66. package/dist/schema/validation.js.map +1 -1
  67. package/dist/sensor/architecture.d.ts +40 -0
  68. package/dist/sensor/architecture.d.ts.map +1 -0
  69. package/dist/sensor/architecture.js +98 -0
  70. package/dist/sensor/architecture.js.map +1 -0
  71. package/dist/sensor/radiometry-readiness.d.ts +111 -0
  72. package/dist/sensor/radiometry-readiness.d.ts.map +1 -0
  73. package/dist/sensor/radiometry-readiness.js +294 -0
  74. package/dist/sensor/radiometry-readiness.js.map +1 -0
  75. package/dist/sensor/sensor-geometry.d.ts +99 -0
  76. package/dist/sensor/sensor-geometry.d.ts.map +1 -0
  77. package/dist/sensor/sensor-geometry.js +71 -0
  78. package/dist/sensor/sensor-geometry.js.map +1 -0
  79. package/dist/simulation/poc-simulation.d.ts +93 -1
  80. package/dist/simulation/poc-simulation.d.ts.map +1 -1
  81. package/dist/simulation/poc-simulation.js +308 -24
  82. package/dist/simulation/poc-simulation.js.map +1 -1
  83. package/docs/API_STYLE.md +35 -2
  84. package/docs/ARCHITECTURE.md +179 -1
  85. package/docs/IMAGE_FORMATION.md +142 -0
  86. package/docs/MOTION_AND_SIGNAL.md +54 -1
  87. package/docs/PHYSICS_FOUNDATION.md +190 -3
  88. package/docs/POC_API.md +73 -7
  89. package/docs/PROVENANCE.md +24 -0
  90. package/docs/PSF_FOUNDATION.md +133 -0
  91. package/docs/STABILIZATION.md +17 -4
  92. package/docs/USAGE.md +680 -4
  93. package/package.json +2 -1
@@ -0,0 +1,107 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { approximationResult } from "../core/calculation-result.js";
3
+ import { InvalidScientificInputError, requirePositiveFinite } from "../core/validation.js";
4
+ function requireFinite(name, value) {
5
+ if (!Number.isFinite(value)) {
6
+ throw new InvalidScientificInputError(`${name} must be finite.`);
7
+ }
8
+ }
9
+ function throughputFromSquaredRadius(squaredRadius, coefficients) {
10
+ return (1 +
11
+ coefficients.r2 * squaredRadius +
12
+ coefficients.r4 * squaredRadius * squaredRadius +
13
+ coefficients.r6 *
14
+ squaredRadius *
15
+ squaredRadius *
16
+ squaredRadius);
17
+ }
18
+ function throughputCriticalSquaredRadii(coefficients) {
19
+ // For s = rho²:
20
+ // T(s) = 1 + r2*s + r4*s² + r6*s³
21
+ // dT/ds = r2 + 2*r4*s + 3*r6*s²
22
+ const a = 3 * coefficients.r6;
23
+ const b = 2 * coefficients.r4;
24
+ const c = coefficients.r2;
25
+ if (a === 0) {
26
+ return b === 0 ? [] : [-c / b];
27
+ }
28
+ const discriminant = b * b - 4 * a * c;
29
+ if (discriminant < 0) {
30
+ return [];
31
+ }
32
+ const root = Math.sqrt(discriminant);
33
+ return [(-b - root) / (2 * a), (-b + root) / (2 * a)];
34
+ }
35
+ function validateProfile(profile) {
36
+ requirePositiveFinite("profile.normalizationRadiusMm", profile.normalizationRadiusMm);
37
+ requirePositiveFinite("profile.maximumNormalizedRadius", profile.maximumNormalizedRadius);
38
+ requireFinite("profile.coefficients.r2", profile.coefficients.r2);
39
+ requireFinite("profile.coefficients.r4", profile.coefficients.r4);
40
+ requireFinite("profile.coefficients.r6", profile.coefficients.r6);
41
+ const maximumSquaredRadius = profile.maximumNormalizedRadius * profile.maximumNormalizedRadius;
42
+ const candidates = [
43
+ 0,
44
+ maximumSquaredRadius,
45
+ ...throughputCriticalSquaredRadii(profile.coefficients).filter((value) => value > 0 && value < maximumSquaredRadius)
46
+ ];
47
+ const throughputs = candidates.map((squaredRadius) => throughputFromSquaredRadius(squaredRadius, profile.coefficients));
48
+ const minimumThroughputFactor = Math.min(...throughputs);
49
+ const maximumThroughputFactor = Math.max(...throughputs);
50
+ if (!Number.isFinite(minimumThroughputFactor) ||
51
+ minimumThroughputFactor <= 0) {
52
+ throw new InvalidScientificInputError("Illumination vignetting profile must keep relative linear throughput strictly greater than zero over maximumNormalizedRadius.");
53
+ }
54
+ if (!Number.isFinite(maximumThroughputFactor) ||
55
+ maximumThroughputFactor > 1) {
56
+ throw new InvalidScientificInputError("Illumination vignetting profile must not amplify relative linear throughput above the optical-axis value of 1.");
57
+ }
58
+ return {
59
+ normalizationRadiusMm: profile.normalizationRadiusMm,
60
+ maximumNormalizedRadius: profile.maximumNormalizedRadius,
61
+ coefficients: { ...profile.coefficients },
62
+ minimumThroughputFactor,
63
+ maximumThroughputFactor
64
+ };
65
+ }
66
+ function assumptions() {
67
+ return [
68
+ "Generic rotationally symmetric illumination-vignetting approximation evaluated from ideal image-plane field radius",
69
+ "Relative linear throughput is normalized to 1 on the optical axis",
70
+ "The model attenuates scene-linear/channel-linear signal only and must be applied before display/gamma encoding",
71
+ "The model changes throughput only; it does not alter field coordinates, pupil shape, PSF shape, bokeh, or focus",
72
+ "Mechanical/pupil vignetting and cat's-eye bokeh are separate pupil/PSF effects",
73
+ "The profile is a generic caller-supplied radial approximation, not calibrated radiometry or a named-lens measurement"
74
+ ];
75
+ }
76
+ /**
77
+ * Calculates generic field-dependent illumination falloff as a multiplicative
78
+ * linear-light throughput factor.
79
+ *
80
+ * The profile is accepted only when its relative throughput remains strictly
81
+ * positive and never exceeds the optical-axis normalization of 1 throughout
82
+ * the complete declared operating envelope.
83
+ */
84
+ export function calculateIlluminationVignetting(input) {
85
+ requireFinite("imagePointMm.x", input.imagePointMm.x);
86
+ requireFinite("imagePointMm.y", input.imagePointMm.y);
87
+ const profile = validateProfile(input.profile);
88
+ const radiusMm = Math.hypot(input.imagePointMm.x, input.imagePointMm.y);
89
+ const normalizedRadius = radiusMm / profile.normalizationRadiusMm;
90
+ if (normalizedRadius > profile.maximumNormalizedRadius) {
91
+ throw new InvalidScientificInputError("imagePointMm lies outside the illumination vignetting profile maximumNormalizedRadius.");
92
+ }
93
+ const squaredRadius = normalizedRadius * normalizedRadius;
94
+ const linearThroughputFactor = throughputFromSquaredRadius(squaredRadius, profile.coefficients);
95
+ const attenuationStops = linearThroughputFactor === 1
96
+ ? 0
97
+ : -Math.log2(linearThroughputFactor);
98
+ return approximationResult({
99
+ imagePointMm: { ...input.imagePointMm },
100
+ normalizedRadius,
101
+ linearThroughputFactor,
102
+ attenuationStops,
103
+ profileMinimumThroughputFactor: profile.minimumThroughputFactor,
104
+ profileMaximumThroughputFactor: profile.maximumThroughputFactor
105
+ }, "generic-radial-illumination-vignetting", "1.0.0", assumptions());
106
+ }
107
+ //# sourceMappingURL=illumination-vignetting.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"illumination-vignetting.js","sourceRoot":"","sources":["../../src/optics/illumination-vignetting.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ import { type CalculationResult } from "../core/calculation-result.js";
2
+ import { type LensFieldPointMm, type RadialDistortionCoefficients } from "./radial-distortion.js";
3
+ export type LateralChromaticAberrationChannel = "red" | "green" | "blue";
4
+ export interface LateralChromaticAberrationProfile {
5
+ /** Shared physical image-plane normalization radius in millimetres. */
6
+ normalizationRadiusMm: number;
7
+ /** Shared undistorted normalized-radius operating envelope. */
8
+ maximumNormalizedRadius: number;
9
+ /**
10
+ * Green-reference base geometric distortion.
11
+ *
12
+ * This common mapping is included in every channel. Red/blue CA terms are
13
+ * added to this base, so callers should not apply a second distortion pass.
14
+ */
15
+ baseDistortionCoefficients: RadialDistortionCoefficients;
16
+ /** Additional red-channel radial coefficients relative to green. */
17
+ redCoefficientOffset: RadialDistortionCoefficients;
18
+ /** Additional blue-channel radial coefficients relative to green. */
19
+ blueCoefficientOffset: RadialDistortionCoefficients;
20
+ }
21
+ export interface CalculateLateralChromaticAberrationMappingInput {
22
+ /** Ideal/undistorted image-plane point shared by all channels. */
23
+ imagePointMm: LensFieldPointMm;
24
+ profile: LateralChromaticAberrationProfile;
25
+ }
26
+ export interface CalculateInverseLateralChromaticAberrationMappingInput {
27
+ /**
28
+ * Distorted output image-plane destination. Each output channel is inverse-
29
+ * mapped independently to its ideal source coordinate.
30
+ */
31
+ distortedImagePointMm: LensFieldPointMm;
32
+ profile: LateralChromaticAberrationProfile;
33
+ }
34
+ export interface LateralChromaticAberrationChannelMapping {
35
+ mappedImagePointMm: LensFieldPointMm;
36
+ radialScale: number;
37
+ combinedCoefficients: RadialDistortionCoefficients;
38
+ }
39
+ export interface InverseLateralChromaticAberrationChannelMapping {
40
+ sourceImagePointMm: LensFieldPointMm;
41
+ radialScaleAtSource: number;
42
+ combinedCoefficients: RadialDistortionCoefficients;
43
+ }
44
+ export interface ChannelSeparationVectorMm {
45
+ x: number;
46
+ y: number;
47
+ distance: number;
48
+ }
49
+ export interface LateralChromaticAberrationSeparation {
50
+ redGreen: ChannelSeparationVectorMm;
51
+ blueGreen: ChannelSeparationVectorMm;
52
+ redBlue: ChannelSeparationVectorMm;
53
+ maximumPairDistanceMm: number;
54
+ }
55
+ export interface LateralChromaticAberrationMapping {
56
+ direction: "undistorted-to-channel-distorted";
57
+ referenceChannel: "green";
58
+ sourceImagePointMm: LensFieldPointMm;
59
+ channels: {
60
+ red: LateralChromaticAberrationChannelMapping;
61
+ green: LateralChromaticAberrationChannelMapping;
62
+ blue: LateralChromaticAberrationChannelMapping;
63
+ };
64
+ separation: LateralChromaticAberrationSeparation;
65
+ }
66
+ export interface InverseLateralChromaticAberrationMapping {
67
+ direction: "distorted-output-to-channel-sources";
68
+ referenceChannel: "green";
69
+ distortedImagePointMm: LensFieldPointMm;
70
+ channels: {
71
+ red: InverseLateralChromaticAberrationChannelMapping;
72
+ green: InverseLateralChromaticAberrationChannelMapping;
73
+ blue: InverseLateralChromaticAberrationChannelMapping;
74
+ };
75
+ sourceSeparation: LateralChromaticAberrationSeparation;
76
+ }
77
+ /**
78
+ * Calculates generic lateral chromatic-aberration field separation by mapping
79
+ * one ideal image-plane point through a shared green-reference base distortion
80
+ * and red/blue radial coefficient offsets.
81
+ *
82
+ * This is channel-dependent field mapping, not a blur kernel.
83
+ */
84
+ export declare function calculateLateralChromaticAberrationMapping(input: CalculateLateralChromaticAberrationMappingInput): CalculationResult<LateralChromaticAberrationMapping>;
85
+ /**
86
+ * Inverse-maps one distorted output destination independently for the
87
+ * representative red/green/blue channels.
88
+ *
89
+ * Renderers can use the returned per-channel ideal source coordinates for
90
+ * inverse sampling without inventing chromatic-aberration equations.
91
+ */
92
+ export declare function calculateInverseLateralChromaticAberrationMapping(input: CalculateInverseLateralChromaticAberrationMappingInput): CalculationResult<InverseLateralChromaticAberrationMapping>;
93
+ //# sourceMappingURL=lateral-chromatic-aberration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lateral-chromatic-aberration.d.ts","sourceRoot":"","sources":["../../src/optics/lateral-chromatic-aberration.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,4BAA4B,EAElC,MAAM,wBAAwB,CAAC;AAEhC,MAAM,MAAM,iCAAiC,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,CAAC;AAEzE,MAAM,WAAW,iCAAiC;IAChD,uEAAuE;IACvE,qBAAqB,EAAE,MAAM,CAAC;IAC9B,+DAA+D;IAC/D,uBAAuB,EAAE,MAAM,CAAC;IAChC;;;;;OAKG;IACH,0BAA0B,EAAE,4BAA4B,CAAC;IACzD,oEAAoE;IACpE,oBAAoB,EAAE,4BAA4B,CAAC;IACnD,qEAAqE;IACrE,qBAAqB,EAAE,4BAA4B,CAAC;CACrD;AAED,MAAM,WAAW,+CAA+C;IAC9D,kEAAkE;IAClE,YAAY,EAAE,gBAAgB,CAAC;IAC/B,OAAO,EAAE,iCAAiC,CAAC;CAC5C;AAED,MAAM,WAAW,sDAAsD;IACrE;;;OAGG;IACH,qBAAqB,EAAE,gBAAgB,CAAC;IACxC,OAAO,EAAE,iCAAiC,CAAC;CAC5C;AAED,MAAM,WAAW,wCAAwC;IACvD,kBAAkB,EAAE,gBAAgB,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,oBAAoB,EAAE,4BAA4B,CAAC;CACpD;AAED,MAAM,WAAW,+CAA+C;IAC9D,kBAAkB,EAAE,gBAAgB,CAAC;IACrC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oBAAoB,EAAE,4BAA4B,CAAC;CACpD;AAED,MAAM,WAAW,yBAAyB;IACxC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oCAAoC;IACnD,QAAQ,EAAE,yBAAyB,CAAC;IACpC,SAAS,EAAE,yBAAyB,CAAC;IACrC,OAAO,EAAE,yBAAyB,CAAC;IACnC,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,iCAAiC;IAChD,SAAS,EAAE,kCAAkC,CAAC;IAC9C,gBAAgB,EAAE,OAAO,CAAC;IAC1B,kBAAkB,EAAE,gBAAgB,CAAC;IACrC,QAAQ,EAAE;QACR,GAAG,EAAE,wCAAwC,CAAC;QAC9C,KAAK,EAAE,wCAAwC,CAAC;QAChD,IAAI,EAAE,wCAAwC,CAAC;KAChD,CAAC;IACF,UAAU,EAAE,oCAAoC,CAAC;CAClD;AAED,MAAM,WAAW,wCAAwC;IACvD,SAAS,EAAE,qCAAqC,CAAC;IACjD,gBAAgB,EAAE,OAAO,CAAC;IAC1B,qBAAqB,EAAE,gBAAgB,CAAC;IACxC,QAAQ,EAAE;QACR,GAAG,EAAE,+CAA+C,CAAC;QACrD,KAAK,EAAE,+CAA+C,CAAC;QACvD,IAAI,EAAE,+CAA+C,CAAC;KACvD,CAAC;IACF,gBAAgB,EAAE,oCAAoC,CAAC;CACxD;AAyID;;;;;;GAMG;AACH,wBAAgB,0CAA0C,CACxD,KAAK,EAAE,+CAA+C,GACrD,iBAAiB,CAAC,iCAAiC,CAAC,CAsDtD;AAED;;;;;;GAMG;AACH,wBAAgB,iDAAiD,CAC/D,KAAK,EAAE,sDAAsD,GAC5D,iBAAiB,CAAC,wCAAwC,CAAC,CAsD7D"}
@@ -0,0 +1,174 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { approximationResult } from "../core/calculation-result.js";
3
+ import { InvalidScientificInputError } from "../core/validation.js";
4
+ import { calculateInverseRadialDistortionMapping, calculateRadialDistortionMapping } from "./radial-distortion.js";
5
+ function requireFiniteCoefficients(path, coefficients) {
6
+ for (const key of ["k1", "k2", "k3"]) {
7
+ if (!Number.isFinite(coefficients[key])) {
8
+ throw new InvalidScientificInputError(`${path}.${key} must be finite.`);
9
+ }
10
+ }
11
+ }
12
+ function addCoefficients(base, offset) {
13
+ return {
14
+ k1: base.k1 + offset.k1,
15
+ k2: base.k2 + offset.k2,
16
+ k3: base.k3 + offset.k3
17
+ };
18
+ }
19
+ function resolveProfiles(profile) {
20
+ requireFiniteCoefficients("profile.baseDistortionCoefficients", profile.baseDistortionCoefficients);
21
+ requireFiniteCoefficients("profile.redCoefficientOffset", profile.redCoefficientOffset);
22
+ requireFiniteCoefficients("profile.blueCoefficientOffset", profile.blueCoefficientOffset);
23
+ const shared = {
24
+ normalizationRadiusMm: profile.normalizationRadiusMm,
25
+ maximumNormalizedRadius: profile.maximumNormalizedRadius
26
+ };
27
+ return {
28
+ red: {
29
+ ...shared,
30
+ coefficients: addCoefficients(profile.baseDistortionCoefficients, profile.redCoefficientOffset)
31
+ },
32
+ green: {
33
+ ...shared,
34
+ coefficients: { ...profile.baseDistortionCoefficients }
35
+ },
36
+ blue: {
37
+ ...shared,
38
+ coefficients: addCoefficients(profile.baseDistortionCoefficients, profile.blueCoefficientOffset)
39
+ }
40
+ };
41
+ }
42
+ function withChannelContext(channel, calculate) {
43
+ try {
44
+ return calculate();
45
+ }
46
+ catch (error) {
47
+ if (error instanceof InvalidScientificInputError) {
48
+ throw new InvalidScientificInputError(`Lateral chromatic aberration ${channel} channel: ${error.message}`);
49
+ }
50
+ throw error;
51
+ }
52
+ }
53
+ function separationVector(first, second) {
54
+ const x = first.x - second.x;
55
+ const y = first.y - second.y;
56
+ return {
57
+ x,
58
+ y,
59
+ distance: Math.hypot(x, y)
60
+ };
61
+ }
62
+ function separations(red, green, blue) {
63
+ const redGreen = separationVector(red, green);
64
+ const blueGreen = separationVector(blue, green);
65
+ const redBlue = separationVector(red, blue);
66
+ return {
67
+ redGreen,
68
+ blueGreen,
69
+ redBlue,
70
+ maximumPairDistanceMm: Math.max(redGreen.distance, blueGreen.distance, redBlue.distance)
71
+ };
72
+ }
73
+ function assumptions() {
74
+ return [
75
+ "Generic lateral chromatic aberration is represented as independent radial field mapping for abstract red, green, and blue renderer channels",
76
+ "Green is the reference field mapping; red and blue coefficient offsets are added to the green-reference base distortion rather than applied as a second warp",
77
+ "All channel mappings share one physical normalization radius and declared operating envelope",
78
+ "Each combined channel radial profile must remain one-to-one over the declared operating envelope",
79
+ "The RGB channel labels are representative rendering channels, not calibrated wavelengths, sensor spectral responses, or CFA primaries",
80
+ "This model changes channel field coordinates only; it does not blur, change PSF shape, modify channel intensity, or model longitudinal chromatic aberration",
81
+ "Profiles are generic caller inputs and do not represent a named lens unless separately calibrated with defensible provenance"
82
+ ];
83
+ }
84
+ /**
85
+ * Calculates generic lateral chromatic-aberration field separation by mapping
86
+ * one ideal image-plane point through a shared green-reference base distortion
87
+ * and red/blue radial coefficient offsets.
88
+ *
89
+ * This is channel-dependent field mapping, not a blur kernel.
90
+ */
91
+ export function calculateLateralChromaticAberrationMapping(input) {
92
+ const profiles = resolveProfiles(input.profile);
93
+ const red = withChannelContext("red", () => calculateRadialDistortionMapping({
94
+ imagePointMm: input.imagePointMm,
95
+ profile: profiles.red
96
+ })).value;
97
+ const green = withChannelContext("green", () => calculateRadialDistortionMapping({
98
+ imagePointMm: input.imagePointMm,
99
+ profile: profiles.green
100
+ })).value;
101
+ const blue = withChannelContext("blue", () => calculateRadialDistortionMapping({
102
+ imagePointMm: input.imagePointMm,
103
+ profile: profiles.blue
104
+ })).value;
105
+ return approximationResult({
106
+ direction: "undistorted-to-channel-distorted",
107
+ referenceChannel: "green",
108
+ sourceImagePointMm: { ...input.imagePointMm },
109
+ channels: {
110
+ red: {
111
+ mappedImagePointMm: { ...red.mappedImagePointMm },
112
+ radialScale: red.radialScale,
113
+ combinedCoefficients: { ...profiles.red.coefficients }
114
+ },
115
+ green: {
116
+ mappedImagePointMm: { ...green.mappedImagePointMm },
117
+ radialScale: green.radialScale,
118
+ combinedCoefficients: { ...profiles.green.coefficients }
119
+ },
120
+ blue: {
121
+ mappedImagePointMm: { ...blue.mappedImagePointMm },
122
+ radialScale: blue.radialScale,
123
+ combinedCoefficients: { ...profiles.blue.coefficients }
124
+ }
125
+ },
126
+ separation: separations(red.mappedImagePointMm, green.mappedImagePointMm, blue.mappedImagePointMm)
127
+ }, "generic-green-reference-lateral-chromatic-aberration", "1.0.0", assumptions());
128
+ }
129
+ /**
130
+ * Inverse-maps one distorted output destination independently for the
131
+ * representative red/green/blue channels.
132
+ *
133
+ * Renderers can use the returned per-channel ideal source coordinates for
134
+ * inverse sampling without inventing chromatic-aberration equations.
135
+ */
136
+ export function calculateInverseLateralChromaticAberrationMapping(input) {
137
+ const profiles = resolveProfiles(input.profile);
138
+ const red = withChannelContext("red", () => calculateInverseRadialDistortionMapping({
139
+ distortedImagePointMm: input.distortedImagePointMm,
140
+ profile: profiles.red
141
+ })).value;
142
+ const green = withChannelContext("green", () => calculateInverseRadialDistortionMapping({
143
+ distortedImagePointMm: input.distortedImagePointMm,
144
+ profile: profiles.green
145
+ })).value;
146
+ const blue = withChannelContext("blue", () => calculateInverseRadialDistortionMapping({
147
+ distortedImagePointMm: input.distortedImagePointMm,
148
+ profile: profiles.blue
149
+ })).value;
150
+ return approximationResult({
151
+ direction: "distorted-output-to-channel-sources",
152
+ referenceChannel: "green",
153
+ distortedImagePointMm: { ...input.distortedImagePointMm },
154
+ channels: {
155
+ red: {
156
+ sourceImagePointMm: { ...red.sourceImagePointMm },
157
+ radialScaleAtSource: red.radialScaleAtSource,
158
+ combinedCoefficients: { ...profiles.red.coefficients }
159
+ },
160
+ green: {
161
+ sourceImagePointMm: { ...green.sourceImagePointMm },
162
+ radialScaleAtSource: green.radialScaleAtSource,
163
+ combinedCoefficients: { ...profiles.green.coefficients }
164
+ },
165
+ blue: {
166
+ sourceImagePointMm: { ...blue.sourceImagePointMm },
167
+ radialScaleAtSource: blue.radialScaleAtSource,
168
+ combinedCoefficients: { ...profiles.blue.coefficients }
169
+ }
170
+ },
171
+ sourceSeparation: separations(red.sourceImagePointMm, green.sourceImagePointMm, blue.sourceImagePointMm)
172
+ }, "generic-inverse-green-reference-lateral-chromatic-aberration", "1.0.0", assumptions());
173
+ }
174
+ //# sourceMappingURL=lateral-chromatic-aberration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lateral-chromatic-aberration.js","sourceRoot":"","sources":["../../src/optics/lateral-chromatic-aberration.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,102 @@
1
+ import { type CalculationProvenance, type CalculationResult } from "../core/calculation-result.js";
2
+ import type { LensFieldPointMm } from "./radial-distortion.js";
3
+ export declare const PSF_FOUNDATION_VERSION: "0.1.0";
4
+ export type PsfContributionId = "geometric-defocus-circle" | "circular-diffraction-first-zero" | "non-circular-diffraction" | "mechanical-pupil-clipping" | "field-curvature" | "field-dependent-aberration" | "field-dependent-bokeh";
5
+ export type PsfContributionStatus = "implemented-diagnostic" | "reserved-contract";
6
+ export interface PsfContributionContract {
7
+ id: PsfContributionId;
8
+ status: PsfContributionStatus;
9
+ dependsOn: readonly ("field-position" | "focus-depth" | "pupil" | "wavelength")[];
10
+ note: string;
11
+ }
12
+ export interface PsfFoundationContract {
13
+ version: typeof PSF_FOUNDATION_VERSION;
14
+ coordinateSpace: "image-plane-metric";
15
+ fieldAxes: "+X right, +Y up";
16
+ compositionPolicy: "separate-contributions-no-combined-psf";
17
+ contributions: readonly PsfContributionContract[];
18
+ previewReferencePolicy: {
19
+ previewMayApproximate: true;
20
+ referenceMayUseHigherFidelity: true;
21
+ mustPreserveContributionSemantics: true;
22
+ };
23
+ notes: readonly string[];
24
+ }
25
+ export interface CalculatePsfFoundationComponentsInput {
26
+ focalLengthMm: number;
27
+ aperture: number;
28
+ focusDistanceM: number;
29
+ subjectDistanceM: number;
30
+ /**
31
+ * Image-plane field position relative to the optical axis, in millimetres.
32
+ * +X is right and +Y is up.
33
+ */
34
+ fieldPointMm: LensFieldPointMm;
35
+ /**
36
+ * Physical image-plane radius used only to normalize field position for
37
+ * cross-case diagnostics. It is not a lens calibration or validity bound.
38
+ */
39
+ fieldNormalizationRadiusMm: number;
40
+ spectralBasis: {
41
+ kind: "monochromatic";
42
+ wavelengthNm: number;
43
+ };
44
+ }
45
+ export interface PsfFoundationComponents {
46
+ context: {
47
+ field: {
48
+ imagePointMm: LensFieldPointMm;
49
+ radiusMm: number;
50
+ normalizationRadiusMm: number;
51
+ normalizedRadius: number;
52
+ };
53
+ depth: {
54
+ focusDistanceM: number;
55
+ subjectDistanceM: number;
56
+ };
57
+ spectralBasis: {
58
+ kind: "monochromatic";
59
+ wavelengthNm: number;
60
+ };
61
+ pupil: {
62
+ kind: "ideal-circular-f-number-derived";
63
+ apertureFNumber: number;
64
+ diameterMm: number;
65
+ };
66
+ };
67
+ contributions: {
68
+ geometricDefocus: {
69
+ id: "geometric-defocus-circle";
70
+ diameterMm: number;
71
+ provenance: CalculationProvenance;
72
+ };
73
+ circularDiffraction: {
74
+ id: "circular-diffraction-first-zero";
75
+ firstZeroDiameterMicrometers: number;
76
+ provenance: CalculationProvenance;
77
+ };
78
+ };
79
+ composition: {
80
+ status: "not-composed";
81
+ note: string;
82
+ };
83
+ }
84
+ /**
85
+ * Returns the public PSF/pupil foundation contract.
86
+ *
87
+ * The contract describes current diagnostics plus reserved future contribution
88
+ * ownership. It does not imply that reserved contributions are implemented.
89
+ */
90
+ export declare function getPsfFoundationContract(): PsfFoundationContract;
91
+ /**
92
+ * Evaluates the currently implemented PSF-related diagnostics in one explicit
93
+ * field/depth/spectral/pupil context without combining them into a synthetic
94
+ * PSF or blur radius.
95
+ *
96
+ * Field position is recorded even though the current defocus-circle and
97
+ * circular-Airy diagnostics are not field dependent. Future field-dependent
98
+ * contributions can consume the same context without changing these existing
99
+ * primitives.
100
+ */
101
+ export declare function calculatePsfFoundationComponents(input: CalculatePsfFoundationComponentsInput): CalculationResult<PsfFoundationComponents>;
102
+ //# sourceMappingURL=psf-foundation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"psf-foundation.d.ts","sourceRoot":"","sources":["../../src/optics/psf-foundation.ts"],"names":[],"mappings":"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"}