@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
@@ -14,14 +14,71 @@ where `d` is the relevant sensor dimension and `v` is the projection/image-plane
14
14
 
15
15
  When no focus distance is supplied, `v = f` is the infinity-focus/pinhole compatibility approximation. When focus distance is supplied, `v` is the ideal Gaussian thin-lens image distance.
16
16
 
17
- ### Pixel pitch and crop
17
+ For a centered sensor interval, the usual symmetric form is sufficient. For an off-center sensor interval, Photivra uses signed sensor-plane bounds relative to the optical axis:
18
18
 
19
- Pixel pitch is derived directly from active sensor width and horizontal active pixel count.
19
+ `FOV = atan(x_max / v) - atan(x_min / v)`
20
20
 
21
- Centered crop dimensions are derived from a linear crop factor while preserving the source aspect ratio, subject to integer pixel flooring.
21
+ This preserves asymmetric left/right or top/bottom angular limits instead of pretending every active crop is centered.
22
+
23
+ ### Sensor geometry, sampling, capture, and crop
24
+
25
+ Photivra separates physical image-formation geometry from digital sampling.
26
+
27
+ `SensorImagingArea` represents the physical photosensitive area used for image formation. It is not a sensor package/die dimension.
28
+
29
+ `NativeImageRaster` represents the effective native image-sampling grid. It does not imply that one output/image sample corresponds one-to-one with one physical photodiode. Generic active/output rasters use `RasterDimensions`.
30
+
31
+ Geometric sample pitch is derived independently on each axis:
32
+
33
+ - `pitchX = imagingWidth / nativePixelWidth`;
34
+ - `pitchY = imagingHeight / nativePixelHeight`.
35
+
36
+ These pitches are geometric sample spacing only. They are not fill factor, effective collection area, or photon-collection area.
37
+
38
+ The capture pipeline is staged:
39
+
40
+ ```text
41
+ physical imaging area + native raster
42
+ ↓
43
+ native active-capture rectangle
44
+ ↓
45
+ physical camera orientation
46
+ ↓
47
+ oriented active capture
48
+ ↓
49
+ digital/output crop
50
+ ↓
51
+ output raster
52
+ ```
53
+
54
+ Native raster coordinates use a top-left origin with +X right and +Y down. Integer rectangles are half-open. Physical camera rotation does not redefine the native coordinate system; Photivra transforms points, vectors, and rectangles explicitly between native and oriented coordinates.
55
+
56
+ For the generic sensor model, active physical dimensions and optical-axis offset are derived by assuming the native sampling grid uniformly spans the declared physical imaging area. This assumption is recorded because a future calibrated camera profile may need explicit physical active-area data instead.
57
+
58
+ An off-center active rectangle changes both retained physical extent and angular position relative to the optical axis. It must therefore use asymmetric angular bounds rather than a centered FOV formula.
59
+
60
+ For an off-center rectangular active area, the two opposite-corner diagonal angular spans need not be equal. `calculateActiveCaptureFieldOfView()` reports both and uses the larger span for its compatibility `diagonalDegrees` summary.
61
+
62
+ Output crop/resampling is digital geometry. It does not mutate physical sensor identity or active-capture geometry. Output raster aspect ratio must remain consistent with the selected output crop; implicit geometric stretching is rejected.
63
+
64
+ Centered crop dimensions are derived from a linear crop factor while preserving source aspect ratio, subject to integer pixel flooring.
22
65
 
23
66
  Subject-height framing crop computes the additional same-aspect crop needed to target a requested subject-height fraction. It assumes the crop can be positioned around the subject and does not check subject position against image edges.
24
67
 
