@photivra/engine 2.16.0 → 2.17.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 +56 -1
  2. package/CITATION.cff +1 -1
  3. package/README.md +6 -6
  4. package/SECURITY.md +1 -1
  5. package/THIRD_PARTY.md +24 -0
  6. package/dist/camera/lens-projection.d.ts +5 -0
  7. package/dist/camera/lens-projection.js +10 -0
  8. package/dist/camera/lens-projection.js.map +1 -1
  9. package/dist/core/version.d.ts +1 -1
  10. package/dist/core/version.js +1 -1
  11. package/dist/core/version.js.map +1 -1
  12. package/dist/equipment/generic-lens-catalogue.d.ts +227 -0
  13. package/dist/equipment/generic-lens-catalogue.js +243 -0
  14. package/dist/equipment/generic-lens-catalogue.js.map +1 -0
  15. package/dist/facade/defocus-bokeh-zones.d.ts +33 -0
  16. package/dist/facade/defocus-bokeh-zones.js +53 -0
  17. package/dist/facade/defocus-bokeh-zones.js.map +1 -0
  18. package/dist/facade/defocus-image-space-provider.d.ts +9 -0
  19. package/dist/facade/defocus-image-space-provider.js +96 -7
  20. package/dist/facade/defocus-image-space-provider.js.map +1 -1
  21. package/dist/facade/diffraction-provider.d.ts +29 -0
  22. package/dist/facade/diffraction-provider.js +117 -11
  23. package/dist/facade/diffraction-provider.js.map +1 -1
  24. package/dist/facade/display-view-gpu-session.js +4 -0
  25. package/dist/facade/display-view-gpu-session.js.map +1 -1
  26. package/dist/facade/display-view-gpu.d.ts +10 -0
  27. package/dist/facade/display-view-gpu.js.map +1 -1
  28. package/dist/facade/display-view-lens-signal.d.ts +30 -0
  29. package/dist/facade/display-view-lens-signal.js +427 -0
  30. package/dist/facade/display-view-lens-signal.js.map +1 -0
  31. package/dist/facade/expected-signal-setup.d.ts +7 -0
  32. package/dist/facade/expected-signal-setup.js +12 -2
  33. package/dist/facade/expected-signal-setup.js.map +1 -1
  34. package/dist/facade/expected-signal.d.ts +8 -0
  35. package/dist/facade/expected-signal.js +150 -0
  36. package/dist/facade/expected-signal.js.map +1 -1
  37. package/dist/facade/generic-lens-capture.d.ts +6 -0
  38. package/dist/facade/generic-lens-capture.js +8 -0
  39. package/dist/facade/generic-lens-capture.js.map +1 -0
  40. package/dist/facade/large-defocus-lattice.d.ts +39 -0
  41. package/dist/facade/large-defocus-lattice.js +64 -0
  42. package/dist/facade/large-defocus-lattice.js.map +1 -0
  43. package/dist/facade/scene-radiance.d.ts +5 -0
  44. package/dist/facade/scene-radiance.js +5 -2
  45. package/dist/facade/scene-radiance.js.map +1 -1
  46. package/dist/facade/simulate-capture.d.ts +38 -5
  47. package/dist/facade/simulate-capture.js +306 -49
  48. package/dist/facade/simulate-capture.js.map +1 -1
  49. package/dist/facade/simulate-display-view.js +22 -16
  50. package/dist/facade/simulate-display-view.js.map +1 -1
  51. package/dist/gpu/display-view-quality-gpu.d.ts +0 -9
  52. package/dist/gpu/display-view-quality-gpu.js +6 -0
  53. package/dist/gpu/display-view-quality-gpu.js.map +1 -1
  54. package/dist/gpu/display-view-speed-gpu.js +2 -0
  55. package/dist/gpu/display-view-speed-gpu.js.map +1 -1
  56. package/dist/index.d.ts +3 -0
  57. package/dist/index.js +3 -0
  58. package/dist/index.js.map +1 -1
  59. package/dist/optics/defocus-bokeh-kernel.d.ts +207 -0
  60. package/dist/optics/defocus-bokeh-kernel.js +380 -0
  61. package/dist/optics/defocus-bokeh-kernel.js.map +1 -0
  62. package/dist/optics/defocus-disc-kernel.d.ts +41 -19
  63. package/dist/optics/defocus-disc-kernel.js +44 -23
  64. package/dist/optics/defocus-disc-kernel.js.map +1 -1
  65. package/dist/optics/generic-lens-psf.d.ts +68 -0
  66. package/dist/optics/generic-lens-psf.js +337 -0
  67. package/dist/optics/generic-lens-psf.js.map +1 -0
  68. package/dist/optics/illumination-vignetting.d.ts +6 -0
  69. package/dist/optics/illumination-vignetting.js +24 -7
  70. package/dist/optics/illumination-vignetting.js.map +1 -1
  71. package/dist/optics/sensor-environment-query.d.ts +6 -0
  72. package/dist/optics/sensor-environment-query.js +13 -6
  73. package/dist/optics/sensor-environment-query.js.map +1 -1
  74. package/dist/optics/site-kernel-runs.d.ts +77 -0
  75. package/dist/optics/site-kernel-runs.js +200 -0
  76. package/dist/optics/site-kernel-runs.js.map +1 -0
  77. package/dist/output/generic-lens-corrections.d.ts +24 -0
  78. package/dist/output/generic-lens-corrections.js +51 -0
  79. package/dist/output/generic-lens-corrections.js.map +1 -0
  80. package/docs/AGENT_GUIDE.md +1 -0
  81. package/docs/API_REFERENCE.md +49 -1
  82. package/docs/QUICK_START.md +257 -17
  83. package/docs/USAGE.md +57 -5
  84. package/docs/api/core-version.md +1 -1
  85. package/docs/api/equipment-generic-lens-catalogue.md +281 -0
  86. package/docs/api/exports.json +137 -2
  87. package/docs/api/facade-simulate-capture.md +23 -2
  88. package/docs/api/optics-defocus-bokeh-kernel.md +145 -0
  89. package/docs/api/optics-defocus-disc-kernel.md +22 -15
  90. package/docs/api/optics-sensor-environment-query.md +6 -0
  91. package/docs/api/output-generic-lens-corrections.md +39 -0
  92. package/llms.txt +1 -0
  93. package/package.json +1 -1
