@photivra/engine 2.16.0 → 2.18.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 (107) hide show
  1. package/CHANGELOG.md +104 -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/camera/sensor-equivalence.d.ts +156 -0
  10. package/dist/camera/sensor-equivalence.js +208 -0
  11. package/dist/camera/sensor-equivalence.js.map +1 -0
  12. package/dist/capture/native-environment-tabulated.d.ts +5 -0
  13. package/dist/capture/native-environment-tabulated.js +1 -1
  14. package/dist/capture/native-environment-tabulated.js.map +1 -1
  15. package/dist/core/version.d.ts +1 -1
  16. package/dist/core/version.js +1 -1
  17. package/dist/core/version.js.map +1 -1
  18. package/dist/equipment/generic-lens-catalogue.d.ts +227 -0
  19. package/dist/equipment/generic-lens-catalogue.js +243 -0
  20. package/dist/equipment/generic-lens-catalogue.js.map +1 -0
  21. package/dist/equipment/ideal-lens.d.ts +25 -0
  22. package/dist/equipment/ideal-lens.js +21 -0
  23. package/dist/equipment/ideal-lens.js.map +1 -0
  24. package/dist/facade/catalogue-lens-signal.d.ts +94 -0
  25. package/dist/facade/catalogue-lens-signal.js +362 -0
  26. package/dist/facade/catalogue-lens-signal.js.map +1 -0
  27. package/dist/facade/defocus-bokeh-zones.d.ts +33 -0
  28. package/dist/facade/defocus-bokeh-zones.js +53 -0
  29. package/dist/facade/defocus-bokeh-zones.js.map +1 -0
  30. package/dist/facade/defocus-image-space-provider.d.ts +9 -0
  31. package/dist/facade/defocus-image-space-provider.js +101 -9
  32. package/dist/facade/defocus-image-space-provider.js.map +1 -1
  33. package/dist/facade/diffraction-provider.d.ts +29 -0
  34. package/dist/facade/diffraction-provider.js +117 -11
  35. package/dist/facade/diffraction-provider.js.map +1 -1
  36. package/dist/facade/display-view-gpu-session.js +4 -0
  37. package/dist/facade/display-view-gpu-session.js.map +1 -1
  38. package/dist/facade/display-view-gpu.d.ts +10 -0
  39. package/dist/facade/display-view-gpu.js.map +1 -1
  40. package/dist/facade/display-view-lens-signal.d.ts +61 -0
  41. package/dist/facade/display-view-lens-signal.js +502 -0
  42. package/dist/facade/display-view-lens-signal.js.map +1 -0
  43. package/dist/facade/expected-signal-setup.d.ts +15 -0
  44. package/dist/facade/expected-signal-setup.js +19 -2
  45. package/dist/facade/expected-signal-setup.js.map +1 -1
  46. package/dist/facade/expected-signal.d.ts +13 -0
  47. package/dist/facade/expected-signal.js +155 -1
  48. package/dist/facade/expected-signal.js.map +1 -1
  49. package/dist/facade/generic-lens-capture.d.ts +7 -0
  50. package/dist/facade/generic-lens-capture.js +9 -0
  51. package/dist/facade/generic-lens-capture.js.map +1 -0
  52. package/dist/facade/large-defocus-lattice.d.ts +39 -0
  53. package/dist/facade/large-defocus-lattice.js +64 -0
  54. package/dist/facade/large-defocus-lattice.js.map +1 -0
  55. package/dist/facade/scene-radiance.d.ts +5 -0
  56. package/dist/facade/scene-radiance.js +5 -2
  57. package/dist/facade/scene-radiance.js.map +1 -1
  58. package/dist/facade/simulate-capture.d.ts +67 -7
  59. package/dist/facade/simulate-capture.js +400 -64
  60. package/dist/facade/simulate-capture.js.map +1 -1
  61. package/dist/facade/simulate-display-view.js +23 -17
  62. package/dist/facade/simulate-display-view.js.map +1 -1
  63. package/dist/gpu/display-view-quality-gpu.d.ts +0 -9
  64. package/dist/gpu/display-view-quality-gpu.js +6 -0
  65. package/dist/gpu/display-view-quality-gpu.js.map +1 -1
  66. package/dist/gpu/display-view-speed-gpu.js +2 -0
  67. package/dist/gpu/display-view-speed-gpu.js.map +1 -1
  68. package/dist/index.d.ts +6 -1
  69. package/dist/index.js +5 -0
  70. package/dist/index.js.map +1 -1
  71. package/dist/optics/defocus-bokeh-kernel.d.ts +207 -0
  72. package/dist/optics/defocus-bokeh-kernel.js +380 -0
  73. package/dist/optics/defocus-bokeh-kernel.js.map +1 -0
  74. package/dist/optics/defocus-disc-kernel.d.ts +41 -19
  75. package/dist/optics/defocus-disc-kernel.js +44 -23
  76. package/dist/optics/defocus-disc-kernel.js.map +1 -1
  77. package/dist/optics/generic-lens-psf.d.ts +68 -0
  78. package/dist/optics/generic-lens-psf.js +337 -0
  79. package/dist/optics/generic-lens-psf.js.map +1 -0
  80. package/dist/optics/illumination-vignetting.d.ts +6 -0
  81. package/dist/optics/illumination-vignetting.js +24 -7
  82. package/dist/optics/illumination-vignetting.js.map +1 -1
  83. package/dist/optics/sensor-environment-query.d.ts +6 -0
  84. package/dist/optics/sensor-environment-query.js +13 -6
  85. package/dist/optics/sensor-environment-query.js.map +1 -1
  86. package/dist/optics/site-kernel-runs.d.ts +77 -0
  87. package/dist/optics/site-kernel-runs.js +200 -0
  88. package/dist/optics/site-kernel-runs.js.map +1 -0
  89. package/dist/output/generic-lens-corrections.d.ts +24 -0
  90. package/dist/output/generic-lens-corrections.js +51 -0
  91. package/dist/output/generic-lens-corrections.js.map +1 -0
  92. package/docs/AGENT_GUIDE.md +4 -0
  93. package/docs/API_REFERENCE.md +77 -1
  94. package/docs/QUICK_START.md +305 -17
  95. package/docs/USAGE.md +98 -5
  96. package/docs/api/camera-sensor-equivalence.md +209 -0
  97. package/docs/api/core-version.md +1 -1
  98. package/docs/api/equipment-generic-lens-catalogue.md +281 -0
  99. package/docs/api/equipment-ideal-lens.md +26 -0
  100. package/docs/api/exports.json +202 -2
  101. package/docs/api/facade-simulate-capture.md +48 -4
  102. package/docs/api/optics-defocus-bokeh-kernel.md +145 -0
  103. package/docs/api/optics-defocus-disc-kernel.md +22 -15
  104. package/docs/api/optics-sensor-environment-query.md +6 -0
  105. package/docs/api/output-generic-lens-corrections.md +39 -0
  106. package/llms.txt +3 -0
  107. package/package.json +1 -1