68
+ ### Crop factor and 35 mm-equivalent focal length
69
+
70
+ Physical crop factor is diagonal-based relative to a 36 × 24 mm reference frame:
71
+
72
+ `cropFactor = diagonal_35mm / activeCaptureDiagonal`
73
+
74
+ Conventional 35 mm-equivalent focal length is:
75
+
76
+ `equivalentFocalLength = actualFocalLength × cropFactor`
77
+
78
+ The actual focal length remains the optical focal length. Equivalent focal length is a derived framing convention; it does not replace physical focal length in projection or depth-of-field calculations.
79
+
80
+ Photivra bases equivalence on the active physical capture area, not later digital/output crop. Physical orientation leaves the active diagonal unchanged and therefore does not change diagonal-based equivalent focal length. Different aspect ratios can still produce different horizontal/vertical FOV even when the single diagonal-equivalent number is the same.
81
+
25
82
  ### Projection, focus extension, object size, and motion
26
83
 
27
84
  Field of view, projected fronto-parallel object size, projected representative-point subject motion, and camera-shake projection support two explicit projection modes:
@@ -37,18 +94,110 @@ The focus-aware model improves internal consistency with thin-lens DOF/defocus c
37
94
 
38
95
  Projected subject motion is a representative-point displacement model with constant linear world velocity. It does not yet describe scale blur of an extended object whose magnification changes materially during the exposure.
39
96
 
97
+ ### Generic illumination vignetting
98
+
99
+ The engine exposes a generic rotationally symmetric **relative linear illumination** model:
100
+
101
+ ```text
102
+ T(rho) = 1 + r2*rho² + r4*rho⁴ + r6*rho⁶
103
+ ```
104
+
105
+ where `rho` is ideal image-plane field radius divided by the caller-declared physical normalization radius.
106
+
107
+ The optical axis is normalized to `T(0) = 1`.
108
+
109
+ The declared `maximumNormalizedRadius` is a scientific operating envelope. Photivra evaluates the polynomial endpoints and all real internal extrema over the full interval and rejects profiles that:
110
+
111
+ - produce zero/negative throughput; or
112
+ - exceed the center-normalized throughput of 1.
113
+
114
+ The result is a linear-light multiplicative attenuation plus equivalent positive stop loss.
115
+
116
+ This slice is **not mechanical/pupil vignetting**. It does not clip the pupil or change bokeh/PSF shape. It also does not claim calibrated radiometry or named-lens behavior.
117
+
118
+ ### Generic lateral chromatic aberration
119
+
120
+ The standalone lateral-CA foundation uses the green channel as the reference field mapping.
121
+
122
+ A shared base radial-distortion profile defines the common geometric lens mapping. Red and blue coefficient offsets are added to that base before each channel is mapped:
123
+
124
+ ```text
125
+ green coefficients = base
126
+ red coefficients = base + red offset
127
+ blue coefficients = base + blue offset
128
+ ```
129
+
130
+ This keeps ordinary geometric distortion and lateral chromatic separation distinct while allowing one renderer pass to consume the combined per-channel mapping.
131
+
132
+ The model deliberately does **not** represent CA as finished-image RGB blur. Forward mapping reports per-channel field coordinates and vector separation; inverse mapping reports the per-channel ideal source coordinate required for one distorted destination.
133
+
134
+ All combined channel profiles share one physical normalization radius and operating envelope and must remain individually invertible.
135
+
136
+ The RGB labels are representative renderer channels only. The model does not define wavelengths, sensor CFA responses, longitudinal chromatic aberration, wavelength-dependent PSFs, or calibrated real-lens color behavior.
137
+
138
+ ### Generic radial distortion
139
+
140
+ The engine includes a standalone rotationally symmetric radial field-mapping approximation:
141
+
142
+ ```text
143
+ p_distorted = p_ideal × (1 + k1 r² + k2 r⁴ + k3 r⁶)
144
+ ```
145
+
146
+ where `r` is image-plane radius divided by a caller-declared physical normalization radius.
147
+
148
+ The declared `maximumNormalizedRadius` is part of the scientific contract. Photivra checks the derivative of the radial mapping over that interval and rejects profiles that are not strictly one-to-one. This makes deterministic inverse destination-to-source sampling well-defined.
149
+
150
+ The model is centered on the optical axis and does not yet include tangential/decentering terms. Coefficients are generic inputs, not real-lens calibration data.
151
+
152
+ ### Declared-scale focus breathing
153
+
154
+ The root engine exposes a generic focus-breathing projection approximation.
155
+
156
+ For one selected focus state:
157
+
158
+ ```text
159
+ effective projection distance
160
+ = ideal thin-lens image distance × caller-declared breathing projection scale
161
+ ```
162
+
163
+ Scale `1` exactly preserves the existing thin-lens projection.
164
+
165
+ The model intentionally does not infer a focus-breathing curve. It represents focus-dependent framing/magnification only when the caller supplies the scale for that state. Physical focal length remains unchanged.
166
+
167
+ This simplified scale does not model principal-plane movement, pupil magnification, distortion, aberrations, or a named commercial lens. Those require separate models and, for calibrated profiles, defensible provenance.
168
+
40
169
  ### Thin-lens focus and depth of field
