@photivra/engine 0.2.0 → 0.5.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 (89) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/CITATION.cff +1 -1
  3. package/README.md +59 -10
  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/index.d.ts +15 -3
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +15 -3
  30. package/dist/index.js.map +1 -1
  31. package/dist/motion/camera-rotation.d.ts +91 -0
  32. package/dist/motion/camera-rotation.d.ts.map +1 -0
  33. package/dist/motion/camera-rotation.js +157 -0
  34. package/dist/motion/camera-rotation.js.map +1 -0
  35. package/dist/optics/focus-breathing.d.ts +73 -0
  36. package/dist/optics/focus-breathing.d.ts.map +1 -0
  37. package/dist/optics/focus-breathing.js +77 -0
  38. package/dist/optics/focus-breathing.js.map +1 -0
  39. package/dist/optics/illumination-vignetting.d.ts +59 -0
  40. package/dist/optics/illumination-vignetting.d.ts.map +1 -0
  41. package/dist/optics/illumination-vignetting.js +107 -0
  42. package/dist/optics/illumination-vignetting.js.map +1 -0
  43. package/dist/optics/lateral-chromatic-aberration.d.ts +117 -0
  44. package/dist/optics/lateral-chromatic-aberration.d.ts.map +1 -0
  45. package/dist/optics/lateral-chromatic-aberration.js +228 -0
  46. package/dist/optics/lateral-chromatic-aberration.js.map +1 -0
  47. package/dist/optics/psf-foundation.d.ts +102 -0
  48. package/dist/optics/psf-foundation.d.ts.map +1 -0
  49. package/dist/optics/psf-foundation.js +164 -0
  50. package/dist/optics/psf-foundation.js.map +1 -0
  51. package/dist/optics/radial-distortion.d.ts +131 -0
  52. package/dist/optics/radial-distortion.d.ts.map +1 -0
  53. package/dist/optics/radial-distortion.js +227 -0
  54. package/dist/optics/radial-distortion.js.map +1 -0
  55. package/dist/output/capture-geometry.d.ts +278 -0
  56. package/dist/output/capture-geometry.d.ts.map +1 -0
  57. package/dist/output/capture-geometry.js +562 -0
  58. package/dist/output/capture-geometry.js.map +1 -0
  59. package/dist/schema/validation.d.ts +1 -8
  60. package/dist/schema/validation.d.ts.map +1 -1
  61. package/dist/schema/validation.js +2 -11
  62. package/dist/schema/validation.js.map +1 -1
  63. package/dist/sensor/architecture.d.ts +40 -0
  64. package/dist/sensor/architecture.d.ts.map +1 -0
  65. package/dist/sensor/architecture.js +98 -0
  66. package/dist/sensor/architecture.js.map +1 -0
  67. package/dist/sensor/radiometry-readiness.d.ts +111 -0
  68. package/dist/sensor/radiometry-readiness.d.ts.map +1 -0
  69. package/dist/sensor/radiometry-readiness.js +294 -0
  70. package/dist/sensor/radiometry-readiness.js.map +1 -0
  71. package/dist/sensor/sensor-geometry.d.ts +99 -0
  72. package/dist/sensor/sensor-geometry.d.ts.map +1 -0
  73. package/dist/sensor/sensor-geometry.js +71 -0
  74. package/dist/sensor/sensor-geometry.js.map +1 -0
  75. package/dist/simulation/poc-simulation.d.ts +93 -1
  76. package/dist/simulation/poc-simulation.d.ts.map +1 -1
  77. package/dist/simulation/poc-simulation.js +308 -24
  78. package/dist/simulation/poc-simulation.js.map +1 -1
  79. package/docs/API_STYLE.md +36 -2
  80. package/docs/ARCHITECTURE.md +179 -1
  81. package/docs/IMAGE_FORMATION.md +151 -0
  82. package/docs/MOTION_AND_SIGNAL.md +52 -1
  83. package/docs/PHYSICS_FOUNDATION.md +190 -3
  84. package/docs/POC_API.md +73 -7
  85. package/docs/PROVENANCE.md +24 -0
  86. package/docs/PSF_FOUNDATION.md +133 -0
  87. package/docs/STABILIZATION.md +17 -4
  88. package/docs/USAGE.md +714 -3
  89. package/package.json +3 -2