@@ -0,0 +1,26 @@
1
+ # equipment/ideal-lens.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
+ ## IDEAL_LENS_LIMITS
6
+
7
+ Ideal lens (#576): the bounds of `simulateCapture`'s and the display view's `lens.ideal`, a plain aberration-free thin lens
8
+ named by its physical focal length, on any sensor. Its blur is that of the options the request gives, the same models as with
9
+ `lens.prime`: `lens.defocus` (the thin-lens blur circle, `calculateDefocusCircle`), `lens.diffraction` (the Airy pattern of a
10
+ circular pupil) and `lens.vignetting` (a caller profile). Without them it renders sharp and evenly lit.
11
+
12
+ - `fNumber.minimum` 0.5 is a physical limit: a lens in air that satisfies the Abbe sine condition (aplanatic) has
13
+ N = 1/(2·n·sin θ′) with n = 1 and sin θ′ ≤ 1, so N ≥ 0.5 (the sine condition: Born & Wolf, *Principles of Optics*, 7th ed.;
14
+ Kingslake & Johnson, *Lens Design Fundamentals*, 2nd ed.). The engine's thin-lens defocus and scalar Airy diffraction
15
+ are paraxial models, so near f/0.5 they are approximations further from a real lens than at f/2.
16
+ - `fNumber.maximum` 128 and both focal-length bounds are engine bounds, not physical limits: 1 mm is shorter than phone
17
+ camera lenses, 4000 mm longer than photographic lenses, and f/128 beyond large-format lenses' smallest stops.
18
+ - The ideal lens's f-number range is the whole of `fNumber`: manual and aperture priority take any `lens.fNumber` in it, and
19
+ shutter priority and program may choose any aperture in it (in dim light, wider than real lenses open).
20
+
21
+ ```ts
22
+ IDEAL_LENS_LIMITS = Object.freeze({
23
+ focalLengthMm: Object.freeze({ minimum: 1, maximum: 4000 }),
24
+ fNumber: Object.freeze({ minimum: 0.5, maximum: 128 })
25
+ })
26
+ ```
@@ -1,6 +1,6 @@
1
1
  {
2
- "packageVersion": "2.16.0",
3
- "engineApiVersion": "2.16.0",
2
+ "packageVersion": "2.18.0",
3
+ "engineApiVersion": "2.18.0",
4
4
  "exports": [
5
5
  {
6
6
  "name": "AbsoluteUncertaintyEstimate",
@@ -282,6 +282,16 @@
282
282
  "source": "src/sensor/local-exposure-binding.ts",
283
283
  "kind": "type"
284
284
  },
285
+ {
286
+ "name": "BlurShareOfFrame",
287
+ "source": "src/camera/sensor-equivalence.ts",
288
+ "kind": "type"
289
+ },
290
+ {
291
+ "name": "BlurShareOfPrint",
292
+ "source": "src/camera/sensor-equivalence.ts",
293
+ "kind": "type"
294
+ },
285
295
  {
286
296
  "name": "BOLTZMANN_CONSTANT_JOULES_PER_KELVIN",
287
297
  "source": "src/color/illuminants.ts",
@@ -367,6 +377,16 @@
367
377
  "source": "src/facade/chart-scenes.ts",
368
378
  "kind": "runtime"
369
379
  },
380
+ {
381
+ "name": "calculateBlurShareOfFrame",
382
+ "source": "src/camera/sensor-equivalence.ts",
383
+ "kind": "runtime"
384
+ },
385
+ {
386
+ "name": "CalculateBlurShareOfFrameInput",
387
+ "source": "src/camera/sensor-equivalence.ts",
388
+ "kind": "type"
389
+ },
370
390
  {
371
391
  "name": "calculateBrownConradyDistortionMapping",
372
392
  "source": "src/optics/brown-conrady-distortion.ts",
@@ -562,6 +582,16 @@
562
582
  "source": "src/color/illuminants.ts",
563
583
  "kind": "runtime"
564
584
  },
585
+ {
586
+ "name": "calculateDefocusBokehKernel",
587
+ "source": "src/optics/defocus-bokeh-kernel.ts",
588
+ "kind": "runtime"
589
+ },
590
+ {
591
+ "name": "CalculateDefocusBokehKernelInput",
592
+ "source": "src/optics/defocus-bokeh-kernel.ts",
593
+ "kind": "type"
594
+ },
565
595
  {
566
596
  "name": "calculateDefocusCircle",
567
597
  "source": "src/optics/depth-of-field.ts",
@@ -1277,6 +1307,16 @@
1277
1307
  "source": "src/sensor/eqe-temporal-exposure.ts",
1278
1308
  "kind": "type"
1279
1309
  },
1310
+ {
1311
+ "name": "calculateSensorEquivalence",
1312
+ "source": "src/camera/sensor-equivalence.ts",
1313
+ "kind": "runtime"
1314
+ },
1315
+ {
1316
+ "name": "CalculateSensorEquivalenceInput",
1317
+ "source": "src/camera/sensor-equivalence.ts",
1318
+ "kind": "type"
1319
+ },
1280
1320
  {
1281
1321
  "name": "calculateSensorFixedPatternSite",
1282
1322
  "source": "src/sensor/fixed-pattern-noise.ts",
@@ -2432,6 +2472,16 @@
2432
2472
  "source": "src/equipment/generic-tier-presets.ts",
2433
2473
  "kind": "runtime"
2434
2474
  },
2475
+ {
2476
+ "name": "createGenericLensCorrectionProfile",
2477
+ "source": "src/output/generic-lens-corrections.ts",
2478
+ "kind": "runtime"
2479
+ },
2480
+ {
2481
+ "name": "CreateGenericLensCorrectionProfileInput",
2482
+ "source": "src/output/generic-lens-corrections.ts",
2483
+ "kind": "type"
2484
+ },
2435
2485
  {
2436
2486
  "name": "createHdrBracketMergeTask",
2437
2487
  "source": "src/processing/hdr.ts",
@@ -2702,11 +2752,46 @@
2702
2752
  "source": "src/sensor/capture-mode.ts",
2703
2753
  "kind": "type"
2704
2754
  },
2755
+ {
2756
+ "name": "DEFOCUS_BOKEH_MAXIMUM_BLADES",
2757
+ "source": "src/optics/defocus-bokeh-kernel.ts",
2758
+ "kind": "runtime"
2759
+ },
2705
2760
  {
2706
2761
  "name": "DEFOCUS_DISC_KERNEL_MAXIMUM_RADIUS_SITES",
2707
2762
  "source": "src/optics/defocus-disc-kernel.ts",
2708
2763
  "kind": "runtime"
2709
2764
  },
2765
+ {
2766
+ "name": "DefocusBokehBlades",
2767
+ "source": "src/optics/defocus-bokeh-kernel.ts",
2768
+ "kind": "type"
2769
+ },
2770
+ {
2771
+ "name": "DefocusBokehCatsEye",
2772
+ "source": "src/optics/defocus-bokeh-kernel.ts",
2773
+ "kind": "type"
2774
+ },
2775
+ {
2776
+ "name": "DefocusBokehClippingAperture",
2777
+ "source": "src/optics/defocus-bokeh-kernel.ts",
2778
+ "kind": "type"
2779
+ },
2780
+ {
2781
+ "name": "DefocusBokehKernel",
2782
+ "source": "src/optics/defocus-bokeh-kernel.ts",
2783
+ "kind": "type"
2784
+ },
2785
+ {
2786
+ "name": "DefocusBokehOptions",
2787
+ "source": "src/optics/defocus-bokeh-kernel.ts",
2788
+ "kind": "type"
2789
+ },
2790
+ {
2791
+ "name": "DefocusBokehSphericalAberration",
2792
+ "source": "src/optics/defocus-bokeh-kernel.ts",
2793
+ "kind": "type"
2794
+ },
2710
2795
  {
2711
2796
  "name": "DefocusCircle",
2712
2797
  "source": "src/optics/depth-of-field.ts",
@@ -2902,6 +2987,16 @@
2902
2987
  "source": "src/capture/environment-raw-producer.ts",
2903
2988
  "kind": "type"
2904
2989
  },
2990
+ {
2991
+ "name": "EquivalenceExactness",
2992
+ "source": "src/camera/sensor-equivalence.ts",
2993
+ "kind": "type"
2994
+ },
2995
+ {
2996
+ "name": "EquivalenceValueBasis",
2997
+ "source": "src/camera/sensor-equivalence.ts",
2998
+ "kind": "type"
2999
+ },
2905
3000
  {
2906
3001
  "name": "EquivalentFocalLength35Mm",
2907
3002
  "source": "src/camera/equivalent-focal-length.ts",
@@ -3482,6 +3577,26 @@
3482
3577
  "source": "src/sensor/iso-signal-chain.ts",
3483
3578
  "kind": "runtime"
3484
3579
  },
3580
+ {
3581
+ "name": "GENERIC_LENS_CATALOGUE",
3582
+ "source": "src/equipment/generic-lens-catalogue.ts",
3583
+ "kind": "runtime"
3584
+ },
3585
+ {
3586
+ "name": "GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM",
3587
+ "source": "src/output/generic-lens-corrections.ts",
3588
+ "kind": "runtime"
3589
+ },
3590
+ {
3591
+ "name": "GENERIC_LENS_IDS",
3592
+ "source": "src/equipment/generic-lens-catalogue.ts",
3593
+ "kind": "runtime"
3594
+ },
3595
+ {
3596
+ "name": "GENERIC_LENS_SET_BASELINES",
3597
+ "source": "src/equipment/generic-lens-catalogue.ts",
3598
+ "kind": "runtime"
3599
+ },
3485
3600
  {
3486
3601
  "name": "GENERIC_PRIME_EQUIVALENT_FOCAL_LENGTHS_MM",
3487
3602
  "source": "src/equipment/generic-prime-lenses.ts",
@@ -3587,6 +3702,26 @@
3587
3702
  "source": "src/sensor/iso-signal-chain.ts",
3588
3703
  "kind": "type"
3589
3704
  },
3705
+ {
3706
+ "name": "GenericLensAdjustments",
3707
+ "source": "src/equipment/generic-lens-catalogue.ts",
3708
+ "kind": "type"
3709
+ },
3710
+ {
3711
+ "name": "GenericLensCatalogueEntry",
3712
+ "source": "src/equipment/generic-lens-catalogue.ts",
3713
+ "kind": "type"
3714
+ },
3715
+ {
3716
+ "name": "GenericLensCharacter",
3717
+ "source": "src/equipment/generic-lens-catalogue.ts",
3718
+ "kind": "type"
3719
+ },
3720
+ {
3721
+ "name": "GenericLensCharacterValues",
3722
+ "source": "src/equipment/generic-lens-catalogue.ts",
3723
+ "kind": "type"
3724
+ },
3590
3725
  {
3591
3726
  "name": "GenericLensCorrectionProfile",
3592
3727
  "source": "src/output/lens-corrections.ts",
@@ -3597,6 +3732,21 @@
3597
3732
  "source": "src/equipment/exposure-capabilities.ts",
3598
3733
  "kind": "type"
3599
3734
  },
3735
+ {
3736
+ "name": "GenericLensFocusError",
3737
+ "source": "src/equipment/generic-lens-catalogue.ts",
3738
+ "kind": "type"
3739
+ },
3740
+ {
3741
+ "name": "GenericLensKind",
3742
+ "source": "src/equipment/generic-lens-catalogue.ts",
3743
+ "kind": "type"
3744
+ },
3745
+ {
3746
+ "name": "GenericLensSet",
3747
+ "source": "src/equipment/generic-lens-catalogue.ts",
3748
+ "kind": "type"
3749
+ },
3600
3750
  {
3601
3751
  "name": "GenericOpticalEvidence",
3602
3752
  "source": "src/optics/profile-contract.ts",
@@ -3782,6 +3932,11 @@
3782
3932
  "source": "src/facade/chart-scenes.ts",
3783
3933
  "kind": "type"
3784
3934
  },
3935
+ {
3936
+ "name": "IDEAL_LENS_LIMITS",
3937
+ "source": "src/equipment/ideal-lens.ts",
3938
+ "kind": "runtime"
3939
+ },
3785
3940
  {
3786
3941
  "name": "IdealApertureGeometry",
3787
3942
  "source": "src/optics/aperture.ts",
@@ -6622,6 +6777,11 @@
6622
6777
  "source": "src/sensor/iso-signal-chain.ts",
6623
6778
  "kind": "type"
6624
6779
  },
6780
+ {
6781
+ "name": "ResolvedGenericLens",
6782
+ "source": "src/equipment/generic-lens-catalogue.ts",
6783
+ "kind": "type"
6784
+ },
6625
6785
  {
6626
6786
  "name": "ResolvedGenericTierLensProfiles",
6627
6787
  "source": "src/equipment/generic-tier-presets.ts",
@@ -6782,6 +6942,16 @@
6782
6942
  "source": "src/sensor/iso-signal-chain.ts",
6783
6943
  "kind": "runtime"
6784
6944
  },
6945
+ {
6946
+ "name": "resolveGenericLens",
6947
+ "source": "src/equipment/generic-lens-catalogue.ts",
6948
+ "kind": "runtime"
6949
+ },
6950
+ {
6951
+ "name": "ResolveGenericLensInput",
6952
+ "source": "src/equipment/generic-lens-catalogue.ts",
6953
+ "kind": "type"
6954
+ },
6785
6955
  {
6786
6956
  "name": "resolveGenericPrimeLens",
6787
6957
  "source": "src/equipment/generic-prime-lenses.ts",
@@ -7702,6 +7872,21 @@
7702
7872
  "source": "src/sensor/eqe-electron-rate.ts",
7703
7873
  "kind": "type"
7704
7874
  },
7875
+ {
7876
+ "name": "SensorEquivalence",
7877
+ "source": "src/camera/sensor-equivalence.ts",
7878
+ "kind": "type"
7879
+ },
7880
+ {
7881
+ "name": "SensorEquivalenceFieldOfView",
7882
+ "source": "src/camera/sensor-equivalence.ts",
7883
+ "kind": "type"
7884
+ },
7885
+ {
7886
+ "name": "SensorEquivalenceQuantity",
7887
+ "source": "src/camera/sensor-equivalence.ts",
7888
+ "kind": "type"
7889
+ },
7705
7890
  {
7706
7891
  "name": "SensorExpectedReadoutSignal",
7707
7892
  "source": "src/sensor/raw-readout.ts",
@@ -8387,6 +8572,11 @@
8387
8572
  "source": "src/facade/simulate-capture.ts",
8388
8573
  "kind": "type"
8389
8574
  },
8575
+ {
8576
+ "name": "SimulateCaptureLensCatalogueRecord",
8577
+ "source": "src/facade/simulate-capture.ts",
8578
+ "kind": "type"
8579
+ },
8390
8580
  {
8391
8581
  "name": "SimulateCaptureLensDistortion",
8392
8582
  "source": "src/facade/simulate-capture.ts",
@@ -8477,6 +8667,16 @@
8477
8667
  "source": "src/capture/environment-raw-producer.ts",
8478
8668
  "kind": "type"
8479
8669
  },
8670
+ {
8671
+ "name": "simulateGenericLensFocusError",
8672
+ "source": "src/equipment/generic-lens-catalogue.ts",
8673
+ "kind": "runtime"
8674
+ },
8675
+ {
8676
+ "name": "SimulateGenericLensFocusErrorInput",
8677
+ "source": "src/equipment/generic-lens-catalogue.ts",
8678
+ "kind": "type"
8679
+ },
8480
8680
  {
8481
8681
  "name": "simulateIdealReference",
8482
8682
  "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
  ```
@@ -173,7 +173,7 @@ the record form with `fast` and is reported in `diagnostics.fast.applied` when i
173
173
  whether or not it has a fast method yet; one without runs exact and reports `false`.
174
174
 
175
175
  ```ts
176
- SIMULATE_CAPTURE_FAST_OPTIONS = Object.freeze(["lens.diffraction", "sensor.antiAliasing", "sensor.pixelAperture.box", "output.demosaic.ahd"] as const)
176
+ SIMULATE_CAPTURE_FAST_OPTIONS = Object.freeze(["lens.diffraction", "lens.catalogue", "sensor.antiAliasing", "sensor.pixelAperture.box", "output.demosaic.ahd"] as const)
177
177
  ```
178
178
 
179
179
  ## SIMULATE_CAPTURE_METERING_MODES
@@ -459,6 +459,14 @@ export interface SimulateCaptureFastReport {
459
459
  }
460
460
  ```
461
461
 
462
+ ## SimulateCaptureLensCatalogueRecord
463
+
464
+ `lens.catalogue` as a record (#568): the lens id as `kind` and the `fast` override (see {@link SimulateCaptureOptions.fast}).
465
+
466
+ ```ts
467
+ export interface SimulateCaptureLensCatalogueRecord { kind: string; fast?: boolean }
468
+ ```
469
+
462
470
  ## SimulateCaptureLensDistortion
463
471
 
464
472
  The exact contract is declared below; use the domain guide for assumptions and staged integration.
@@ -514,6 +522,37 @@ export interface SimulateCaptureOptions {
514
522
  * focus distance below its minimum focus distance, is rejected. Not combinable with `focalLengthMm`.
515
523
  */
516
524
  prime?: number;
525
+ /**
526
+ * Opt-in (#576): an ideal lens, an aberration-free thin lens of this physical focal length (1–4000 mm) on whatever sensor the
527
+ * frame is (output width × height × pixel pitch; no crop-factor scaling), at any `fNumber` from f/0.5 to f/128
528
+ * (`IDEAL_LENS_LIMITS`; beyond them the request is refused). It replaces the tier lens's focal length and f-number range (the
529
+ * transmission is kept); shutter priority and program may choose any aperture in that range. Recorded in the `lens.ideal`
530
+ * assumption. Its blur is that of `defocus`, `diffraction` and `vignetting`, the same models as with `prime`. It focuses at any
531
+ * distance beyond the focal length (no minimum focus distance; `subjectDistanceM` must be at least twice the focal length, as for
532
+ * every lens). Works in `simulateCapture` and the display view. Not combinable with `prime`, `catalogue` or `focalLengthMm`.
533
+ * Optional `widestFNumber` and `narrowestFNumber` (within `IDEAL_LENS_LIMITS`, widest ≤ narrowest; each defaults to its bound)
534
+ * narrow the range: `fNumber` must lie in it, and shutter priority and program choose apertures only in it. Default off.
535
+ */
536
+ ideal?: { focalLengthMm: number; widestFNumber?: number; narrowestFNumber?: number };
537
+ /**
538
+ * 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";
539
+ * docs/LENS_CATALOGUE.md). Like `prime`, its focal length is the full-frame equivalent ÷ the simulated frame's diagonal crop
540
+ * factor and it replaces the tier lens's focal length and f-number range (the transmission is kept). The capture renders the
541
+ * lens's character, all labelled approximations: its PSF (diffraction with the lens's Seidel aberrations, axial and lateral
542
+ * colour, at the resolved f-number and focus) through the environment PSF quadrature, its radial distortion, its vignetting
543
+ * and, for the fisheyes, the equisolid projection. Flare, focus breathing and bokeh shape are not rendered. Not combinable
544
+ * with `prime`, `focalLengthMm`, `projection`, `distortion`, `vignetting`, `psf`, `diffraction` or `pupilSampling` (the
545
+ * lens sets them); `defocus` convolves the disc into the lens's PSF. The display view renders it with the PSF taken per field zone (#569; docs/DISPLAY_VIEW.md). Default off.
546
+ * #568: the record form `{ kind: <lens id>, fast }` overrides the top-level `fast` flag for this option; the fast method
547
+ * blends the PSF between exactly resolved kernels on a field grid and tabulates each site's expected signal
548
+ * (docs/LENS_CATALOGUE.md, "Fast method").
549
+ */
550
+ catalogue?: string | SimulateCaptureLensCatalogueRecord;
551
+ /**
552
+ * Opt-in (#549), with `catalogue` only: a seeded autofocus error (`simulateGenericLensFocusError`). The focus distance becomes
553
+ * the sampled one for (seed, shotIndex), reported in the `lens.focusScatter` assumption; it is visible with `defocus`. Default off.
554
+ */
555
+ focusScatter?: { seed: number; shotIndex?: number };
517
556
  /** Aperture for manual and aperture-priority modes. Default 8. */
518
557
  fNumber?: number;
519
558
  /** Focus distance in metres. Default: the subject distance. */
@@ -547,8 +586,13 @@ export interface SimulateCaptureOptions {
547
586
  * kernel is convolved with the disc (a further approximation). In focus the result is that of the same request
548
587
  * without the option, plus the assumption.
549
588
  * Not combinable with `pupilSampling` or `psf`. Default off.
589
+ *
590
+ * Bokeh character (#553), each part off by default (without them the disc is unchanged, byte for byte): `blades` (a
591
+ * polygonal or rounded-blade opening), `sphericalAberration` (primary W040: soft-edged blur on one side of the focus
592
+ * plane, bright-rimmed on the other) and `catsEye` (mechanical vignetting clipping the pupil by field position; the
593
+ * capture only, not with `diffraction`). See `calculateDefocusBokehKernel` and docs/QUICK_START.md, "Bokeh character".
550
594
  */
551
- defocus?: { model: "geometric-disc" };
595
+ defocus?: { model: "geometric-disc" } & DefocusBokehOptions;
552
596
  };
553
597
  exposure?: {
554
598
  /** Default "manual". */
@@ -727,7 +771,7 @@ export interface SimulateCaptureOptions {
727
771
  * Exact or fast (#395). `false` or omitted: every option runs its exact method, the engine reference. `true`: each option
728
772
  * listed in {@link SIMULATE_CAPTURE_FAST_OPTIONS} runs its fast method where it has one, a measured, bounded approximation of
729
773
  * the exact one; an option without a fast method runs exact. An option's own record `fast` overrides this for that option
730
- * only. `diagnostics.fast` reports what was requested and what ran. No option has a fast method yet, so every result is exact.
774
+ * only. `diagnostics.fast` reports what was requested and what ran. `lens.catalogue` has a fast method (#568); the other options run exact.
731
775
  */
732
776
  fast?: boolean;
733
777
  /** Aborting cancels the running task; the promise rejects with a domain error and returns no partial image. */
@@ -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
+ ```
@@ -6,14 +6,15 @@ The package and root API versions are recorded once in [Navigation](../API_REFER
6
6
 
7
7
  Checks, before rendering, whether `lens.defocus: { model: "geometric-disc" }` can render a request: the geometric
8
8
  defocus disc's diameter on the site lattice, the kernel radius it needs, and whether that radius is within
9
- {@link DEFOCUS_DISC_KERNEL_MAXIMUM_RADIUS_SITES} (#532). Pure and cheap: no kernel is built and nothing is rendered.
9
+ {@link DEFOCUS_DISC_KERNEL_MAXIMUM_RADIUS_SITES} (#532; 512 sites since 2.17.0, #564), and which convolution the renderers
10
+ would use. Pure and cheap: no kernel is built and nothing is rendered.
10
11
 
11
12
  It runs the same resolution and the same limit test as `simulateCapture`, `simulateDisplayView` and `prepareDisplayView`
12
13
  (the blur circle c = f²·|s − d| / (N·d·(s − f)) of `calculateDefocusCircle`, thin-lens model 1.1.0, divided by the pitch),
13
14
  so for the same focal length, f-number, distances and pitch a request they refuse for its defocus size has
14
15
  `withinLimit: false`, and one they accept has `withinLimit: true`. Pass the values the capture uses: in an automatic
15
- exposure mode that is the resolved f-number. The limit applies to the disc alone; with `lens.diffraction` the combined
16
- kernel (diffraction radius plus defocus radius, at most 32 + 64 sites) has no further limit. Other reasons a capture
16
+ exposure mode that is the resolved f-number. The limit applies to the disc alone; with `lens.diffraction` the diffraction
17
+ kernel (at most 32 sites) is applied with it and has no further limit. Other reasons a capture
17
18
  refuses `lens.defocus` out of focus (pupil sampling, a caller PSF, camera rotation with a rolling shutter, a vignetting or
18
19
  distortion profile that does not reach the kernel's radius beyond the frame, and in the display view a projection,
19
20
  distortion, vignetting or rotation) are not checked here.
@@ -49,18 +50,19 @@ export interface CalculateDefocusDiscKernelRadiusInput {
49
50
 
50
51
  ## DEFOCUS_DISC_KERNEL_MAXIMUM_RADIUS_SITES
51
52
 
52
- Largest kernel radius in sites of `lens.defocus: { model: "geometric-disc" }`, R ≤ 64: a disc of at most 129 sites
53
- across (a 129 × 129 window, 16 641 taps per site; the diffraction kernels stop at 32). `simulateCapture`,
54
- `simulateDisplayView` and `prepareDisplayView` refuse a request whose disc needs more, and never clip the disc; check a
55
- request first with {@link calculateDefocusDiscKernelRadius}.
53
+ Largest kernel radius in sites of `lens.defocus: { model: "geometric-disc" }`, R ≤ 512 (#564, 2.17.0; 64 before): a disc of
54
+ at most 1025 sites across (6.15 mm at 6 µm). `simulateCapture`, `simulateDisplayView` and `prepareDisplayView` refuse a
55
+ request whose disc needs more, and never clip the disc; check a request first with {@link calculateDefocusDiscKernelRadius}.
56
56
 
57
- Set from the cost measured in the capture (#492, step 2a; docs/QUICK_START.md, "Optics"): the capture's time grows with
58
- R² at every site the disc sees an edge from, and a 0.7 MP Siemens star took 0.37 s in focus, 2.9 s at R = 32, 4.7 s at
59
- R = 41 and 11.6 s at R = 64 (Node, one thread). 64 keeps the issue's 1.4 µm example (50 mm f/2.8, subject 2 m, focus
60
- 1.6 m: R = 41) with room for larger blurs, at a cost the caller chooses and the docs state; past it the call fails.
57
+ Up to 64 sites the disc is convolved directly ((2R + 1)² taps per site,
58
+ byte-identical to 2.16.0). Above it the renderers use the exact run convolution (`site-kernel-runs.ts`: one prefix-sum
59
+ difference per kernel row plus the cells the circle crosses, about 12·R reads per site), whose cost grows with R instead of
60
+ R². 512 bounds that cost (6,145 reads per site, below the 16,641 taps of the direct gather at 64), the kernel's own
61
+ memory ((2R + 1)² doubles, 8.4 MB) and each tile's source ((64 + 2R)² doubles per field, 9.5 MB): measured in
62
+ docs/QUICK_START.md, "Optics".
61
63
 
62
64
  ```ts
63
- DEFOCUS_DISC_KERNEL_MAXIMUM_RADIUS_SITES = 64
65
+ DEFOCUS_DISC_KERNEL_MAXIMUM_RADIUS_SITES = 512
64
66
  ```
65
67
 
66
68
  ## DefocusDiscKernelRadius
@@ -75,13 +77,18 @@ export interface DefocusDiscKernelRadius {
75
77
  diameterSites: number;
76
78
  /** Kernel radius R in sites the renderer needs for this disc, ⌈c / (2·pitch) − ½⌉ (0 for a disc inside one site, and in focus). */
77
79
  kernelRadiusSites: number;
78
- /** {@link DEFOCUS_DISC_KERNEL_MAXIMUM_RADIUS_SITES}, 64. */
80
+ /** {@link DEFOCUS_DISC_KERNEL_MAXIMUM_RADIUS_SITES}, 512 (64 before 2.17.0). */
79
81
  maximumKernelRadiusSites: number;
80
- /** Largest disc diameter in sites within the limit, 2·64 + 1 = 129: within the limit exactly when `diameterSites` ≤ 129. */
82
+ /** Largest disc diameter in sites within the limit, 2·512 + 1 = 1025: within the limit exactly when `diameterSites` ≤ 1025. */
81
83
  maximumDiameterSites: number;
82
84
  /** True when `kernelRadiusSites` ≤ `maximumKernelRadiusSites`: `lens.defocus` renders the request; false: it refuses it. */
83
85
  withinLimit: boolean;
84
- /** How far past the limit the kernel radius is, max(0, R − 64) sites; 0 when within the limit. */
86
+ /** How far past the limit the kernel radius is, max(0, R − 512) sites; 0 when within the limit. */
85
87
  excessKernelRadiusSites: number;
88
+ /**
89
+ * How the renderers convolve this disc (#564): `"direct"` for a kernel radius up to 64 sites (as in 2.16.0; also in focus),
90
+ * `"runs"` above it (the exact run convolution, cost about proportional to R per site), null past the limit.
91
+ */
92
+ convolution: "direct" | "runs" | null;
86
93
  }
87
94
  ```