41
170
 
42
171
  The geometric depth-of-field model uses the conventional hyperfocal/near/far equations with a caller-supplied acceptable circle of confusion.
43
172
 
44
173
  The circle-of-confusion criterion is a viewing/acceptability input, not a physical sensor threshold. The optional equivalent-viewing helper scales a caller-supplied reference criterion by sensor-diagonal ratio and is explicitly labeled an approximation.
45
174
 
175
+ In composed POC capture mode, the equivalent-viewing helper uses the final retained physical image region that will be enlarged to the assumed final viewing size. Active capture, digital output crop, and centered subject framing can therefore change the viewing criterion; output pixel resolution alone cannot. This changes only the viewing/acceptability convention, not the physical optical blur. An explicit caller-supplied `circleOfConfusionMm` is never rescaled.
176
+
46
177
  The model is not a macro calibration model and does not include diffraction, pupil magnification, aberrations, focus breathing, or lens-specific principal-plane behavior in its DOF criterion.
47
178
 
48
179
  ### Defocus circle
49
180
 
50
181
  Defocus is calculated geometrically by comparing the selected sensor plane for the focus distance with the ideal image plane for the subject distance and projecting an ideal circular entrance-pupil cone to the sensor.
51
182
 
183
+ ### PSF/pupil foundation
184
+
185
+ The engine now exposes a PSF/pupil foundation that evaluates existing geometric defocus and circular diffraction diagnostics in one explicit context while keeping the numerical contributions separate.
186
+
187
+ The context declares:
188
+
189
+ - physical image-plane field position;
190
+ - a comparison-only field normalization radius;
191
+ - focus and subject depth;
192
+ - monochromatic wavelength basis;
193
+ - ideal circular f-number-derived pupil size.
194
+
195
+ The current foundation does **not** calculate a combined point-spread function or MTF. Defocus-circle diameter and Airy first-zero diameter are not added together or collapsed into one sharpness value.
196
+
197
+ Reserved future contributions include non-circular diffraction, mechanical pupil clipping, field curvature, field-dependent aberration structure, and field-dependent bokeh.
198
+
199
+ Illumination vignetting remains outside the PSF contribution list because it is currently modeled as throughput-only.
200
+
52
201
  ### Diffraction
53
202
 
54
203
  For an ideal circular aperture, first-zero Airy diameter is:
@@ -79,6 +228,44 @@ The current model returns one global image-plane vector. It does not compute the
79
228
 
80
229
  Equivalent stabilization stops attenuate angular displacement by `2^-stops`; that attenuation is explicitly an educational approximation rather than a real IBIS/OIS or CIPA performance model.
81
230
 
