@photivra/engine 2.17.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.
- package/CHANGELOG.md +49 -1
- package/CITATION.cff +1 -1
- package/README.md +6 -6
- package/SECURITY.md +1 -1
- package/dist/camera/sensor-equivalence.d.ts +156 -0
- package/dist/camera/sensor-equivalence.js +208 -0
- package/dist/camera/sensor-equivalence.js.map +1 -0
- package/dist/capture/native-environment-tabulated.d.ts +5 -0
- package/dist/capture/native-environment-tabulated.js +1 -1
- package/dist/capture/native-environment-tabulated.js.map +1 -1
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/dist/core/version.js.map +1 -1
- package/dist/equipment/ideal-lens.d.ts +25 -0
- package/dist/equipment/ideal-lens.js +21 -0
- package/dist/equipment/ideal-lens.js.map +1 -0
- package/dist/facade/catalogue-lens-signal.d.ts +94 -0
- package/dist/facade/catalogue-lens-signal.js +362 -0
- package/dist/facade/catalogue-lens-signal.js.map +1 -0
- package/dist/facade/defocus-image-space-provider.js +5 -2
- package/dist/facade/defocus-image-space-provider.js.map +1 -1
- package/dist/facade/display-view-lens-signal.d.ts +32 -1
- package/dist/facade/display-view-lens-signal.js +85 -10
- package/dist/facade/display-view-lens-signal.js.map +1 -1
- package/dist/facade/expected-signal-setup.d.ts +8 -0
- package/dist/facade/expected-signal-setup.js +7 -0
- package/dist/facade/expected-signal-setup.js.map +1 -1
- package/dist/facade/expected-signal.d.ts +5 -0
- package/dist/facade/expected-signal.js +5 -1
- package/dist/facade/expected-signal.js.map +1 -1
- package/dist/facade/generic-lens-capture.d.ts +1 -0
- package/dist/facade/generic-lens-capture.js +1 -0
- package/dist/facade/generic-lens-capture.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +31 -4
- package/dist/facade/simulate-capture.js +117 -38
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.js +1 -1
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/docs/AGENT_GUIDE.md +3 -0
- package/docs/API_REFERENCE.md +29 -1
- package/docs/QUICK_START.md +49 -1
- package/docs/USAGE.md +43 -2
- package/docs/api/camera-sensor-equivalence.md +209 -0
- package/docs/api/core-version.md +1 -1
- package/docs/api/equipment-ideal-lens.md +26 -0
- package/docs/api/exports.json +67 -2
- package/docs/api/facade-simulate-capture.md +27 -4
- package/llms.txt +2 -0
- package/package.json +1 -1
package/docs/USAGE.md
CHANGED
|
@@ -374,6 +374,8 @@ The actual focal length stays the physical optical quantity used by projection a
|
|
|
374
374
|
- Focus distance is not part of conventional 35 mm-equivalent focal length. A later digital/output crop and the output resolution are also excluded.
|
|
375
375
|
- If a product wants to describe the final digital framing, it should label it as output framing. It should not silently replace the optical focal length or the capture-equivalent focal length.
|
|
376
376
|
|
|
377
|
+
To render a lens by its physical focal length on any sensor, give `simulateCapture()` or the display view `lens.ideal: { focalLengthMm }` with any `lens.fNumber` within `IDEAL_LENS_LIMITS` (f/0.5–f/128, 1–4000 mm; optional `widestFNumber` and `narrowestFNumber` narrow the f-number range for the automatic exposure modes): an aberration-free thin lens that is not scaled by the crop factor, unlike `lens.prime` (see docs/QUICK_START.md, "Ideal lens at any focal length and f-number").
|
|
378
|
+
|
|
377
379
|
`calculateImagingAreaMetrics()` provides the same canonical diagonal crop-factor calculation, independent of raster density, so other engine modules do not need to repeat that formula.
|
|
378
380
|
|
|
379
381
|
## Focus-breathing projection
|
|
@@ -7479,8 +7481,9 @@ own designs, not real products). `resolveGenericLens` resolves one for a sensor,
|
|
|
7479
7481
|
colour, distortion and vignetting profiles, flare, breathing and focus scatter, each a labelled approximation.
|
|
7480
7482
|
`simulateGenericLensFocusError` draws a seeded autofocus error; `createGenericLensCorrectionProfile` builds the lens's generic
|
|
7481
7483
|
correction profile for `resolveLensCorrectionPlan`. `simulateCapture` renders a catalogue lens with `lens.catalogue`
|
|
7482
|
-
([quick start](QUICK_START.md#generic-lens-catalogue-549))
|
|
7483
|
-
|
|
7484
|
+
([quick start](QUICK_START.md#generic-lens-catalogue-549)); `fast: true` (or `lens: { catalogue: { kind, fast: true } }`) takes its
|
|
7485
|
+
fast method (#568), a labeled approximation that blends the PSF over a field grid, for interactive captures. Equations, tables and the
|
|
7486
|
+
chart measurements: [LENS_CATALOGUE.md](https://github.com/Photivra/photivra/blob/main/docs/LENS_CATALOGUE.md).
|
|
7484
7487
|
|
|
7485
7488
|
```ts
|
|
7486
7489
|
import { GENERIC_LENS_CATALOGUE, resolveGenericLens, simulateGenericLensFocusError } from "@photivra/engine";
|
|
@@ -7999,3 +8002,41 @@ import { simulateCapture } from "@photivra/engine";
|
|
|
7999
8002
|
const rolling = await simulateCapture({ scene: { preset: "checkerboard" }, motion: { angularVelocityRadPerSec: { pitch: 0.02 }, shutter: "rolling" }, output: { width: 6, height: 6 } });
|
|
8000
8003
|
console.log(rolling.request.siteInput(7 * 8).charge.completenessProfile.startOffsetSecondsFromOpeningReference);
|
|
8001
8004
|
```
|
|
8005
|
+
|
|
8006
|
+
## Equivalent f-number, sensor area and blur as a share of the frame
|
|
8007
|
+
|
|
8008
|
+
`calculateSensorEquivalence()` says what a lens on one sensor corresponds to on a 36 × 24 mm frame (or on a `reference` you pass). Call it once per sensor to fill a lens table:
|
|
8009
|
+
|
|
8010
|
+
```ts
|
|
8011
|
+
import { calculateBlurShareOfFrame, calculateSensorEquivalence } from "@photivra/engine";
|
|
8012
|
+
|
|
8013
|
+
const apsC = calculateSensorEquivalence({
|
|
8014
|
+
sensor: { widthMm: 23.5, heightMm: 15.6 },
|
|
8015
|
+
focalLengthMm: 50,
|
|
8016
|
+
fNumber: 2
|
|
8017
|
+
}).value;
|
|
8018
|
+
|
|
8019
|
+
console.log(apsC.cropFactor); // ≈ 1.53
|
|
8020
|
+
console.log(apsC.equivalentFocalLengthMm); // ≈ 76.7 (framing)
|
|
8021
|
+
console.log(apsC.equivalentFNumber); // ≈ 3.07 (depth of field, blur and total light; not exposure)
|
|
8022
|
+
console.log(apsC.exposureFNumber); // 2 (exposure does not change with sensor size)
|
|
8023
|
+
console.log(apsC.areaRatio); // ≈ 0.42 of the full-frame area
|
|
8024
|
+
console.log(apsC.totalLightDifferenceStops); // ≈ -1.24 stops at the same f-number and shutter
|
|
8025
|
+
console.log(apsC.basis.equivalentFNumber.kind); // "approximation"
|
|
8026
|
+
|
|
8027
|
+
const share = calculateBlurShareOfFrame({
|
|
8028
|
+
blurCircleMm: 0.03,
|
|
8029
|
+
sensor: { widthMm: 36, heightMm: 24 },
|
|
8030
|
+
print: { widthMm: 300, heightMm: 200 }
|
|
8031
|
+
}).value;
|
|
8032
|
+
|
|
8033
|
+
console.log(share.shareOfDiagonal); // ≈ 0.00069 of the frame diagonal
|
|
8034
|
+
console.log(share.print?.blurOnPrintMm); // 0.25 mm on the print
|
|
8035
|
+
```
|
|
8036
|
+
|
|
8037
|
+
- `basis` gives, for every number, `kind: "exact"` or `"approximation"` and the conditions. Crop factor, equivalent focal length, area ratio, total-light difference, entrance pupil and angles of view are exact under their stated conditions.
|
|
8038
|
+
- The equivalent f-number (crop factor × f-number) assumes the same framing, subject distance and output size. It is a thin-lens approximation that is exact only when the focus distance is much greater than the focal length; at close focus the blur shares match at `k·N·(s − f)/(s − k·f)`.
|
|
8039
|
+
- Exposure uses the f-number on the lens. Equivalence never changes shutter, ISO or exposure.
|
|
8040
|
+
- Equal shares of the frame diagonal look the same when images are shown at the same size. `calculateBlurShareOfFrame()` does not judge whether a blur is visible.
|
|
8041
|
+
|
|
8042
|
+
See [Physics Foundation](https://github.com/Photivra/photivra/blob/main/docs/PHYSICS_FOUNDATION.md#sensor-equivalence-f-number-area-and-blur-share).
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# camera/sensor-equivalence.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
|
+
## BlurShareOfFrame
|
|
6
|
+
|
|
7
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
export interface BlurShareOfFrame {
|
|
11
|
+
/** Blur-circle diameter on the sensor in millimetres. */
|
|
12
|
+
blurCircleMm: number;
|
|
13
|
+
/** Sensor diagonal in millimetres. */
|
|
14
|
+
frameDiagonalMm: number;
|
|
15
|
+
/** Blur diameter / frame diagonal. Equal shares look the same at the same output size. */
|
|
16
|
+
shareOfDiagonal: number;
|
|
17
|
+
/** Blur diameter / frame width. */
|
|
18
|
+
shareOfWidth: number;
|
|
19
|
+
/** Blur diameter / frame height. */
|
|
20
|
+
shareOfHeight: number;
|
|
21
|
+
/** Blur on the print; present only when a print size was given. */
|
|
22
|
+
print?: BlurShareOfPrint;
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## BlurShareOfPrint
|
|
27
|
+
|
|
28
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
export interface BlurShareOfPrint {
|
|
32
|
+
/** Linear enlargement from sensor to print: the smaller of the long-side and short-side ratios. */
|
|
33
|
+
enlargement: number;
|
|
34
|
+
/** Blur-circle diameter on the print in millimetres: blurCircleMm × enlargement. */
|
|
35
|
+
blurOnPrintMm: number;
|
|
36
|
+
/** Size of the printed image in millimetres (equal to the print when the aspect ratios match). */
|
|
37
|
+
imageLongSideMm: number;
|
|
38
|
+
imageShortSideMm: number;
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## calculateBlurShareOfFrame
|
|
43
|
+
|
|
44
|
+
A blur circle on the sensor as a share of the frame (diagonal, width and height) and, when a
|
|
45
|
+
print size is given, its diameter on that print.
|
|
46
|
+
|
|
47
|
+
Equal shares of the diagonal look the same when the images are shown at the same size, which
|
|
48
|
+
is how "same framing" comparisons across sensor sizes are stated. Every value is an exact ratio
|
|
49
|
+
or scaling; whether a blur is visible depends on viewing distance and acuity, which this
|
|
50
|
+
function does not judge.
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
export function calculateBlurShareOfFrame(
|
|
54
|
+
input: CalculateBlurShareOfFrameInput
|
|
55
|
+
): CalculationResult<BlurShareOfFrame>;
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
This call is synchronous; any supplied provider must follow its explicit synchronous contract.
|
|
59
|
+
|
|
60
|
+
## CalculateBlurShareOfFrameInput
|
|
61
|
+
|
|
62
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
export interface CalculateBlurShareOfFrameInput {
|
|
66
|
+
/** Blur-circle diameter on the sensor in millimetres (for example from calculateDefocusCircle). Zero is allowed. */
|
|
67
|
+
blurCircleMm: number;
|
|
68
|
+
/** Physical active imaging area of the sensor. */
|
|
69
|
+
sensor: SensorImagingArea;
|
|
70
|
+
/**
|
|
71
|
+
* Optional print size. The whole frame is enlarged to fit inside it without cropping, long side
|
|
72
|
+
* to long side.
|
|
73
|
+
*/
|
|
74
|
+
print?: { widthMm: number; heightMm: number };
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## calculateSensorEquivalence
|
|
79
|
+
|
|
80
|
+
Equivalence of a lens (focal length, f-number) on one sensor against a reference format,
|
|
81
|
+
36 × 24 mm by default: crop factor, equivalent focal length, equivalent f-number for depth of
|
|
82
|
+
field, blur and total light, the unchanged f-number for exposure, sensor area ratio, total-light
|
|
83
|
+
difference in stops and the angles of view.
|
|
84
|
+
|
|
85
|
+
Call it once per sensor for a lens table. Every quantity carries a `basis` entry saying whether it
|
|
86
|
+
is exact under its stated conditions or an approximation. The equivalent f-number is an
|
|
87
|
+
approximation (exact when the focus distance is much greater than the focal length) and never an
|
|
88
|
+
exposure setting: exposure uses `exposureFNumber`, the f-number on the lens.
|
|
89
|
+
|
|
90
|
+
Example: 50 mm f/2 on a 23.5 × 15.6 mm sensor gives crop factor ≈ 1.53, ≈ 77 mm and ≈ f/3.1.
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
export function calculateSensorEquivalence(
|
|
94
|
+
input: CalculateSensorEquivalenceInput
|
|
95
|
+
): CalculationResult<SensorEquivalence>;
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
This call is synchronous; any supplied provider must follow its explicit synchronous contract.
|
|
99
|
+
|
|
100
|
+
## CalculateSensorEquivalenceInput
|
|
101
|
+
|
|
102
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
export interface CalculateSensorEquivalenceInput {
|
|
106
|
+
/** Physical active imaging area of the sensor the lens is used on. */
|
|
107
|
+
sensor: SensorImagingArea;
|
|
108
|
+
/** Physical focal length of the lens in millimetres. */
|
|
109
|
+
focalLengthMm: number;
|
|
110
|
+
/** f-number set on the lens (N = focal length / entrance-pupil diameter). */
|
|
111
|
+
fNumber: number;
|
|
112
|
+
/** Format to compare against. Defaults to the 36 × 24 mm frame. */
|
|
113
|
+
reference?: SensorImagingArea;
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## EquivalenceExactness
|
|
118
|
+
|
|
119
|
+
Whether a value is exact under its stated conditions or an approximation.
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
export type EquivalenceExactness = "exact" | "approximation";
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## EquivalenceValueBasis
|
|
126
|
+
|
|
127
|
+
How one equivalence quantity was obtained, and under which conditions it holds.
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
export interface EquivalenceValueBasis {
|
|
131
|
+
/** `"exact"` under the stated conditions, or `"approximation"`. */
|
|
132
|
+
kind: EquivalenceExactness;
|
|
133
|
+
/** Plain statement of the definition and its conditions. */
|
|
134
|
+
statement: string;
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## SensorEquivalence
|
|
139
|
+
|
|
140
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
export interface SensorEquivalence {
|
|
144
|
+
/** Physical focal length supplied by the caller, in millimetres. */
|
|
145
|
+
focalLengthMm: number;
|
|
146
|
+
/** f-number supplied by the caller. */
|
|
147
|
+
fNumber: number;
|
|
148
|
+
/** Sensor active imaging area used. */
|
|
149
|
+
sensor: SensorImagingArea;
|
|
150
|
+
/** Reference format used (36 × 24 mm unless the caller passed one). */
|
|
151
|
+
reference: SensorImagingArea;
|
|
152
|
+
/** Sensor diagonal in millimetres. */
|
|
153
|
+
sensorDiagonalMm: number;
|
|
154
|
+
/** Reference diagonal in millimetres. */
|
|
155
|
+
referenceDiagonalMm: number;
|
|
156
|
+
/** Diagonal crop factor: reference diagonal / sensor diagonal. Greater than 1 for a smaller sensor. */
|
|
157
|
+
cropFactor: number;
|
|
158
|
+
/** Focal length on the reference format with the same diagonal angle of view: cropFactor × focalLengthMm. */
|
|
159
|
+
equivalentFocalLengthMm: number;
|
|
160
|
+
/**
|
|
161
|
+
* f-number on the reference format that gives the same depth of field, background blur and total
|
|
162
|
+
* light for the same framing and output size: cropFactor × fNumber. Not an exposure setting.
|
|
163
|
+
*/
|
|
164
|
+
equivalentFNumber: number;
|
|
165
|
+
/** f-number that sets exposure (image brightness): the f-number on the lens, unchanged by sensor size. */
|
|
166
|
+
exposureFNumber: number;
|
|
167
|
+
/** Entrance-pupil diameter f/N in millimetres; the equivalent lens has the same diameter. */
|
|
168
|
+
entrancePupilDiameterMm: number;
|
|
169
|
+
/** Sensor area / reference area. Less than 1 for a smaller sensor. */
|
|
170
|
+
areaRatio: number;
|
|
171
|
+
/**
|
|
172
|
+
* log2(areaRatio): total light collected by the sensor relative to the reference at the same
|
|
173
|
+
* f-number, shutter time and scene, in stops. Negative for a smaller sensor.
|
|
174
|
+
*/
|
|
175
|
+
totalLightDifferenceStops: number;
|
|
176
|
+
/** Angles of view of this lens on the sensor (the equivalent lens gives the same diagonal angle). */
|
|
177
|
+
fieldOfView: SensorEquivalenceFieldOfView;
|
|
178
|
+
/** Exact or approximated, with the conditions, for every quantity above. */
|
|
179
|
+
basis: Readonly<Record<SensorEquivalenceQuantity, EquivalenceValueBasis>>;
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## SensorEquivalenceFieldOfView
|
|
184
|
+
|
|
185
|
+
Angles of view of an ideal rectilinear lens focused at infinity, in degrees.
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
export interface SensorEquivalenceFieldOfView {
|
|
189
|
+
horizontalDegrees: number;
|
|
190
|
+
verticalDegrees: number;
|
|
191
|
+
diagonalDegrees: number;
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## SensorEquivalenceQuantity
|
|
196
|
+
|
|
197
|
+
Quantities of a sensor equivalence result that carry an exactness statement.
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
export type SensorEquivalenceQuantity =
|
|
201
|
+
| "cropFactor"
|
|
202
|
+
| "equivalentFocalLengthMm"
|
|
203
|
+
| "equivalentFNumber"
|
|
204
|
+
| "exposureFNumber"
|
|
205
|
+
| "entrancePupilDiameterMm"
|
|
206
|
+
| "areaRatio"
|
|
207
|
+
| "totalLightDifferenceStops"
|
|
208
|
+
| "fieldOfView";
|
|
209
|
+
```
|
package/docs/api/core-version.md
CHANGED
|
@@ -11,5 +11,5 @@ version they were made with. Proof-of-concept (POC), schema and model contracts
|
|
|
11
11
|
own versions. Release checks confirm it matches package.json and the packed package.
|
|
12
12
|
|
|
13
13
|
```ts
|
|
14
|
-
ENGINE_API_VERSION = "2.
|
|
14
|
+
ENGINE_API_VERSION = "2.18.0" as const
|
|
15
15
|
```
|
|
@@ -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
|
+
```
|
package/docs/api/exports.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"packageVersion": "2.
|
|
3
|
-
"engineApiVersion": "2.
|
|
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",
|
|
@@ -1287,6 +1307,16 @@
|
|
|
1287
1307
|
"source": "src/sensor/eqe-temporal-exposure.ts",
|
|
1288
1308
|
"kind": "type"
|
|
1289
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
|
+
},
|
|
1290
1320
|
{
|
|
1291
1321
|
"name": "calculateSensorFixedPatternSite",
|
|
1292
1322
|
"source": "src/sensor/fixed-pattern-noise.ts",
|
|
@@ -2957,6 +2987,16 @@
|
|
|
2957
2987
|
"source": "src/capture/environment-raw-producer.ts",
|
|
2958
2988
|
"kind": "type"
|
|
2959
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
|
+
},
|
|
2960
3000
|
{
|
|
2961
3001
|
"name": "EquivalentFocalLength35Mm",
|
|
2962
3002
|
"source": "src/camera/equivalent-focal-length.ts",
|
|
@@ -3892,6 +3932,11 @@
|
|
|
3892
3932
|
"source": "src/facade/chart-scenes.ts",
|
|
3893
3933
|
"kind": "type"
|
|
3894
3934
|
},
|
|
3935
|
+
{
|
|
3936
|
+
"name": "IDEAL_LENS_LIMITS",
|
|
3937
|
+
"source": "src/equipment/ideal-lens.ts",
|
|
3938
|
+
"kind": "runtime"
|
|
3939
|
+
},
|
|
3895
3940
|
{
|
|
3896
3941
|
"name": "IdealApertureGeometry",
|
|
3897
3942
|
"source": "src/optics/aperture.ts",
|
|
@@ -7827,6 +7872,21 @@
|
|
|
7827
7872
|
"source": "src/sensor/eqe-electron-rate.ts",
|
|
7828
7873
|
"kind": "type"
|
|
7829
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
|
+
},
|
|
7830
7890
|
{
|
|
7831
7891
|
"name": "SensorExpectedReadoutSignal",
|
|
7832
7892
|
"source": "src/sensor/raw-readout.ts",
|
|
@@ -8512,6 +8572,11 @@
|
|
|
8512
8572
|
"source": "src/facade/simulate-capture.ts",
|
|
8513
8573
|
"kind": "type"
|
|
8514
8574
|
},
|
|
8575
|
+
{
|
|
8576
|
+
"name": "SimulateCaptureLensCatalogueRecord",
|
|
8577
|
+
"source": "src/facade/simulate-capture.ts",
|
|
8578
|
+
"kind": "type"
|
|
8579
|
+
},
|
|
8515
8580
|
{
|
|
8516
8581
|
"name": "SimulateCaptureLensDistortion",
|
|
8517
8582
|
"source": "src/facade/simulate-capture.ts",
|
|
@@ -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,18 @@ 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 };
|
|
517
537
|
/**
|
|
518
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";
|
|
519
539
|
* docs/LENS_CATALOGUE.md). Like `prime`, its focal length is the full-frame equivalent ÷ the simulated frame's diagonal crop
|
|
@@ -522,9 +542,12 @@ export interface SimulateCaptureOptions {
|
|
|
522
542
|
* colour, at the resolved f-number and focus) through the environment PSF quadrature, its radial distortion, its vignetting
|
|
523
543
|
* and, for the fisheyes, the equisolid projection. Flare, focus breathing and bokeh shape are not rendered. Not combinable
|
|
524
544
|
* with `prime`, `focalLengthMm`, `projection`, `distortion`, `vignetting`, `psf`, `diffraction` or `pupilSampling` (the
|
|
525
|
-
* lens sets them); `defocus` convolves the disc into the lens's PSF.
|
|
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").
|
|
526
549
|
*/
|
|
527
|
-
catalogue?: string;
|
|
550
|
+
catalogue?: string | SimulateCaptureLensCatalogueRecord;
|
|
528
551
|
/**
|
|
529
552
|
* Opt-in (#549), with `catalogue` only: a seeded autofocus error (`simulateGenericLensFocusError`). The focus distance becomes
|
|
530
553
|
* the sampled one for (seed, shotIndex), reported in the `lens.focusScatter` assumption; it is visible with `defocus`. Default off.
|
|
@@ -748,7 +771,7 @@ export interface SimulateCaptureOptions {
|
|
|
748
771
|
* Exact or fast (#395). `false` or omitted: every option runs its exact method, the engine reference. `true`: each option
|
|
749
772
|
* listed in {@link SIMULATE_CAPTURE_FAST_OPTIONS} runs its fast method where it has one, a measured, bounded approximation of
|
|
750
773
|
* the exact one; an option without a fast method runs exact. An option's own record `fast` overrides this for that option
|
|
751
|
-
* only. `diagnostics.fast` reports what was requested and what ran.
|
|
774
|
+
* only. `diagnostics.fast` reports what was requested and what ran. `lens.catalogue` has a fast method (#568); the other options run exact.
|
|
752
775
|
*/
|
|
753
776
|
fast?: boolean;
|
|
754
777
|
/** Aborting cancels the running task; the promise rejects with a domain error and returns no partial image. */
|
package/llms.txt
CHANGED
|
@@ -24,6 +24,8 @@
|
|
|
24
24
|
- [resolveGenericLens()](https://github.com/Photivra/photivra/blob/main/docs/api/equipment-generic-lens-catalogue.md#resolvegenericlens): one of the 30 generic catalogue lenses ("PV 50 1.2 Pro") on a sensor, with its approximated optical character; `simulateCapture` renders it with `lens.catalogue`
|
|
25
25
|
- [calculateFieldOfView()](https://github.com/Photivra/photivra/blob/main/docs/api/camera-field-of-view.md#calculatefieldofview): angle of view from focal length and sensor size
|
|
26
26
|
- [calculateEquivalentFocalLength35Mm()](https://github.com/Photivra/photivra/blob/main/docs/api/camera-equivalent-focal-length.md#calculateequivalentfocallength35mm): crop factor and 35 mm-equivalent focal length
|
|
27
|
+
- [calculateSensorEquivalence()](https://github.com/Photivra/photivra/blob/main/docs/api/camera-sensor-equivalence.md#calculatesensorequivalence): equivalent focal length and f-number, sensor area ratio and angles of view of a lens on one sensor, each marked exact or approximated
|
|
28
|
+
- [calculateBlurShareOfFrame()](https://github.com/Photivra/photivra/blob/main/docs/api/camera-sensor-equivalence.md#calculateblurshareofframe): a blur circle as a share of the frame diagonal, or its size on a print
|
|
27
29
|
- [calculateCenteredCrop()](https://github.com/Photivra/photivra/blob/main/docs/api/output-crop.md#calculatecenteredcrop): pixels left after a centred crop
|
|
28
30
|
- [calculateProjectedMotionBlur()](https://github.com/Photivra/photivra/blob/main/docs/api/motion-projected-motion.md#calculateprojectedmotionblur): how far a moving subject smears on the sensor during the shutter time
|
|
29
31
|
- [calculateAiryDisk()](https://github.com/Photivra/photivra/blob/main/docs/api/optics-diffraction.md#calculateairydisk): diffraction spot size from f-number and wavelength
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@photivra/engine",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.18.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Scientific TypeScript engine for camera optics, exposure, focus, motion, sensor sampling, diffraction, and digital imaging calculations.",
|
|
6
6
|
"type": "module",
|