@@ -0,0 +1,131 @@
1
+ import { type CalculationResult } from "../core/calculation-result.js";
2
+ export interface RadialDistortionCoefficients {
3
+ /** Dimensionless r² coefficient. */
4
+ k1: number;
5
+ /** Dimensionless r⁴ coefficient. */
6
+ k2: number;
7
+ /** Dimensionless r⁶ coefficient. */
8
+ k3: number;
9
+ }
10
+ export interface RadialDistortionProfile {
11
+ /**
12
+ * Physical image-plane radius used to normalize field position, in mm.
13
+ * Coefficients are meaningful only with this declared normalization.
14
+ */
15
+ normalizationRadiusMm: number;
16
+ /**
17
+ * Maximum undistorted normalized radius over which this profile is declared
18
+ * valid and must remain one-to-one/invertible.
19
+ */
20
+ maximumNormalizedRadius: number;
21
+ coefficients: RadialDistortionCoefficients;
22
+ }
23
+ export interface LensFieldPointMm {
24
+ /** Image-plane X coordinate relative to the optical axis, in mm. */
25
+ x: number;
26
+ /** Image-plane Y coordinate relative to the optical axis, in mm. */
27
+ y: number;
28
+ }
29
+ export interface CalculateRadialDistortionMappingInput {
30
+ /** Ideal/undistorted image-plane point. */
31
+ imagePointMm: LensFieldPointMm;
32
+ profile: RadialDistortionProfile;
33
+ }
34
+ export interface CalculateInverseRadialDistortionMappingInput {
35
+ /** Distorted image-plane point to inverse-map back to ideal coordinates. */
36
+ distortedImagePointMm: LensFieldPointMm;
37
+ profile: RadialDistortionProfile;
38
+ }
39
+ export interface CalculateInverseRadialDistortionMappingsInput {
40
+ /** Distorted image-plane destinations to inverse-map in one validated batch. */
41
+ distortedImagePointsMm: readonly LensFieldPointMm[];
42
+ profile: RadialDistortionProfile;
43
+ }
44
+ export interface RadialDistortionMapping {
45
+ direction: "undistorted-to-distorted";
46
+ sourceImagePointMm: LensFieldPointMm;
47
+ mappedImagePointMm: LensFieldPointMm;
48
+ sourceNormalizedRadius: number;
49
+ mappedNormalizedRadius: number;
50
+ radialScale: number;
51
+ deltaMm: {
52
+ x: number;
53
+ y: number;
54
+ distance: number;
55
+ };
56
+ profileMinimumRadialDerivative: number;
57
+ }
58
+ export interface InverseRadialDistortionMapping {
59
+ direction: "distorted-to-undistorted";
60
+ distortedImagePointMm: LensFieldPointMm;
61
+ sourceImagePointMm: LensFieldPointMm;
62
+ distortedNormalizedRadius: number;
63
+ sourceNormalizedRadius: number;
64
+ radialScaleAtSource: number;
65
+ deltaMm: {
66
+ x: number;
67
+ y: number;
68
+ distance: number;
69
+ };
70
+ profileMinimumRadialDerivative: number;
71
+ }
72
+ export interface InverseRadialDistortionMappings {
73
+ direction: "distorted-to-undistorted-batch";
74
+ mappings: readonly InverseRadialDistortionMapping[];
75
+ pointCount: number;
76
+ profileMinimumRadialDerivative: number;
77
+ }
78
+ /** @internal Shared by composite lens-field batch evaluators; not root-exported. */
79
+ export interface ValidatedRadialProfile {
80
+ normalizationRadiusMm: number;
81
+ maximumNormalizedRadius: number;
82
+ coefficients: RadialDistortionCoefficients;
83
+ minimumRadialDerivative: number;
84
+ maximumMappedNormalizedRadius: number;
85
+ }
86
+ /** @internal Validates/copies one profile for repeated in-package evaluation. */
87
+ export declare function validateRadialDistortionProfile(profile: RadialDistortionProfile): ValidatedRadialProfile;
88
+ /**
89
+ * Applies a generic radial lens-distortion field mapping to one ideal
90
+ * image-plane point.
91
+ *
92
+ * The mapping is rotationally symmetric about the optical axis:
93
+ *
94
+ * p_distorted = p_ideal * (1 + k1 r² + k2 r⁴ + k3 r⁶)
95
+ *
96
+ * where r is normalized by the caller-declared physical reference radius.
97
+ *
98
+ * The profile is accepted only when the radial mapping is strictly monotonic
99
+ * over its declared operating radius, which makes inverse destination-to-source
100
+ * sampling well-defined.
101
+ */
102
+ export declare function calculateRadialDistortionMapping(input: CalculateRadialDistortionMappingInput): CalculationResult<RadialDistortionMapping>;
103
+ /**
104
+ * Inverse-maps one distorted image-plane point to its ideal/undistorted source
105
+ * coordinate for destination-to-source renderer sampling.
106
+ *
107
+ * The same profile monotonicity requirement used by the forward mapping makes
108
+ * the inverse unique over the declared operating radius. A deterministic fixed
109
+ * bisection count is used rather than an unconstrained iterative solver.
110
+ */
111
+ export interface InverseRadialSourcePointValue {
112
+ sourceImagePointMm: LensFieldPointMm;
113
+ distortedNormalizedRadius: number;
114
+ sourceNormalizedRadius: number;
115
+ radialScaleAtSource: number;
116
+ }
117
+ /**
118
+ * @internal Minimal inverse evaluation used by composite in-package batch
119
+ * models that do not need the standalone radial delta/provenance envelope.
120
+ */
121
+ export declare function calculateInverseRadialSourcePointValue(distortedImagePointMm: LensFieldPointMm, profile: ValidatedRadialProfile, pointPath: string): InverseRadialSourcePointValue;
122
+ export declare function calculateInverseRadialDistortionMapping(input: CalculateInverseRadialDistortionMappingInput): CalculationResult<InverseRadialDistortionMapping>;
123
+ /**
124
+ * Inverse-maps multiple distorted image-plane destinations while validating
125
+ * and resolving the radial profile only once for the complete batch.
126
+ *
127
+ * The returned per-point mappings are numerically identical to the scalar API;
128
+ * provenance and profile validation are shared once at the batch boundary.
129
+ */
130
+ export declare function calculateInverseRadialDistortionMappings(input: CalculateInverseRadialDistortionMappingsInput): CalculationResult<InverseRadialDistortionMappings>;
131
+ //# sourceMappingURL=radial-distortion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"radial-distortion.d.ts","sourceRoot":"","sources":["../../src/optics/radial-distortion.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,227 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { approximationResult } from "../core/calculation-result.js";
3
+ import { InvalidScientificInputError, requirePositiveFinite } from "../core/validation.js";
4
+ const INVERSE_BISECTION_ITERATIONS = 80;
5
+ function requireFinite(name, value) {
6
+ if (!Number.isFinite(value)) {
7
+ throw new InvalidScientificInputError(`${name} must be finite.`);
8
+ }
9
+ }
10
+ function radialScale(normalizedRadius, coefficients) {
11
+ const r2 = normalizedRadius * normalizedRadius;
12
+ const r4 = r2 * r2;
13
+ const r6 = r4 * r2;
14
+ return (1 +
15
+ coefficients.k1 * r2 +
16
+ coefficients.k2 * r4 +
17
+ coefficients.k3 * r6);
18
+ }
19
+ function mappedNormalizedRadius(normalizedRadius, coefficients) {
20
+ return normalizedRadius * radialScale(normalizedRadius, coefficients);
21
+ }
22
+ function radialDerivativeFromSquaredRadius(squaredRadius, coefficients) {
23
+ return (1 +
24
+ 3 * coefficients.k1 * squaredRadius +
25
+ 5 * coefficients.k2 * squaredRadius * squaredRadius +
26
+ 7 *
27
+ coefficients.k3 *
28
+ squaredRadius *
29
+ squaredRadius *
30
+ squaredRadius);
31
+ }
32
+ function derivativeCriticalSquaredRadii(coefficients) {
33
+ // d/dr² of the radial derivative:
34
+ // 3 k1 + 10 k2 s + 21 k3 s² = 0, where s = r².
35
+ const a = 21 * coefficients.k3;
36
+ const b = 10 * coefficients.k2;
37
+ const c = 3 * coefficients.k1;
38
+ if (a === 0) {
39
+ return b === 0 ? [] : [-c / b];
40
+ }
41
+ const discriminant = b * b - 4 * a * c;
42
+ if (discriminant < 0) {
43
+ return [];
44
+ }
45
+ const root = Math.sqrt(discriminant);
46
+ return [(-b - root) / (2 * a), (-b + root) / (2 * a)];
47
+ }
48
+ /** @internal Validates/copies one profile for repeated in-package evaluation. */
49
+ export function validateRadialDistortionProfile(profile) {
50
+ requirePositiveFinite("profile.normalizationRadiusMm", profile.normalizationRadiusMm);
51
+ requirePositiveFinite("profile.maximumNormalizedRadius", profile.maximumNormalizedRadius);
52
+ requireFinite("profile.coefficients.k1", profile.coefficients.k1);
53
+ requireFinite("profile.coefficients.k2", profile.coefficients.k2);
54
+ requireFinite("profile.coefficients.k3", profile.coefficients.k3);
55
+ const maximumSquaredRadius = profile.maximumNormalizedRadius * profile.maximumNormalizedRadius;
56
+ const candidates = [
57
+ 0,
58
+ maximumSquaredRadius,
59
+ ...derivativeCriticalSquaredRadii(profile.coefficients).filter((value) => value > 0 && value < maximumSquaredRadius)
60
+ ];
61
+ const minimumRadialDerivative = Math.min(...candidates.map((squaredRadius) => radialDerivativeFromSquaredRadius(squaredRadius, profile.coefficients)));
62
+ if (!Number.isFinite(minimumRadialDerivative) ||
63
+ minimumRadialDerivative <= 0) {
64
+ throw new InvalidScientificInputError("Radial distortion profile must remain strictly monotonic over maximumNormalizedRadius so inverse mapping is single-valued.");
65
+ }
66
+ const maximumMappedRadius = mappedNormalizedRadius(profile.maximumNormalizedRadius, profile.coefficients);
67
+ if (!Number.isFinite(maximumMappedRadius) || maximumMappedRadius <= 0) {
68
+ throw new InvalidScientificInputError("Radial distortion profile produces an invalid mapped operating radius.");
69
+ }
70
+ return {
71
+ normalizationRadiusMm: profile.normalizationRadiusMm,
72
+ maximumNormalizedRadius: profile.maximumNormalizedRadius,
73
+ coefficients: { ...profile.coefficients },
74
+ minimumRadialDerivative,
75
+ maximumMappedNormalizedRadius: maximumMappedRadius
76
+ };
77
+ }
78
+ function requirePoint(name, point) {
79
+ requireFinite(`${name}.x`, point.x);
80
+ requireFinite(`${name}.y`, point.y);
81
+ }
82
+ function mappingAssumptions() {
83
+ return [
84
+ "Generic rotationally symmetric radial polynomial field mapping",
85
+ "Distortion is centered on the optical axis; decentering and tangential distortion are not modeled",
86
+ "Normalized radius is image-plane radius divided by the caller-declared normalizationRadiusMm",
87
+ "Radial scale is 1 + k1 r^2 + k2 r^4 + k3 r^6",
88
+ "The declared operating radius is required to remain strictly monotonic so inverse mapping is single-valued",
89
+ "Coefficients are generic caller inputs and do not represent a named lens unless separately calibrated with defensible provenance"
90
+ ];
91
+ }
92
+ /**
93
+ * Applies a generic radial lens-distortion field mapping to one ideal
94
+ * image-plane point.
95
+ *
96
+ * The mapping is rotationally symmetric about the optical axis:
97
+ *
98
+ * p_distorted = p_ideal * (1 + k1 r² + k2 r⁴ + k3 r⁶)
99
+ *
100
+ * where r is normalized by the caller-declared physical reference radius.
101
+ *
102
+ * The profile is accepted only when the radial mapping is strictly monotonic
103
+ * over its declared operating radius, which makes inverse destination-to-source
104
+ * sampling well-defined.
105
+ */
106
+ export function calculateRadialDistortionMapping(input) {
107
+ requirePoint("imagePointMm", input.imagePointMm);
108
+ const profile = validateRadialDistortionProfile(input.profile);
109
+ const sourceRadiusMm = Math.hypot(input.imagePointMm.x, input.imagePointMm.y);
110
+ const sourceNormalizedRadius = sourceRadiusMm / profile.normalizationRadiusMm;
111
+ if (sourceNormalizedRadius > profile.maximumNormalizedRadius) {
112
+ throw new InvalidScientificInputError("imagePointMm lies outside the radial distortion profile maximumNormalizedRadius.");
113
+ }
114
+ const scale = radialScale(sourceNormalizedRadius, profile.coefficients);
115
+ const mappedImagePointMm = {
116
+ x: input.imagePointMm.x * scale,
117
+ y: input.imagePointMm.y * scale
118
+ };
119
+ const mappedRadius = sourceNormalizedRadius * scale;
120
+ const deltaX = mappedImagePointMm.x - input.imagePointMm.x;
121
+ const deltaY = mappedImagePointMm.y - input.imagePointMm.y;
122
+ return approximationResult({
123
+ direction: "undistorted-to-distorted",
124
+ sourceImagePointMm: { ...input.imagePointMm },
125
+ mappedImagePointMm,
126
+ sourceNormalizedRadius,
127
+ mappedNormalizedRadius: mappedRadius,
128
+ radialScale: scale,
129
+ deltaMm: {
130
+ x: deltaX,
131
+ y: deltaY,
132
+ distance: Math.hypot(deltaX, deltaY)
133
+ },
134
+ profileMinimumRadialDerivative: profile.minimumRadialDerivative
135
+ }, "generic-radial-lens-distortion", "1.0.0", mappingAssumptions());
136
+ }
137
+ /**
138
+ * @internal Minimal inverse evaluation used by composite in-package batch
139
+ * models that do not need the standalone radial delta/provenance envelope.
140
+ */
141
+ export function calculateInverseRadialSourcePointValue(distortedImagePointMm, profile, pointPath) {
142
+ requirePoint(pointPath, distortedImagePointMm);
143
+ const distortedRadiusMm = Math.hypot(distortedImagePointMm.x, distortedImagePointMm.y);
144
+ const distortedNormalizedRadius = distortedRadiusMm / profile.normalizationRadiusMm;
145
+ if (distortedNormalizedRadius >
146
+ profile.maximumMappedNormalizedRadius) {
147
+ throw new InvalidScientificInputError(`${pointPath} lies outside the mapped radial distortion profile operating radius.`);
148
+ }
149
+ let sourceNormalizedRadius = 0;
150
+ if (distortedNormalizedRadius > 0) {
151
+ let lower = 0;
152
+ let upper = profile.maximumNormalizedRadius;
153
+ for (let iteration = 0; iteration < INVERSE_BISECTION_ITERATIONS; iteration += 1) {
154
+ const midpoint = (lower + upper) / 2;
155
+ const mapped = mappedNormalizedRadius(midpoint, profile.coefficients);
156
+ if (mapped < distortedNormalizedRadius) {
157
+ lower = midpoint;
158
+ }
159
+ else {
160
+ upper = midpoint;
161
+ }
162
+ }
163
+ sourceNormalizedRadius = (lower + upper) / 2;
164
+ }
165
+ const radialScaleAtSource = radialScale(sourceNormalizedRadius, profile.coefficients);
166
+ const sourceImagePointMm = distortedNormalizedRadius === 0
167
+ ? { x: 0, y: 0 }
168
+ : {
169
+ x: distortedImagePointMm.x / radialScaleAtSource,
170
+ y: distortedImagePointMm.y / radialScaleAtSource
171
+ };
172
+ return {
173
+ sourceImagePointMm,
174
+ distortedNormalizedRadius,
175
+ sourceNormalizedRadius,
176
+ radialScaleAtSource
177
+ };
178
+ }
179
+ function calculateInverseRadialDistortionMappingValue(distortedImagePointMm, profile, pointPath) {
180
+ const source = calculateInverseRadialSourcePointValue(distortedImagePointMm, profile, pointPath);
181
+ const deltaX = source.sourceImagePointMm.x - distortedImagePointMm.x;
182
+ const deltaY = source.sourceImagePointMm.y - distortedImagePointMm.y;
183
+ return {
184
+ direction: "distorted-to-undistorted",
185
+ distortedImagePointMm: { ...distortedImagePointMm },
186
+ sourceImagePointMm: source.sourceImagePointMm,
187
+ distortedNormalizedRadius: source.distortedNormalizedRadius,
188
+ sourceNormalizedRadius: source.sourceNormalizedRadius,
189
+ radialScaleAtSource: source.radialScaleAtSource,
190
+ deltaMm: {
191
+ x: deltaX,
192
+ y: deltaY,
193
+ distance: Math.hypot(deltaX, deltaY)
194
+ },
195
+ profileMinimumRadialDerivative: profile.minimumRadialDerivative
196
+ };
197
+ }
198
+ export function calculateInverseRadialDistortionMapping(input) {
199
+ const profile = validateRadialDistortionProfile(input.profile);
200
+ const mapping = calculateInverseRadialDistortionMappingValue(input.distortedImagePointMm, profile, "distortedImagePointMm");
201
+ return approximationResult(mapping, "generic-inverse-radial-lens-distortion", "1.0.0", [
202
+ ...mappingAssumptions(),
203
+ `Inverse radius is solved with ${INVERSE_BISECTION_ITERATIONS} deterministic bisection iterations`
204
+ ]);
205
+ }
206
+ /**
207
+ * Inverse-maps multiple distorted image-plane destinations while validating
208
+ * and resolving the radial profile only once for the complete batch.
209
+ *
210
+ * The returned per-point mappings are numerically identical to the scalar API;
211
+ * provenance and profile validation are shared once at the batch boundary.
212
+ */
213
+ export function calculateInverseRadialDistortionMappings(input) {
214
+ const profile = validateRadialDistortionProfile(input.profile);
215
+ const mappings = input.distortedImagePointsMm.map((point, index) => calculateInverseRadialDistortionMappingValue(point, profile, `distortedImagePointsMm[${index}]`));
216
+ return approximationResult({
217
+ direction: "distorted-to-undistorted-batch",
218
+ mappings,
219
+ pointCount: mappings.length,
220
+ profileMinimumRadialDerivative: profile.minimumRadialDerivative
221
+ }, "generic-inverse-radial-lens-distortion-batch", "1.0.0", [
222
+ ...mappingAssumptions(),
223
+ `Inverse radius is solved with ${INVERSE_BISECTION_ITERATIONS} deterministic bisection iterations per point`,
224
+ "The radial profile is validated and its invariant extrema are resolved once for the batch"
225
+ ]);
226
+ }
227
+ //# sourceMappingURL=radial-distortion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"radial-distortion.js","sourceRoot":"","sources":["../../src/optics/radial-distortion.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,278 @@
1
+ import { type CalculationProvenance, type CalculationResult } from "../core/calculation-result.js";
2
+ import { type NativeImageRaster, type RasterDimensions, type SensorImagingArea } from "../sensor/sensor-geometry.js";
3
+ export type CaptureOrientation = "landscape" | "portrait-clockwise" | "landscape-inverted" | "portrait-counter-clockwise";
4
+ export interface RasterPoint {
5
+ x: number;
6
+ y: number;
7
+ }
8
+ export interface RasterVector {
9
+ x: number;
10
+ y: number;
11
+ }
12
+ /**
13
+ * Integer half-open rectangle in raster coordinates.
14
+ *
15
+ * Coordinates use a top-left origin with +X to the right and +Y downward.
16
+ * The covered extent is [x, x + width) × [y, y + height).
17
+ */
18
+ export interface RasterRect {
19
+ x: number;
20
+ y: number;
21
+ width: number;
22
+ height: number;
23
+ }
24
+ export interface PhysicalBoundsFromOpticalAxisMm {
25
+ left: number;
26
+ right: number;
27
+ top: number;
28
+ bottom: number;
29
+ }
30
+ export interface NormalizedRasterUv {
31
+ /** 0 at the left edge, 1 at the right edge. */
32
+ u: number;
33
+ /** 0 at the top edge, 1 at the bottom edge. */
34
+ v: number;
35
+ }
36
+ /**
37
+ * Pre-orientation optical image-plane coordinate in millimetres.
38
+ *
39
+ * Origin is the optical axis; +X points right and +Y points up.
40
+ */
41
+ export interface ImagePlaneMetricPointMm {
42
+ x: number;
43
+ y: number;
44
+ }
45
+ export interface MapOrientedPhysicalUvToImagePlaneInput {
46
+ uv: NormalizedRasterUv;
47
+ /**
48
+ * Physical region represented by the normalized raster, after physical
49
+ * camera orientation, using the capture contract's +Y-down physical basis.
50
+ */
51
+ orientedPhysicalBoundsFromOpticalAxisMm: PhysicalBoundsFromOpticalAxisMm;
52
+ orientation: CaptureOrientation;
53
+ }
54
+ export interface MapImagePlanePointToOrientedPhysicalUvInput {
55
+ imagePlanePointMm: ImagePlaneMetricPointMm;
56
+ /**
57
+ * Physical region represented by the normalized raster, after physical
58
+ * camera orientation, using the capture contract's +Y-down physical basis.
59
+ */
60
+ orientedPhysicalBoundsFromOpticalAxisMm: PhysicalBoundsFromOpticalAxisMm;
61
+ orientation: CaptureOrientation;
62
+ }
63
+ export interface ResolveCaptureGeometryInput {
64
+ imagingArea: SensorImagingArea;
65
+ nativeRaster: NativeImageRaster;
66
+ orientation: CaptureOrientation;
67
+ /**
68
+ * Active capture rectangle in native sensor raster coordinates.
69
+ *
70
+ * Defaults to the full native raster.
71
+ */
72
+ activeCaptureRect?: RasterRect;
73
+ /**
74
+ * Final digital/output crop in oriented active-capture raster coordinates.
75
+ *
76
+ * Defaults to the full oriented active capture.
77
+ */
78
+ outputCropRect?: RasterRect;
79
+ /**
80
+ * Final output raster after optional digital crop/resampling.
81
+ *
82
+ * Defaults to the output-crop dimensions. The output raster must preserve
83
+ * the crop aspect ratio within a small integer-rounding tolerance.
84
+ */
85
+ outputRaster?: RasterDimensions;
86
+ }
87
+ export interface ResolvedCaptureGeometry {
88
+ native: {
89
+ imagingArea: SensorImagingArea;
90
+ raster: NativeImageRaster;
91
+ };
92
+ activeCapture: {
93
+ nativeRect: RasterRect;
94
+ imagingArea: SensorImagingArea;
95
+ /**
96
+ * Top-left active-area offset from the native imaging-area top-left.
97
+ */
98
+ physicalOffsetMm: {
99
+ x: number;
100
+ y: number;
101
+ };
102
+ /** Active-area center relative to the native optical axis. */
103
+ centerOffsetFromOpticalAxisMm: RasterVector;
104
+ /** Native physical bounds relative to the optical axis. */
105
+ physicalBoundsFromOpticalAxisMm: PhysicalBoundsFromOpticalAxisMm;
106
+ /**
107
+ * Current generic model assumes native image samples uniformly span the
108
+ * declared physical imaging area.
109
+ */
110
+ imagingAreaDerivation: "uniform-native-raster";
111
+ raster: RasterDimensions;
112
+ };
113
+ orientedCapture: {
114
+ orientation: CaptureOrientation;
115
+ imagingArea: SensorImagingArea;
116
+ /** Oriented physical bounds relative to the optical axis. */
117
+ physicalBoundsFromOpticalAxisMm: PhysicalBoundsFromOpticalAxisMm;
118
+ raster: RasterDimensions;
119
+ };
120
+ output: {
121
+ /**
122
+ * Digital/output crop expressed in oriented active-capture coordinates.
123
+ *
124
+ * This crop does not mutate physical sensor identity or active-capture
125
+ * geometry.
126
+ */
127
+ cropRect: RasterRect;
128
+ raster: RasterDimensions;
129
+ /** Physical region retained by the digital/output crop. */
130
+ imagingArea: SensorImagingArea;
131
+ physicalBoundsFromOpticalAxisMm: PhysicalBoundsFromOpticalAxisMm;
132
+ centerOffsetFromOpticalAxisMm: RasterVector;
133
+ sourceRetainedAreaFraction: number;
134
+ /** Pixel-domain resampling from oriented active capture into output. */
135
+ orientedCaptureToOutputScale: {
136
+ x: number;
137
+ y: number;
138
+ axisRelativeDifference: number;
139
+ };
140
+ };
141
+ }
142
+ export interface TransformRasterPointInput {
143
+ point: RasterPoint;
144
+ nativeRaster: NativeImageRaster;
145
+ orientation: CaptureOrientation;
146
+ }
147
+ export interface TransformRasterVectorInput {
148
+ vector: RasterVector;
149
+ orientation: CaptureOrientation;
150
+ }
151
+ export interface TransformRasterRectInput {
152
+ rect: RasterRect;
153
+ nativeRaster: NativeImageRaster;
154
+ orientation: CaptureOrientation;
155
+ }
156
+ export interface CalculateActiveCaptureFieldOfViewInput {
157
+ imagingArea: SensorImagingArea;
158
+ nativeRaster: NativeImageRaster;
159
+ orientation: CaptureOrientation;
160
+ activeCaptureRect?: RasterRect;
161
+ focalLengthMm: number;
162
+ focusDistanceM?: number;
163
+ }
164
+ export interface CalculateOutputFieldOfViewInput {
165
+ imagingArea: SensorImagingArea;
166
+ nativeRaster: NativeImageRaster;
167
+ orientation: CaptureOrientation;
168
+ activeCaptureRect?: RasterRect;
169
+ outputCropRect?: RasterRect;
170
+ focalLengthMm: number;
171
+ focusDistanceM?: number;
172
+ }
173
+ export interface ActiveCaptureFieldOfView {
174
+ orientation: CaptureOrientation;
175
+ horizontalDegrees: number;
176
+ verticalDegrees: number;
177
+ /**
178
+ * Larger of the two opposite-corner diagonal angular spans. For centered
179
+ * crops both diagonal spans are equal.
180
+ */
181
+ diagonalDegrees: number;
182
+ horizontalBoundsDegrees: {
183
+ minimum: number;
184
+ maximum: number;
185
+ };
186
+ verticalBoundsDegrees: {
187
+ minimum: number;
188
+ maximum: number;
189
+ };
190
+ diagonalDegreesByCornerPair: {
191
+ topLeftToBottomRight: number;
192
+ topRightToBottomLeft: number;
193
+ };
194
+ activeImagingArea: SensorImagingArea;
195
+ centerOffsetFromOpticalAxisMm: RasterVector;
196
+ projection: {
197
+ projectionDistanceMm: number;
198
+ provenance: CalculationProvenance;
199
+ };
200
+ }
201
+ export interface OutputFieldOfView {
202
+ orientation: CaptureOrientation;
203
+ horizontalDegrees: number;
204
+ verticalDegrees: number;
205
+ diagonalDegrees: number;
206
+ horizontalBoundsDegrees: {
207
+ minimum: number;
208
+ maximum: number;
209
+ };
210
+ verticalBoundsDegrees: {
211
+ minimum: number;
212
+ maximum: number;
213
+ };
214
+ diagonalDegreesByCornerPair: {
215
+ topLeftToBottomRight: number;
216
+ topRightToBottomLeft: number;
217
+ };
218
+ outputImagingArea: SensorImagingArea;
219
+ centerOffsetFromOpticalAxisMm: RasterVector;
220
+ projection: {
221
+ projectionDistanceMm: number;
222
+ provenance: CalculationProvenance;
223
+ };
224
+ }
225
+ /**
226
+ * Transforms a continuous native raster edge-coordinate point into oriented
227
+ * capture coordinates. Pixel centers can be represented with +0.5 offsets.
228
+ */
229
+ export declare function transformNativeRasterPointToOriented(input: TransformRasterPointInput): RasterPoint;
230
+ /** Transforms an oriented raster edge-coordinate point back to native coordinates. */
231
+ export declare function transformOrientedRasterPointToNative(input: TransformRasterPointInput): RasterPoint;
232
+ /** Rotates a native raster vector into oriented coordinates. */
233
+ export declare function transformNativeRasterVectorToOriented(input: TransformRasterVectorInput): RasterVector;
234
+ /** Rotates an oriented raster vector back into native coordinates. */
235
+ export declare function transformOrientedRasterVectorToNative(input: TransformRasterVectorInput): RasterVector;
236
+ /**
237
+ * Maps normalized coordinates in an oriented physical raster region into the
238
+ * pre-orientation optical image plane used by lens-field and camera-mapping
239
+ * APIs.
240
+ *
241
+ * The supplied physical bounds use the capture contract's +Y-down basis after
242
+ * physical camera orientation. The returned image-plane point uses optical-axis
243
+ * origin, +X right and +Y up. No lens or projection equation is applied.
244
+ */
245
+ export declare function mapOrientedPhysicalUvToImagePlanePoint(input: MapOrientedPhysicalUvToImagePlaneInput): ImagePlaneMetricPointMm;
246
+ /**
247
+ * Maps a pre-orientation optical image-plane point into normalized coordinates
248
+ * of an oriented physical raster region.
249
+ *
250
+ * This is the exact coordinate inverse of
251
+ * mapOrientedPhysicalUvToImagePlanePoint(). Points outside the supplied region
252
+ * fail closed; a tiny tolerance only absorbs floating-point round-trip noise at
253
+ * an edge.
254
+ */
255
+ export declare function mapImagePlanePointToOrientedPhysicalUv(input: MapImagePlanePointToOrientedPhysicalUvInput): NormalizedRasterUv;
256
+ /** Transforms an integer half-open native raster rectangle into oriented coordinates. */
257
+ export declare function transformNativeRasterRectToOriented(input: TransformRasterRectInput): RasterRect;
258
+ /** Transforms an oriented integer half-open rectangle back to native coordinates. */
259
+ export declare function transformOrientedRasterRectToNative(input: TransformRasterRectInput): RasterRect;
260
+ /**
261
+ * Resolves native sensor geometry, active capture, physical camera orientation,
262
+ * and final digital/output geometry without conflating their identities.
263
+ */
264
+ export declare function resolveCaptureGeometry(input: ResolveCaptureGeometryInput): CalculationResult<ResolvedCaptureGeometry>;
265
+ /**
266
+ * Calculates asymmetric horizontal/vertical FOV plus opposite-corner diagonal
267
+ * spans for the active physical capture area after physical camera orientation.
268
+ *
269
+ * Final digital/output crop is intentionally excluded.
270
+ */
271
+ export declare function calculateActiveCaptureFieldOfView(input: CalculateActiveCaptureFieldOfViewInput): CalculationResult<ActiveCaptureFieldOfView>;
272
+ /**
273
+ * Calculates final visible field of view after physical active capture,
274
+ * orientation, and digital/output crop. Output raster resolution does not
275
+ * affect this optical/framing result.
276
+ */
277
+ export declare function calculateOutputFieldOfView(input: CalculateOutputFieldOfViewInput): CalculationResult<OutputFieldOfView>;
278
+ //# sourceMappingURL=capture-geometry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture-geometry.d.ts","sourceRoot":"","sources":["../../src/output/capture-geometry.ts"],"names":[],"mappings":"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"}