231
+ ### Radiometry readiness boundary
232
+
233
+ Absolute scene luminance or relative exposure alone is not enough to derive a defensible photon count.
234
+
235
+ Before any future composed photon simulation, Photivra requires an explicit prerequisite package covering:
236
+
237
+ - scene spectral radiance or a documented spectral approximation;
238
+ - optical transmission;
239
+ - pupil/vignetting behavior;
240
+ - photosite collection-area semantics;
241
+ - exposure integration;
242
+ - sensor spectral response / quantum efficiency;
243
+ - evidence and uncertainty/limitation metadata for each component.
244
+
245
+ `assessRadiometryReadiness()` can classify a declared package as `not-ready`, `approximate-only`, or `calibrated-ready`. This classification validates the declared model/evidence structure; it does not prove that the calibration is scientifically correct and does not itself calculate photons.
246
+
247
+ Geometric sample pitch is intentionally insufficient as a collection-area model. A profile must provide either an effective collection area or geometric cell area plus explicit fill factor.
248
+
249
+ Approximate inputs remain approximate even when every prerequisite category is present. A calibrated-ready declaration requires calibrated components with quantified uncertainty.
250
+
251
+ The composed POC continues to emit no photon/photoelectron/SNR output from scene settings. Low-level `calculatePhotoelectrons()` remains a separate primitive for callers that already possess a defensible incident-photon count and QE.
252
+
253
+ ## Image-formation domain boundary
254
+
255
+ Future optical and sensor models must follow `getImageFormationContract()`.
256
+
257
+ The contract intentionally avoids describing all image formation as independent serial post-processing. Some quantities are coupled:
258
+
259
+ - focus-dependent projection and lens mapping;
260
+ - wavelength-dependent field mapping and PSF basis;
261
+ - pupil clipping, throughput, and bokeh/PSF shape;
262
+ - time-varying scene/camera mapping and sensor readout schedule;
263
+ - native sensor sampling and reconstruction.
264
+
265
+ Current analytical primitives remain valid and separately named. Adding a broader stage must not silently reinterpret existing focal length, defocus, diffraction, motion, crop, or sensor-sampling outputs.
266
+
267
+ Reserved sensor stages in the contract are ordering placeholders only; they do not establish OLPF, CFA, photon, ADC, or reconstruction capabilities.
268
+
82
269
  ## Source provenance
83
270
 
84
271
  These implementations were written independently from established mathematical/physical relations. No third-party source code, calibration dataset, table, or model weight is incorporated by these scientific modules.
package/docs/POC_API.md CHANGED
@@ -50,7 +50,7 @@ Example:
50
50
  {
51
51
  "ok": true,
52
52
  "service": "photivra-engine",
53
- "apiVersion": "0.17.0"
53
+ "apiVersion": "0.20.0"
54
54
  }