@@ -0,0 +1,281 @@
1
+ # equipment/generic-lens-catalogue.ts public contracts
2
+
3
+ The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
4
+
5
+ ## GENERIC_LENS_CATALOGUE
6
+
7
+ The 30 lenses: 17 professional, then 13 consumer, each set by focal length.
8
+
9
+ ```ts
10
+ GENERIC_LENS_CATALOGUE: readonly GenericLensCatalogueEntry[] = Object.freeze([...PRO.map((row) => entry("professional", row)),
11
+ ...CONSUMER.map((row) => entry("consumer", row))])
12
+ ```
13
+
14
+ ## GENERIC_LENS_IDS
15
+
16
+ The catalogue ids, in catalogue order.
17
+
18
+ ```ts
19
+ GENERIC_LENS_IDS: readonly string[] = Object.freeze(GENERIC_LENS_CATALOGUE.map((lens) => lens.lensId))
20
+ ```
21
+
22
+ ## GENERIC_LENS_SET_BASELINES
23
+
24
+ The set baselines (owner's table, #549), each lens's starting point before its adjustments.
25
+
26
+ ```ts
27
+ GENERIC_LENS_SET_BASELINES: Readonly<Record<GenericLensSet, Readonly<GenericLensCharacterValues>>> = Object.freeze({
28
+ professional: Object.freeze({ sphericalWaves: 0.25, comaWaves: 0.35, astigmatismWaves: 0.3, fieldCurvatureWaves: 0.15, axialColorWaves: 0.15,
29
+ lateralColorFraction: 0.00008, distortionMagnitude: 0.01, naturalVignettingStops: 0.4, opticalVignettingStops: 0.6, veilingGlareFraction: 0.005, ghostFraction: 0.002,
30
+ breathingFraction: -0.03, focusScatterDepthOfFocusFraction: 0.1, closeFocusFactor: 0.15 }),
31
+ consumer: Object.freeze({ sphericalWaves: 0.5, comaWaves: 1.0, astigmatismWaves: 0.8, fieldCurvatureWaves: 0.45, axialColorWaves: 0.4,
32
+ lateralColorFraction: 0.0003, distortionMagnitude: 0.03, naturalVignettingStops: 0.7, opticalVignettingStops: 1.1, veilingGlareFraction: 0.02, ghostFraction: 0.008,
33
+ breathingFraction: -0.08, focusScatterDepthOfFocusFraction: 0.3, closeFocusFactor: 0.5 })
34
+ })
35
+ ```
36
+
37
+ ## GenericLensAdjustments
38
+
39
+ Per-lens multipliers of the set baseline (missing: 1). `distortion` is signed and required: −1 barrel, +1 pincushion at the baseline.
40
+
41
+ ```ts
42
+ export type GenericLensAdjustments = { distortion: number } & Partial<Record<"spherical" | "coma" | "astigmatism" | "fieldCurvature" | "axialColor" | "lateralColor" |
43
+ "vignetting" | "strayLight" | "breathing" | "focusScatter" | "closeFocus", number>>;
44
+ ```
45
+
46
+ ## GenericLensCatalogueEntry
47
+
48
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
49
+
50
+ ```ts
51
+ export interface GenericLensCatalogueEntry {
52
+ /** Catalogue id, for example "pv-50-1.2-pro". */
53
+ lensId: string;
54
+ /** Display name, for example "PV 50 1.2 Pro". */
55
+ name: string;
56
+ set: GenericLensSet;
57
+ kind: GenericLensKind;
58
+ /** Full-frame-equivalent focal length (36 × 24 mm reference). */
59
+ equivalentFocalLengthMm: number;
60
+ widestFNumber: number;
61
+ narrowestFNumber: number;
62
+ /** Largest magnification: 0.15 (the generic rule) or 1 for the professional macro. */
63
+ maximumMagnification: number;
64
+ projection: LensProjectionModel;
65
+ /** Fisheyes only: a circular image inside the frame, or one that fills it. */
66
+ fisheyeImage?: "circular" | "full-frame";
67
+ adjustments: Readonly<GenericLensAdjustments>;
68
+ /** Why this lens departs from its set's baseline. */
69
+ note: string;
70
+ /**
71
+ * A short plain-English summary of the lens's character (at most 40 characters, US spelling), written from its adjustments and
72
+ * `note`, for lists and pickers. It describes only what the catalogue models (bokeh shape is not modeled: the defocus is a disc).
73
+ */
74
+ summary: string;
75
+ }
76
+ ```
77
+
78
+ ## GenericLensCharacter
79
+
80
+ A catalogue lens's character resolved for a sensor, an f-number and a focus distance. Aberrations in waves at 550 nm.
81
+
82
+ ```ts
83
+ export interface GenericLensCharacter {
84
+ fNumber: number;
85
+ focusDistanceM: number | null;
86
+ /** Thin-lens magnification at the focus distance (0 at infinity). */
87
+ magnification: number;
88
+ /** 1 + c·m/m_max. */
89
+ closeFocusGain: number;
90
+ /** Pupil-radius ratio N0/N. */
91
+ pupilScale: number;
92
+ /** Resolved Seidel coefficients at the frame corner (H = 1), waves at 550 nm; `balancingDefocusWaves` = −spherical. */
93
+ aberrations: { sphericalWaves: number; balancingDefocusWaves: number; comaWaves: number; astigmatismWaves: number; fieldCurvatureWaves: number; axialColorWaves: number };
94
+ /** Lateral colour: F-to-C image-radius difference at the corner, fraction of the half-diagonal and micrometres on this sensor. */
95
+ lateralColor: { fraction: number; cornerMicrometers: number };
96
+ /** Radial distortion profile (normalised to the half-diagonal, valid to 1.25 of it, beyond the corner) and its corner value (fraction; negative = barrel). */
97
+ distortion: { profile: RadialDistortionProfile; corner: number };
98
+ /** Relative illumination profile (normalised to the half-diagonal, valid to 1.25 of it) and the corner fall-off in stops. */
99
+ vignetting: { profile: IlluminationVignettingProfile; cornerStops: number };
100
+ strayLight: { veilingGlareFraction: number; ghostFraction: number };
101
+ /** Field-of-view change at this focus and at minimum focus, beyond thin-lens unit focusing (fraction, negative = narrower). */
102
+ breathing: { fieldOfViewChange: number; atMinimumFocus: number };
103
+ /** Standard deviation of the autofocus error as a fraction of the depth of focus 2·N·c. */
104
+ focusScatterDepthOfFocusFraction: number;
105
+ }
106
+ ```
107
+
108
+ ## GenericLensCharacterValues
109
+
110
+ The character of a set, at each lens's widest aperture. Aberrations are in waves at 550 nm at the frame corner.
111
+
112
+ ```ts
113
+ export interface GenericLensCharacterValues {
114
+ /** Spherical aberration W040 (balanced by W020 = −W040): centre sharpness. */
115
+ sphericalWaves: number;
116
+ /** Coma W131 at H = 1: the comet-shaped point images of night photography. */
117
+ comaWaves: number;
118
+ /** Astigmatism W222 at H = 1: edge and corner sharpness. */
119
+ astigmatismWaves: number;
120
+ /** Field curvature W220 at H = 1: edge and corner sharpness. */
121
+ fieldCurvatureWaves: number;
122
+ /** Axial colour: the F-to-C difference of the defocus W020, waves at 550 nm. */
123
+ axialColorWaves: number;
124
+ /** Lateral colour: the F-to-C difference of the image radius at the corner, as a fraction of the half-diagonal. */
125
+ lateralColorFraction: number;
126
+ /** Corner distortion magnitude |k1| (fraction); each lens gives the sign (barrel −, pincushion +). */
127
+ distortionMagnitude: number;
128
+ /** Corner fall-off in stops that stays when stopped down (natural vignetting). */
129
+ naturalVignettingStops: number;
130
+ /** Extra corner fall-off in stops at the widest aperture (optical vignetting); gone two stops down. */
131
+ opticalVignettingStops: number;
132
+ /** Veiling glare: the fraction of the image's mean irradiance spread as a uniform veil. */
133
+ veilingGlareFraction: number;
134
+ /** Peak ghost power as a fraction of a bright source's power on the axis. */
135
+ ghostFraction: number;
136
+ /** Field-of-view change between infinity and minimum focus as a fraction (negative: narrower), beyond thin-lens unit focusing. */
137
+ breathingFraction: number;
138
+ /** Standard deviation of the autofocus error as a fraction of the depth of focus 2·N·c. */
139
+ focusScatterDepthOfFocusFraction: number;
140
+ /** c in the close-focus factor 1 + c·m/m_max. */
141
+ closeFocusFactor: number;
142
+ }
143
+ ```
144
+
145
+ ## GenericLensFocusError
146
+
147
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
148
+
149
+ ```ts
150
+ export interface GenericLensFocusError {
151
+ lensId: string;
152
+ /** The intended and the sampled focus distance, m (the sampled one held within minimum focus and a far limit). */
153
+ intendedFocusDistanceM: number;
154
+ focusDistanceM: number;
155
+ /** The standard normal draw z and the image-side focus error z·σ·2Nc, µm (positive: a longer image distance, so focused nearer). */
156
+ standardNormal: number;
157
+ imageSideErrorMicrometers: number;
158
+ /** σ (fraction of the depth of focus) and the depth of focus 2·N·c, µm. */
159
+ depthOfFocusFraction: number;
160
+ depthOfFocusMicrometers: number;
161
+ /** True when the sampled image distance was held at the minimum-focus or the far (1000 m) limit. */
162
+ held: "minimum-focus" | "far-limit" | null;
163
+ scientificStatus: "approximation";
164
+ }
165
+ ```
166
+
167
+ ## GenericLensKind
168
+
169
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
170
+
171
+ ```ts
172
+ export type GenericLensKind = "standard" | "macro" | "fisheye";
173
+ ```
174
+
175
+ ## GenericLensSet
176
+
177
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
178
+
179
+ ```ts
180
+ export type GenericLensSet = "professional" | "consumer";
181
+ ```
182
+
183
+ ## ResolvedGenericLens
184
+
185
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
186
+
187
+ ```ts
188
+ export interface ResolvedGenericLens {
189
+ lensId: string;
190
+ name: string;
191
+ set: GenericLensSet;
192
+ kind: GenericLensKind;
193
+ equivalentFocalLengthMm: number;
194
+ /** Physical focal length on this sensor: equivalent ÷ crop factor. */
195
+ focalLengthMm: number;
196
+ cropFactor35Mm: number;
197
+ /** Half-diagonal of the sensor, mm: field height H = 1 and ρ = 1 of the profiles. */
198
+ halfDiagonalMm: number;
199
+ widestFNumber: number;
200
+ narrowestFNumber: number;
201
+ /** Marked third-stop f-numbers within the range. */
202
+ fNumbers: readonly number[];
203
+ /** max(0.10 m, f·(1 + 1/m_max)), metres from the lens (thin lens). */
204
+ minimumFocusDistanceM: number;
205
+ maximumMagnification: number;
206
+ projection: LensProjectionModel;
207
+ fisheyeImage: "circular" | "full-frame" | null;
208
+ /** Fisheyes: the radius of the 180° image circle on this sensor, 2·f·sin 45° (equisolid), mm. Null otherwise. */
209
+ imageCircleRadiusMm: number | null;
210
+ character: GenericLensCharacter;
211
+ note: string;
212
+ /** The catalogue entry's `summary`: a short plain-English description of the lens's character (at most 40 characters). */
213
+ summary: string;
214
+ evidence: readonly EvidenceProvenance[];
215
+ scientificStatus: "approximation";
216
+ limitations: readonly string[];
217
+ }
218
+ ```
219
+
220
+ ## resolveGenericLens
221
+
222
+ Resolves a catalogue lens for a sensor: its focal length (equivalent ÷ the diagonal crop factor), f-number range, close focus
223
+ and projection, and its character (aberrations, colour, distortion, vignetting, flare, breathing and focus scatter) at the
224
+ given f-number and focus distance. Every value is a labelled approximation of a generic lens, not a measured product.
225
+
226
+ ```ts
227
+ export function resolveGenericLens(input: ResolveGenericLensInput): ResolvedGenericLens;
228
+ ```
229
+
230
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
231
+
232
+ ## ResolveGenericLensInput
233
+
234
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
235
+
236
+ ```ts
237
+ export interface ResolveGenericLensInput {
238
+ /** A catalogue id (`GENERIC_LENS_IDS`). */
239
+ lensId: string;
240
+ /** The sensor's active capture area, mm. */
241
+ activeImagingArea: { widthMm: number; heightMm: number };
242
+ /** Default: the widest aperture. Must lie in the lens's range. */
243
+ fNumber?: number;
244
+ /** Default: infinity. At least the minimum focus distance. */
245
+ focusDistanceM?: number;
246
+ }
247
+ ```
248
+
249
+ ## simulateGenericLensFocusError
250
+
251
+ Focus consistency (#549): a seeded, reproducible autofocus error for one shot. The image-side error is z·σ·2Nc, with z a
252
+ standard normal draw from (seed, shotIndex) (MurmurHash3 stream seed, xorshift32 and Box–Muller, the generators of the
253
+ capture's sensor noise), σ the lens's focus scatter as a fraction of the depth of focus 2·N·c, and
254
+ c the circle of confusion. The thin-lens image distance of the intended focus moves by that error and the focus distance is
255
+ read back from it. An approximation of autofocus variation; off unless called (or `simulateCapture`'s `lens.focusScatter`).
256
+
257
+ ```ts
258
+ export function simulateGenericLensFocusError(input: SimulateGenericLensFocusErrorInput): GenericLensFocusError;
259
+ ```
260
+
261
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
262
+
263
+ ## SimulateGenericLensFocusErrorInput
264
+
265
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
266
+
267
+ ```ts
268
+ export interface SimulateGenericLensFocusErrorInput {
269
+ lensId: string;
270
+ activeImagingArea: { widthMm: number; heightMm: number };
271
+ fNumber: number;
272
+ /** The intended focus distance, m. */
273
+ focusDistanceM: number;
274
+ /** uint32 seed of the shooting session. */
275
+ seed: number;
276
+ /** uint32 index of the shot in the session. Default 0. */
277
+ shotIndex?: number;
278
+ /** Circle of confusion for the depth of focus, mm. Default: the sensor diagonal / 1500. */
279
+ circleOfConfusionMm?: number;
280
+ }
281
+ ```
@@ -1,6 +1,6 @@
1
1
  {
2
- "packageVersion": "2.16.0",
3
- "engineApiVersion": "2.16.0",
2
+ "packageVersion": "2.17.0",
3
+ "engineApiVersion": "2.17.0",
4
4
  "exports": [
5
5
  {
6
6
  "name": "AbsoluteUncertaintyEstimate",
@@ -562,6 +562,16 @@
562
562
  "source": "src/color/illuminants.ts",
563
563
  "kind": "runtime"
564
564
  },
565
+ {
566
+ "name": "calculateDefocusBokehKernel",
567
+ "source": "src/optics/defocus-bokeh-kernel.ts",
568
+ "kind": "runtime"
569
+ },
570
+ {
571
+ "name": "CalculateDefocusBokehKernelInput",
572
+ "source": "src/optics/defocus-bokeh-kernel.ts",
573
+ "kind": "type"
574
+ },
565
575
  {
566
576
  "name": "calculateDefocusCircle",
567
577
  "source": "src/optics/depth-of-field.ts",
@@ -2432,6 +2442,16 @@
2432
2442
  "source": "src/equipment/generic-tier-presets.ts",
2433
2443
  "kind": "runtime"
2434
2444
  },
2445
+ {
2446
+ "name": "createGenericLensCorrectionProfile",
2447
+ "source": "src/output/generic-lens-corrections.ts",
2448
+ "kind": "runtime"
2449
+ },
2450
+ {
2451
+ "name": "CreateGenericLensCorrectionProfileInput",
2452
+ "source": "src/output/generic-lens-corrections.ts",
2453
+ "kind": "type"
2454
+ },
2435
2455
  {
2436
2456
  "name": "createHdrBracketMergeTask",
2437
2457
  "source": "src/processing/hdr.ts",
@@ -2702,11 +2722,46 @@
2702
2722
  "source": "src/sensor/capture-mode.ts",
2703
2723
  "kind": "type"
2704
2724
  },
2725
+ {
2726
+ "name": "DEFOCUS_BOKEH_MAXIMUM_BLADES",
2727
+ "source": "src/optics/defocus-bokeh-kernel.ts",
2728
+ "kind": "runtime"
2729
+ },
2705
2730
  {
2706
2731
  "name": "DEFOCUS_DISC_KERNEL_MAXIMUM_RADIUS_SITES",
2707
2732
  "source": "src/optics/defocus-disc-kernel.ts",
2708
2733
  "kind": "runtime"
2709
2734
  },
2735
+ {
2736
+ "name": "DefocusBokehBlades",
2737
+ "source": "src/optics/defocus-bokeh-kernel.ts",
2738
+ "kind": "type"
2739
+ },
2740
+ {
2741
+ "name": "DefocusBokehCatsEye",
2742
+ "source": "src/optics/defocus-bokeh-kernel.ts",
2743
+ "kind": "type"
2744
+ },
2745
+ {
2746
+ "name": "DefocusBokehClippingAperture",
2747
+ "source": "src/optics/defocus-bokeh-kernel.ts",
2748
+ "kind": "type"
2749
+ },
2750
+ {
2751
+ "name": "DefocusBokehKernel",
2752
+ "source": "src/optics/defocus-bokeh-kernel.ts",
2753
+ "kind": "type"
2754
+ },
2755
+ {
2756
+ "name": "DefocusBokehOptions",
2757
+ "source": "src/optics/defocus-bokeh-kernel.ts",
2758
+ "kind": "type"
2759
+ },
2760
+ {
2761
+ "name": "DefocusBokehSphericalAberration",
2762
+ "source": "src/optics/defocus-bokeh-kernel.ts",
2763
+ "kind": "type"
2764
+ },
2710
2765
  {
2711
2766
  "name": "DefocusCircle",
2712
2767
  "source": "src/optics/depth-of-field.ts",
@@ -3482,6 +3537,26 @@
3482
3537
  "source": "src/sensor/iso-signal-chain.ts",
3483
3538
  "kind": "runtime"
3484
3539
  },
3540
+ {
3541
+ "name": "GENERIC_LENS_CATALOGUE",
3542
+ "source": "src/equipment/generic-lens-catalogue.ts",
3543
+ "kind": "runtime"
3544
+ },
3545
+ {
3546
+ "name": "GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM",
3547
+ "source": "src/output/generic-lens-corrections.ts",
3548
+ "kind": "runtime"
3549
+ },
3550
+ {
3551
+ "name": "GENERIC_LENS_IDS",
3552
+ "source": "src/equipment/generic-lens-catalogue.ts",
3553
+ "kind": "runtime"
3554
+ },
3555
+ {
3556
+ "name": "GENERIC_LENS_SET_BASELINES",
3557
+ "source": "src/equipment/generic-lens-catalogue.ts",
3558
+ "kind": "runtime"
3559
+ },
3485
3560
  {
3486
3561
  "name": "GENERIC_PRIME_EQUIVALENT_FOCAL_LENGTHS_MM",
3487
3562
  "source": "src/equipment/generic-prime-lenses.ts",
@@ -3587,6 +3662,26 @@
3587
3662
  "source": "src/sensor/iso-signal-chain.ts",
3588
3663
  "kind": "type"
3589
3664
  },
3665
+ {
3666
+ "name": "GenericLensAdjustments",
3667
+ "source": "src/equipment/generic-lens-catalogue.ts",
3668
+ "kind": "type"
3669
+ },
3670
+ {
3671
+ "name": "GenericLensCatalogueEntry",
3672
+ "source": "src/equipment/generic-lens-catalogue.ts",
3673
+ "kind": "type"
3674
+ },
3675
+ {
3676
+ "name": "GenericLensCharacter",
3677
+ "source": "src/equipment/generic-lens-catalogue.ts",
3678
+ "kind": "type"
3679
+ },
3680
+ {
3681
+ "name": "GenericLensCharacterValues",
3682
+ "source": "src/equipment/generic-lens-catalogue.ts",
3683
+ "kind": "type"
3684
+ },
3590
3685
  {
3591
3686
  "name": "GenericLensCorrectionProfile",
3592
3687
  "source": "src/output/lens-corrections.ts",
@@ -3597,6 +3692,21 @@
3597
3692
  "source": "src/equipment/exposure-capabilities.ts",
3598
3693
  "kind": "type"
3599
3694
  },
3695
+ {
3696
+ "name": "GenericLensFocusError",
3697
+ "source": "src/equipment/generic-lens-catalogue.ts",
3698
+ "kind": "type"
3699
+ },
3700
+ {
3701
+ "name": "GenericLensKind",
3702
+ "source": "src/equipment/generic-lens-catalogue.ts",
3703
+ "kind": "type"
3704
+ },
3705
+ {
3706
+ "name": "GenericLensSet",
3707
+ "source": "src/equipment/generic-lens-catalogue.ts",
3708
+ "kind": "type"
3709
+ },
3600
3710
  {
3601
3711
  "name": "GenericOpticalEvidence",
3602
3712
  "source": "src/optics/profile-contract.ts",
@@ -6622,6 +6732,11 @@
6622
6732
  "source": "src/sensor/iso-signal-chain.ts",
6623
6733
  "kind": "type"
6624
6734
  },
6735
+ {
6736
+ "name": "ResolvedGenericLens",
6737
+ "source": "src/equipment/generic-lens-catalogue.ts",
6738
+ "kind": "type"
6739
+ },
6625
6740
  {
6626
6741
  "name": "ResolvedGenericTierLensProfiles",
6627
6742
  "source": "src/equipment/generic-tier-presets.ts",
@@ -6782,6 +6897,16 @@
6782
6897
  "source": "src/sensor/iso-signal-chain.ts",
6783
6898
  "kind": "runtime"
6784
6899
  },
6900
+ {
6901
+ "name": "resolveGenericLens",
6902
+ "source": "src/equipment/generic-lens-catalogue.ts",
6903
+ "kind": "runtime"
6904
+ },
6905
+ {
6906
+ "name": "ResolveGenericLensInput",
6907
+ "source": "src/equipment/generic-lens-catalogue.ts",
6908
+ "kind": "type"
6909
+ },
6785
6910
  {
6786
6911
  "name": "resolveGenericPrimeLens",
6787
6912
  "source": "src/equipment/generic-prime-lenses.ts",
@@ -8477,6 +8602,16 @@
8477
8602
  "source": "src/capture/environment-raw-producer.ts",
8478
8603
  "kind": "type"
8479
8604
  },
8605
+ {
8606
+ "name": "simulateGenericLensFocusError",
8607
+ "source": "src/equipment/generic-lens-catalogue.ts",
8608
+ "kind": "runtime"
8609
+ },
8610
+ {
8611
+ "name": "SimulateGenericLensFocusErrorInput",
8612
+ "source": "src/equipment/generic-lens-catalogue.ts",
8613
+ "kind": "type"
8614
+ },
8480
8615
  {
8481
8616
  "name": "simulateIdealReference",
8482
8617
  "source": "src/facade/simulate-capture.ts",
@@ -142,7 +142,7 @@ a capture does. `motion.rotation` is a non-zero angular velocity; `sensor.tempor
142
142
 
143
143
  ```ts
144
144
  SIMULATE_CAPTURE_COST_OPTIONS = Object.freeze(["lens.pupilSampling", "lens.projection", "lens.distortion", "lens.vignetting", "lens.psf",
145
- "lens.diffraction", "lens.defocus", "sensor.antiAliasing", "sensor.pixelAperture.box", "sensor.temporalSampleCount", "motion.rotation", "motion.shutter.rolling", "output.borderHandling.mirror", "output.region",
145
+ "lens.diffraction", "lens.defocus", "lens.catalogue", "sensor.antiAliasing", "sensor.pixelAperture.box", "sensor.temporalSampleCount", "motion.rotation", "motion.shutter.rolling", "output.borderHandling.mirror", "output.region",
146
146
  "output.whiteBalance.auto", "output.demosaic.ahd", "output.highlightMode.reconstruct", "output.colorEncoding.wide-gamut", "output.gamutMapping.perceptual",
147
147
  "output.diffractionCorrection", "output.sharpening", "output.format.jpeg", "output.format.png", "output.format.dng"] as const)
148
148
  ```
@@ -514,6 +514,22 @@ export interface SimulateCaptureOptions {
514
514
  * focus distance below its minimum focus distance, is rejected. Not combinable with `focalLengthMm`.
515
515
  */
516
516
  prime?: number;
517
+ /**
518
+ * Opt-in (#549): a lens of the generic lens catalogue by id (`GENERIC_LENS_IDS`, for example "pv-50-1.2-pro" or "pv-50-1.8";
519
+ * docs/LENS_CATALOGUE.md). Like `prime`, its focal length is the full-frame equivalent ÷ the simulated frame's diagonal crop
520
+ * factor and it replaces the tier lens's focal length and f-number range (the transmission is kept). The capture renders the
521
+ * lens's character, all labelled approximations: its PSF (diffraction with the lens's Seidel aberrations, axial and lateral
522
+ * colour, at the resolved f-number and focus) through the environment PSF quadrature, its radial distortion, its vignetting
523
+ * and, for the fisheyes, the equisolid projection. Flare, focus breathing and bokeh shape are not rendered. Not combinable
524
+ * with `prime`, `focalLengthMm`, `projection`, `distortion`, `vignetting`, `psf`, `diffraction` or `pupilSampling` (the
525
+ * lens sets them); `defocus` convolves the disc into the lens's PSF. Not offered by the display view. Default off.
526
+ */
527
+ catalogue?: string;
528
+ /**
529
+ * Opt-in (#549), with `catalogue` only: a seeded autofocus error (`simulateGenericLensFocusError`). The focus distance becomes
530
+ * the sampled one for (seed, shotIndex), reported in the `lens.focusScatter` assumption; it is visible with `defocus`. Default off.
531
+ */
532
+ focusScatter?: { seed: number; shotIndex?: number };
517
533
  /** Aperture for manual and aperture-priority modes. Default 8. */
518
534
  fNumber?: number;
519
535
  /** Focus distance in metres. Default: the subject distance. */
@@ -547,8 +563,13 @@ export interface SimulateCaptureOptions {
547
563
  * kernel is convolved with the disc (a further approximation). In focus the result is that of the same request
548
564
  * without the option, plus the assumption.
549
565
  * Not combinable with `pupilSampling` or `psf`. Default off.
566
+ *
567
+ * Bokeh character (#553), each part off by default (without them the disc is unchanged, byte for byte): `blades` (a
568
+ * polygonal or rounded-blade opening), `sphericalAberration` (primary W040: soft-edged blur on one side of the focus
569
+ * plane, bright-rimmed on the other) and `catsEye` (mechanical vignetting clipping the pupil by field position; the
570
+ * capture only, not with `diffraction`). See `calculateDefocusBokehKernel` and docs/QUICK_START.md, "Bokeh character".
550
571
  */
551
- defocus?: { model: "geometric-disc" };
572
+ defocus?: { model: "geometric-disc" } & DefocusBokehOptions;
552
573
  };
553
574
  exposure?: {
554
575
  /** Default "manual". */
@@ -0,0 +1,145 @@
1
+ # optics/defocus-bokeh-kernel.ts public contracts
2
+
3
+ The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
4
+
5
+ ## calculateDefocusBokehKernel
6
+
7
+ The defocus kernel of `lens.defocus` with bokeh options (#553), on the site lattice, for one field point: the same kernel
8
+ `simulateCapture` and the display view apply (on the field zone's centre with a cat's eye). Pure; for tests, previews and
9
+ lens catalogues. Fails with `InvalidScientificInputError` past {@link DEFOCUS_DISC_KERNEL_MAXIMUM_RADIUS_SITES} (spherical
10
+ aberration can widen the blur beyond the disc) or when the cat's eye closes the pupil, `InvalidConfigurationError` for
11
+ unknown fields. A geometric approximation: see the module comment of `defocus-bokeh-kernel.ts` and docs/QUICK_START.md,
12
+ "Bokeh character".
13
+
14
+ ```ts
15
+ export function calculateDefocusBokehKernel(input: CalculateDefocusBokehKernelInput): CalculationResult<DefocusBokehKernel>;
16
+ ```
17
+
18
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
19
+
20
+ ## CalculateDefocusBokehKernelInput
21
+
22
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
23
+
24
+ ```ts
25
+ export interface CalculateDefocusBokehKernelInput {
26
+ /** Lens focal length, mm. */
27
+ focalLengthMm: number;
28
+ /** Working f-number. */
29
+ aperture: number;
30
+ /** Focus-plane distance, m. */
31
+ focusDistanceM: number;
32
+ /** Scene-plane distance, m. */
33
+ subjectDistanceM: number;
34
+ /** Sensor site pitch, µm: the lattice the kernel is sampled on. */
35
+ pixelPitchMicrometers: number;
36
+ /** The bokeh options, as `lens.defocus` takes them (blades, sphericalAberration, catsEye). */
37
+ bokeh: DefocusBokehOptions;
38
+ /** Field tangent tan θ·(cos φ, sin φ) in output-image axes (+X right, +Y up); only the cat's eye reads it. Default on axis. */
39
+ fieldTangent?: { x: number; y: number };
40
+ }
41
+ ```
42
+
43
+ ## DEFOCUS_BOKEH_MAXIMUM_BLADES
44
+
45
+ Largest blade count of `lens.defocus.blades` (and of {@link DefocusBokehBlades}).
46
+
47
+ ```ts
48
+ DEFOCUS_BOKEH_MAXIMUM_BLADES = 32
49
+ ```
50
+
51
+ ## DefocusBokehBlades
52
+
53
+ Aperture blades: a regular polygon, optionally with rounded blades and open at wide f-numbers.
54
+
55
+ ```ts
56
+ export interface DefocusBokehBlades {
57
+ /** Number of diaphragm blades, 3 to {@link DEFOCUS_BOKEH_MAXIMUM_BLADES}. */
58
+ count: number;
59
+ /** Rotation of the first blade edge, degrees counter-clockwise (`calculateIdealApertureGeometry`'s convention). Default 0. */
60
+ firstBladeEdgeAngleDegrees?: number;
61
+ /** Blade roundness κ in [0, 1]: each edge is an arc of radius 1/κ circumradii (0: straight blades, 1: a circle). Default 0. */
62
+ curvature?: number;
63
+ /** At or below this f-number the blades are open and the opening is circular (the lens's widest aperture). Default: never. */
64
+ circularAtOrBelowFNumber?: number;
65
+ }
66
+ ```
67
+
68
+ ## DefocusBokehCatsEye
69
+
70
+ Mechanical vignetting (cat's eye): the open pupil clipped by each aperture at the field point.
71
+
72
+ ```ts
73
+ export interface DefocusBokehCatsEye {
74
+ /** One through eight clipping apertures. */
75
+ apertures: readonly DefocusBokehClippingAperture[];
76
+ /**
77
+ * `lens.defocus` only: zones across the frame's long side (1 to {@link DEFOCUS_BOKEH_MAXIMUM_FIELD_ZONES}, default
78
+ * {@link DEFOCUS_BOKEH_DEFAULT_FIELD_ZONES}); square zones, each with the kernel of its centre's field point.
79
+ */
80
+ fieldZonesAcrossLongSide?: number;
81
+ }
82
+ ```
83
+
84
+ ## DefocusBokehClippingAperture
85
+
86
+ One clipping aperture of the cat's eye, as `MechanicalVignettingAperture` of `calculateMechanicalVignetting`.
87
+
88
+ ```ts
89
+ export interface DefocusBokehClippingAperture {
90
+ /** Aperture radius referred to the entrance-pupil plane, mm. */
91
+ radiusMm: number;
92
+ /** Shift of its projected centre per unit tan(field angle), mm, toward the field point when positive. */
93
+ shiftPerTanFieldMm: number;
94
+ }
95
+ ```
96
+
97
+ ## DefocusBokehKernel
98
+
99
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
100
+
101
+ ```ts
102
+ export interface DefocusBokehKernel {
103
+ /** "behind-focus" (subject farther than focus), "in-front-of-focus", or "in-focus" (no kernel). */
104
+ side: "behind-focus" | "in-front-of-focus" | "in-focus";
105
+ /** Thin-lens blur diameter c, µm and sites (`calculateDefocusCircle`, thin-lens model 1.1.0). */
106
+ diameterMicrometers: number;
107
+ diameterSites: number;
108
+ /** Kernel radius R and window side 2R + 1 (0 and 1 in focus). */
109
+ kernelRadiusSites: number;
110
+ width: number;
111
+ /** Row-major weights, index (j + R)·width + (i + R) for site offset (i right, j down) from the chief ray; null in focus. */
112
+ weights: Float64Array | null;
113
+ /** Open pupil area / unclipped opening area at the field point (the cat's eye's open fraction; 1 without one). */
114
+ openAreaRatio: number;
115
+ method: "exact-polygon-cell-area" | "pupil-ray-binning" | "none";
116
+ /** Transverse spherical aberration of the marginal ray at the working f-number, sites (8·N·W040(N)/pitch). */
117
+ sphericalAberrationSites: number;
118
+ raySamples: number;
119
+ }
120
+ ```
121
+
122
+ ## DefocusBokehOptions
123
+
124
+ The bokeh options of `lens.defocus` (#553), each off when omitted; a lens catalogue entry can carry them as they are.
125
+
126
+ ```ts
127
+ export interface DefocusBokehOptions {
128
+ blades?: DefocusBokehBlades;
129
+ sphericalAberration?: DefocusBokehSphericalAberration;
130
+ catsEye?: DefocusBokehCatsEye;
131
+ }
132
+ ```
133
+
134
+ ## DefocusBokehSphericalAberration
135
+
136
+ Primary (Seidel) spherical aberration W = W040·ρ⁴, given at a reference f-number.
137
+
138
+ ```ts
139
+ export interface DefocusBokehSphericalAberration {
140
+ /** W040 in µm of optical path at the pupil edge of `atFNumber`; positive = undercorrected (marginal rays focus short). */
141
+ w040Micrometers: number;
142
+ /** The f-number at which `w040Micrometers` is given; the working value is W040·(atFNumber/N)⁴. */
143
+ atFNumber: number;
144
+ }
145
+ ```