55
55
  ```
56
56
 
@@ -62,13 +62,48 @@ Requests must use `Content-Type: application/json`.
62
62
 
63
63
  All physical quantities use explicit units in their property names.
64
64
 
65
- API 0.17 composes projection using the selected focus plane. The response includes a `projection` block with ideal thin-lens image distance, scale relative to the infinity-focus approximation, and model provenance. Full-sensor field of view, crop field of view, object sampling, subject motion, and camera-shake projection use that same selected projection plane.
65
+ The POC transport reports the composed simulation contract version, not the root library `ENGINE_API_VERSION`. The current composed contract is exposed as `POC_SIMULATION_API_VERSION`.
66
+
67
+ POC simulation API 0.20 composes projection using the selected focus plane. The response includes a `projection` block with ideal thin-lens image distance, scale relative to the infinity-focus approximation, and model provenance. Full-sensor field of view, crop field of view, object sampling, subject motion, and camera-shake projection use that same selected projection plane.
68
+
69
+ ### Sensor/capture composition in POC API 0.20
70
+
71
+ POC simulation API 0.20 begins composing the standalone sensor/capture foundation **additively**.
72
+
73
+ The existing request remains valid. New callers may opt into staged capture geometry with a `capture` object containing:
74
+
75
+ - physical `orientation`;
76
+ - optional native `activeCaptureRect`;
77
+ - optional oriented `outputCropRect`;
78
+ - optional final `outputRaster`.
79
+
80
+ Compatibility rules are intentionally strict:
81
+
82
+ - legacy `crop.factor` remains available for existing callers;
83
+ - when `capture` is supplied, legacy `crop.factor` must be `1` so two crop models are not silently stacked;
84
+ - legacy top-level `subjectCrop` response semantics remain unchanged when staged capture is absent;
85
+ - with staged capture, subject framing is reported under `capture.subjectFraming` and is applied after declared output crop/resampling;
86
+ - equivalent-viewing CoC in staged capture is based on the final retained physical image region used for viewing, including subject framing when present; output pixel count alone does not change it.
87
+
88
+ The response keeps existing full-sensor/native-vector fields intact and adds an optional `capture` block with:
89
+
90
+ - resolved native/active/oriented/output geometry, including final retained physical bounds;
91
+ - active-capture and final-output FOV, including asymmetric bounds for off-center capture/output crop;
92
+ - physical actual focal length plus active-capture diagonal-based 35 mm equivalence;
93
+ - explicit oriented-capture→output pixel scaling;
94
+ - optional capture-specific subject framing with final retained physical area and effective FOV;
95
+ - explicit native-raster, oriented-capture, and final-output motion-vector diagnostics;
96
+ - corresponding native-raster/oriented/output camera-shake vectors when camera shake is requested.
97
+
98
+ Final digital/output crop does not redefine physical focal length or active-capture 35 mm equivalence. Physical `lens.focalLengthMm` remains the optical input.
99
+
100
+ Sensor architecture metadata and radiometry-readiness profiles remain standalone foundations and are not yet composed into the POC.
66
101
 
67
102
  ### Required request groups
68
103
 
69
104
  The request includes:
70
105
 
71
- - sensor dimensions and resolution;
106
+ - sensor physical imaging dimensions and native effective raster;
72
107
  - focal length and aperture;
73
108
  - shutter duration and ISO;
74
109
  - focus distance;
@@ -109,9 +144,33 @@ The top-level `fieldOfView` always describes the full sensor before digital crop
109
144
 
110
145
  This remains ideal paraxial geometry, not a real-lens focus-breathing calibration.
111
146
 
112
- ### Crop
147
+ ### Sensor and staged capture geometry
113
148
 
114
- `crop` reports the requested fixed crop factor, retained pixel dimensions/area, megapixels, and its effective field of view.
149
+ The `sensor` response now includes the shared `SensorGeometryMetrics` result in addition to the backwards-compatible representative pitch fields. This exposes physical imaging-area crop factor, exact native-raster megapixels, and independent X/Y geometric sample pitch without treating sample pitch as photosite collection area.
150
+
151
+ When the opt-in `capture` request is present, the response additionally exposes the staged geometry pipeline:
152
+
153
+ ```text
154
+ native physical imaging area + native raster
155
+ ↓
156
+ native active-capture rectangle
157
+ ↓
158
+ physical camera orientation
159
+ ↓
160
+ oriented active capture
161
+ ↓
162
+ digital/output crop
163
+ ↓
164
+ final output raster
165
+ ```
166
+
167
+ Native sensor coordinates remain invariant under orientation. Active-capture FOV is calculated from the physical retained area, including off-center asymmetric angular bounds.
168
+
169
+ Legacy projected-motion/camera-shake X/Y values use the existing image-plane basis (+X right, +Y up). Capture raster coordinates use +X right, +Y down. The capture diagnostics therefore expose the explicit conversion to `nativeRasterDeltaPixels` before rotating to `orientedCaptureDeltaPixels` and scaling to `outputDeltaPixels`; legacy fields remain unchanged.
170
+
171
+ ### Legacy crop
172
+
173
+ `crop` reports the requested legacy centered crop factor, retained pixel dimensions/area, megapixels, and its effective field of view.
115
174
 
116
175
  If `subjectCrop` is requested, subject framing is composed **after** the fixed crop. The response distinguishes:
117
176
 
@@ -122,8 +181,13 @@ If `subjectCrop` is requested, subject framing is composed **after** the fixed c
122
181
 
123
182
  The framing calculation assumes the crop can be positioned around the subject; it does not validate subject position against source-image edges.
124
183
 
184
+ ### Sensor sampling
185
+
186
+ The response exposes horizontal and vertical geometric sampling pitch plus their relative difference. The composed POC still uses the backwards-compatible horizontal pitch as its representative scalar for blur/sampling calculations and therefore rejects sensor geometry whose X/Y pitch differs by more than 1%. This prevents silent directional error until the composed POC becomes fully axis-aware.
187
+
125
188
  ### Subject/object sampling
126
189
 
190
+
127
191
  When physical subject/object dimensions are supplied, projected dimensions are returned in sensor millimetres and pixels using the focus-aware projection plane.
128
192
 
129
193
  These values can drive pre-authored asset/detail selection without moving projection math into a renderer.
@@ -156,9 +220,11 @@ Polygon aperture geometry does not convert the circular Airy result into a polyg
156
220
 
157
221
  ### Focus criterion
158
222
 
159
- For cross-format comparisons, the equivalent-viewing helper scales a caller-supplied reference CoC by sensor-diagonal ratio.
223
+ For cross-format comparisons, the equivalent-viewing helper scales a caller-supplied reference CoC by the diagonal of the physical image region assumed to be enlarged to the final viewing size.
224
+
225
+ Legacy requests continue to use the full physical sensor. In staged capture mode the target is the final physical region retained by active capture + output crop and, when requested, centered subject framing. Changing only the encoded output raster does not change this viewing criterion.
160
226
 
161
- That is explicitly an approximation based on equivalent final viewing assumptions; it is not a physical lens/sensor blur threshold.
227
+ This remains explicitly a viewing approximation, not a physical lens/sensor blur threshold. An explicit caller-supplied `circleOfConfusionMm` is never changed by crop or output geometry.
162
228
 
163
229
  ### Primary-subject diagnostics
164
230
 
@@ -15,6 +15,20 @@ An implementation may be based on:
15
15
 
16
16
  A paper, standard, or patent publication may explain a method without granting copyright permission to copy its expression and without granting a patent license. Independent implementation addresses source-code/prose provenance; it does not by itself resolve active third-party patent claims.
17
17
 
18
+ ## Source evidence and reuse rights
19
+
20
+ Factual/source evidence keeps **origin** separate from **reuse rights**.
21
+
22
+ - `sourceOrigin` identifies who originated the referenced fact or data: manufacturer, third party, or Photivra.
23
+ - `reuseStatus` identifies how Photivra may use it: factual-reference-only, reusable-data, or Photivra-owned.
24
+ - Reusable data requires an explicit license.
25
+ - Photivra-owned material must originate from Photivra.
26
+ - Manufacturer or third-party sources may be either factual-reference-only or explicitly reusable when licensing supports that use.
27
+
28
+ One evidence record must not silently support unrelated facts. Scalar metadata may cite multiple evidence records, and multi-valued capabilities should carry evidence per value.
29
+
30
+ Evidence provenance is not a confidence score and does not establish scientific effect by itself. Descriptive hardware facts only affect calculations when a separate documented model explicitly consumes them.
31
+
18
32
  ## Independent implementation
19
33
 
20
34
  When a reference explains a method but does not license its implementation or text for reuse:
@@ -82,6 +96,16 @@ Valid-range metadata describes where a model/calibration is considered applicabl
82
96
 
83
97
  When composing models, preserve uncertainty components separately unless a documented model provides a defensible combination rule. Do not imply statistical independence, add errors in quadrature, or publish a single aggregate confidence score without evidence.
84
98
 
99
+ ## Radiometry readiness evidence
100
+
101
+ Radiometry readiness is a declaration/validation gate, not proof that a calibration is correct.
102
+
103
+ A component labeled `calibrated` must identify its model/version, supporting evidence, and quantified uncertainty. If uncertainty cannot yet be quantified, the component may still be represented, but the overall assessment cannot become `calibrated-ready`.
104
+
105
+ Data-bearing calibration artifacts are referenced by stable identifier plus SHA-256 checksum. Publicly viewable curves/tables without reuse rights must not be copied into those artifacts. Manufacturer-published factual specifications may support independently authored models, while copied calibration datasets require explicit reusable licensing or Photivra ownership.
106
+
107
+ An `approximation` remains explicitly approximate even when all prerequisite categories are present. Readiness must never be used to upgrade approximation provenance into calibration provenance.
108
+
85
109
  ## Release gate
86
110
 
87
111
  Ambiguous licensing, unresolved material patent risk, uncertain provenance, unexplained copied material, fabricated uncertainty, undocumented uncertainty combination, or unsupported scientific claims block release until resolved.
@@ -0,0 +1,133 @@
1
+ # PSF and Pupil Foundation
2
+
3
+ Photivra's PSF/pupil foundation keeps optical blur contributions **separate and explicitly contextualized** until a defensible composition model exists.
4
+
5
+ Use:
6
+
7
+ ```ts
8
+ import {
9
+ calculatePsfFoundationComponents,
10
+ getPsfFoundationContract
11
+ } from "@photivra/engine";
12
+
13
+ const contract = getPsfFoundationContract();
14
+
15
+ const components = calculatePsfFoundationComponents({
16
+ focalLengthMm: 85,
17
+ aperture: 2.8,
18
+ focusDistanceM: 10,
19
+ subjectDistanceM: 20,
20
+ fieldPointMm: { x: 12, y: 8 },
21
+ fieldNormalizationRadiusMm: 21.6,
22
+ spectralBasis: {
23
+ kind: "monochromatic",
24
+ wavelengthNm: 550
25
+ }
26
+ });
27
+ ```
28
+
29
+ ## What is implemented
30
+
31
+ The current foundation evaluates two existing diagnostics in one explicit context:
32
+
33
+ - **geometric defocus circle** — the existing thin-lens defocus-circle diameter;
34
+ - **circular diffraction first-zero diameter** — the existing ideal circular-pupil Airy diagnostic.
35
+
36
+ Each contribution retains the provenance of its underlying primitive.
37
+
38
+ The foundation records:
39
+
40
+ - physical image-plane field position;
41
+ - normalized field radius for comparison;
42
+ - focus and subject depth;
43
+ - monochromatic wavelength basis;
44
+ - ideal circular f-number-derived pupil diameter.
45
+
46
+ ## What is deliberately not calculated
47
+
48
+ The foundation does **not** currently calculate:
49
+
50
+ - a combined PSF;
51
+ - a convolution kernel;
52
+ - MTF;
53
+ - one combined blur diameter;
54
+ - a lens-sharpness score.
55
+
56
+ Defocus and diffraction are independent diagnostics. A future combined PSF must document the mathematical composition method and assumptions rather than adding their diameters or scores ad hoc.
57
+
58
+ ## Reserved contributions
59
+
60
+ The public contract reserves ownership for:
61
+
62
+ - non-circular diffraction;
63
+ - mechanical/pupil clipping;
64
+ - field curvature;
65
+ - field-dependent aberration/PSF structure;
66
+ - field-dependent bokeh.
67
+
68
+ A reserved contribution is not an implemented capability.
69
+
70
+ ## Field position
71
+
72
+ Field position uses physical image-plane coordinates:
73
+
74
+ - optical axis at the origin;
75
+ - +X right;
76
+ - +Y up;
77
+ - millimetres.
78
+
79
+ `fieldNormalizationRadiusMm` exists only to make center/mid/edge/corner comparisons reproducible. It is not a real-lens calibration or model-validity limit by itself.
80
+
81
+ The current defocus and circular Airy diagnostics are field invariant. Recording field position now gives future field-dependent contributions a stable context without changing those existing calculations.
82
+
83
+ ## Spectral basis
84
+
85
+ The current foundation accepts an explicit monochromatic wavelength because the circular Airy diagnostic is wavelength dependent.
86
+
87
+ This does not create:
88
+
89
+ - a spectral lens model;
90
+ - longitudinal chromatic aberration;
91
+ - sensor spectral response;
92
+ - CFA/color calibration.
93
+
94
+ Future wavelength-dependent PSFs must declare their basis explicitly.
95
+
96
+ ## Pupil boundary
97
+
98
+ The current context records an ideal circular, f-number-derived pupil diameter because the existing defocus/Airy diagnostics use circular-pupil assumptions.
99
+
100
+ Polygon aperture geometry is not automatically a non-circular diffraction PSF.
101
+
102
+ Mechanical/pupil vignetting is also separate from illumination vignetting:
103
+
104
+ - illumination vignetting changes throughput only;
105
+ - pupil clipping can change throughput **and** PSF/bokeh shape.
106
+
107
+ ## Preview and reference fidelity
108
+
109
+ Browser preview and higher-fidelity reference implementations may use different numerical approximations or sampling budgets.
110
+
111
+ They must preserve the same:
112
+
113
+ - contribution identities;
114
+ - field/depth/spectral context;
115
+ - pupil semantics;
116
+ - engine-owned parameters;
117
+ - scientific limitations.
118
+
119
+ A preview approximation must not redefine the science merely because it is cheaper to render.
120
+
121
+ ## Test Fixture relationship
122
+
123
+ Test Fixture v0.3 provides deterministic center/mid/edge/corner detail and point-highlight targets for integration regression.
124
+
125
+ It does not establish:
126
+
127
+ - calibrated MTF;
128
+ - a measured real-lens PSF;
129
+ - spectral lens behavior;
130
+ - calibrated mechanical vignetting;
131
+ - named-lens performance.
132
+
133
+ Engine analytical/reference tests remain the scientific source of truth.
@@ -26,11 +26,24 @@ Projection has two modes:
26
26
 
27
27
  When pixel pitch is supplied, the same displacement is also reported in sensor pixels.
28
28
 
29
+ The returned X/Y shake components are expressed in the primitive's sensor/image-plane axes. `estimateCameraShakeBlur()` does not accept `CaptureOrientation` and does not rotate its vector into portrait/oriented output coordinates. A caller combining this primitive with the capture-orientation layer must explicitly transform the vector with the orientation helpers.
30
+
29
31
  The supplied shake profile is synthetic input. The model is not an empirical statement about how much a particular photographer shakes.
30
32
 
31
- ### Spatial limitation
33
+ ### Spatial limitation and the separate rotation primitive
34
+
35
+ `estimateCameraShakeBlur()` intentionally remains one global image-plane yaw/pitch vector for backwards compatibility with the existing stabilization-equivalent teaching model.
36
+
37
+ The root engine now separately exposes `calculateCameraRotationImageMapping()`, which models field-position-dependent **pure camera rotation** for yaw/pitch/roll at arbitrary physical time from exposure start.
38
+
39
+ The two APIs do not share sign semantics:
40
+
41
+ - the legacy shake estimator preserves its historical direct image-plane displacement convention;
42
+ - the spatial rotation primitive represents physical camera rotation using right-hand pitch/yaw/roll, then maps a stationary world ray through the inverse camera rotation.
43
+
44
+ Do not substitute one result for the other without an explicit migration.
32
45
 
33
- The current result is one global image-plane yaw/pitch vector. Real camera rotation produces position-dependent optical flow away from the optical axis; that spatial variation is not yet modeled. Roll and translational camera motion are also omitted.
46
+ The spatial primitive does not apply stabilization stops. Real IBIS/OIS behavior and translation/parallax remain separate future models.
34
47
 
35
48
  ## Equivalent stabilization stops
36
49
 
@@ -60,11 +73,11 @@ Only the public standard identifier and high-level terminology are referenced he
60
73
 
61
74
  ## Not yet modeled
62
75
 
63
- The current model intentionally omits:
76
+ The legacy stabilization-equivalent model intentionally omits:
64
77
 
65
78
  - roll;
66
79
  - translational camera shake;
67
- - spatially varying rotational optical flow;
80
+ - spatially varying rotational optical flow inside this compatibility API;
68
81
  - frequency-dependent shake spectra;
69
82
  - photographer-to-photographer variation;
70
83
  - shutter-button impulse;