@photivra/engine 2.5.0 → 2.7.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 +129 -1
- package/CITATION.cff +1 -1
- package/README.md +6 -6
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +1 -0
- package/dist/capture/native-environment-lean.d.ts +1 -1
- package/dist/capture/native-environment-lean.js +17 -3
- package/dist/capture/native-environment-lean.js.map +1 -1
- package/dist/capture/native-environment-raw.d.ts +0 -4
- package/dist/capture/native-environment-raw.js +6 -2
- package/dist/capture/native-environment-raw.js.map +1 -1
- package/dist/color/illuminants.d.ts +15 -0
- package/dist/color/illuminants.js +81 -24
- package/dist/color/illuminants.js.map +1 -1
- package/dist/color/srgb-transfer.d.ts +6 -0
- package/dist/color/srgb-transfer.js +9 -0
- package/dist/color/srgb-transfer.js.map +1 -1
- package/dist/color/white-balance.d.ts +6 -0
- package/dist/color/white-balance.js +15 -2
- package/dist/color/white-balance.js.map +1 -1
- package/dist/core/internal-radiance-evaluator.d.ts +6 -0
- package/dist/core/internal-radiance-evaluator.js +33 -0
- package/dist/core/internal-radiance-evaluator.js.map +1 -0
- package/dist/core/portable-math.d.ts +33 -0
- package/dist/core/portable-math.js +342 -0
- package/dist/core/portable-math.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/facade/chart-radiance-batch.js +3 -2
- package/dist/facade/chart-radiance-batch.js.map +1 -1
- package/dist/facade/chart-scenes.d.ts +11 -0
- package/dist/facade/chart-scenes.js +21 -7
- package/dist/facade/chart-scenes.js.map +1 -1
- package/dist/facade/diffraction-provider.d.ts +10 -0
- package/dist/facade/diffraction-provider.js +38 -17
- package/dist/facade/diffraction-provider.js.map +1 -1
- package/dist/facade/display-view.d.ts +213 -0
- package/dist/facade/display-view.js +1543 -0
- package/dist/facade/display-view.js.map +1 -0
- package/dist/facade/scene-radiance.d.ts +45 -5
- package/dist/facade/scene-radiance.js +97 -28
- package/dist/facade/scene-radiance.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +233 -5
- package/dist/facade/simulate-capture.js +120 -37
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.d.ts +45 -0
- package/dist/facade/simulate-display-view.js +214 -0
- package/dist/facade/simulate-display-view.js.map +1 -0
- package/dist/facade/subject-framing.d.ts +16 -0
- package/dist/facade/subject-framing.js +8 -2
- package/dist/facade/subject-framing.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/optics/diffraction-psf-profile.d.ts +9 -2
- package/dist/optics/diffraction-psf-profile.js +33 -4
- package/dist/optics/diffraction-psf-profile.js.map +1 -1
- package/dist/optics/irradiance-convolution.js +1 -1
- package/dist/optics/irradiance-convolution.js.map +1 -1
- package/dist/optics/scene-to-sensor-irradiance.d.ts +7 -0
- package/dist/optics/scene-to-sensor-irradiance.js +10 -5
- package/dist/optics/scene-to-sensor-irradiance.js.map +1 -1
- package/dist/output/print-plan.d.ts +8 -1
- package/dist/output/print-plan.js +8 -1
- package/dist/output/print-plan.js.map +1 -1
- package/dist/schema/scene-radiance.d.ts +16 -0
- package/dist/schema/scene-radiance.js +25 -4
- package/dist/schema/scene-radiance.js.map +1 -1
- package/dist/sensor/raw-readout.d.ts +12 -0
- package/dist/sensor/raw-readout.js +131 -9
- package/dist/sensor/raw-readout.js.map +1 -1
- package/dist/sensor/stochastic-seed.d.ts +13 -0
- package/dist/sensor/stochastic-seed.js +42 -8
- package/dist/sensor/stochastic-seed.js.map +1 -1
- package/docs/AGENT_GUIDE.md +1 -0
- package/docs/API_REFERENCE.md +19 -1
- package/docs/QUICK_START.md +203 -4
- package/docs/USAGE.md +15 -0
- package/docs/api/core-version.md +1 -1
- package/docs/api/exports.json +52 -2
- package/docs/api/facade-scene-radiance.md +34 -1
- package/docs/api/facade-simulate-capture.md +66 -2
- package/docs/api/facade-simulate-display-view.md +57 -0
- package/docs/api/facade-subject-framing.md +16 -0
- package/docs/api/output-print-plan.md +8 -1
- package/package.json +3 -2
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
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
4
|
|
|
5
|
+
## CALLER_MATERIAL_SCENE_MAX_MATERIALS
|
|
6
|
+
|
|
7
|
+
Most materials a {@link CallerMaterialScene} may list.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
CALLER_MATERIAL_SCENE_MAX_MATERIALS = 65_536
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## CallerMaterialScene
|
|
14
|
+
|
|
15
|
+
2.7 (#386): a piecewise-constant caller scene, lit by the facade illuminant like a reflectance function. `materials` are
|
|
16
|
+
reflectances as a chart's patches take them (a neutral number in [0, 1] or a reflectance spectrum covering 380–780 nm, at
|
|
17
|
+
most {@link CALLER_MATERIAL_SCENE_MAX_MATERIALS}); `materialAt` returns the index of the material at a plane point (an
|
|
18
|
+
integer from 0 to materials.length − 1). The radiance at a point is that material's, exactly as for a reflectance function
|
|
19
|
+
returning its reflectance, but renderers can tabulate each material once: the display view then takes the chart path.
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
export interface CallerMaterialScene {
|
|
23
|
+
kind: "material-function";
|
|
24
|
+
sceneId: string;
|
|
25
|
+
materials: readonly ChartReflectance[];
|
|
26
|
+
materialAt(point: Readonly<SceneMaterialPoint>): number;
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
5
30
|
## CallerRadianceScene
|
|
6
31
|
|
|
7
32
|
Caller absolute spectral radiance in W·m⁻²·sr⁻¹·nm⁻¹; the facade illuminant is not used.
|
|
@@ -65,7 +90,7 @@ provider `providerProfileId`.
|
|
|
65
90
|
|
|
66
91
|
```ts
|
|
67
92
|
export function createPlanarSceneRadianceProvider(input: {
|
|
68
|
-
scene: ChartScene | CallerReflectanceScene | CallerRadianceScene;
|
|
93
|
+
scene: ChartScene | CallerReflectanceScene | CallerRadianceScene | CallerMaterialScene;
|
|
69
94
|
illuminant: CaptureIlluminant;
|
|
70
95
|
illuminanceLux: number;
|
|
71
96
|
distanceM: number;
|
|
@@ -140,6 +165,14 @@ export interface ResolvedCaptureIlluminant {
|
|
|
140
165
|
}
|
|
141
166
|
```
|
|
142
167
|
|
|
168
|
+
## SceneMaterialPoint
|
|
169
|
+
|
|
170
|
+
2.7: a scene-plane point without a wavelength, passed to a material scene's `materialAt`.
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
export type SceneMaterialPoint = Omit<ScenePlanePoint, "wavelengthNanometers">;
|
|
174
|
+
```
|
|
175
|
+
|
|
143
176
|
## ScenePlanePoint
|
|
144
177
|
|
|
145
178
|
A point of the scene plane passed to caller scene functions.
|
|
@@ -2,6 +2,42 @@
|
|
|
2
2
|
|
|
3
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
4
|
|
|
5
|
+
## DisplayViewResult
|
|
6
|
+
|
|
7
|
+
Result of {@link simulateDisplayView }: the full-resolution capture resized for display, estimated; not a capture.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
export interface DisplayViewResult {
|
|
11
|
+
kind: "display-view";
|
|
12
|
+
/** Human-readable label for a user interface. */
|
|
13
|
+
label: string;
|
|
14
|
+
facadeVersion: typeof SIMULATE_CAPTURE_FACADE_VERSION;
|
|
15
|
+
engineApiVersion: string;
|
|
16
|
+
/** The display view (or its region): interleaved sRGB samples, row by row from the top-left. */
|
|
17
|
+
image: { width: number; height: number; format: "srgb-8" | "srgb-16"; samples: Uint8Array | Uint16Array };
|
|
18
|
+
/**
|
|
19
|
+
* The view: display size, the capture frame it shows (output pixels), real pixels per display pixel along each axis,
|
|
20
|
+
* the samples per axis and, with `view.region`, the rendered rectangle.
|
|
21
|
+
*/
|
|
22
|
+
view: { width: number; height: number; frameWidth: number; frameHeight: number; scaleX: number; scaleY: number; samplesPerAxis: number;
|
|
23
|
+
region?: { x: number; y: number; width: number; height: number } };
|
|
24
|
+
/** The resolved exposure of the capture this view shows. */
|
|
25
|
+
exposure: { mode: SimulateCaptureExposureMode; settings: { fNumber: number; shutterSeconds: number; iso: number }; ev100: number };
|
|
26
|
+
scene: SimulateCaptureResult["scene"];
|
|
27
|
+
/**
|
|
28
|
+
* Work done: real pixels sampled, real-site expectations evaluated, scene queries, display pixels sampled in full (detail) or with
|
|
29
|
+
* a quarter of the samples per axis (uniform), display pixels whose value includes the AHD low-signal correction, and wall-clock
|
|
30
|
+
* milliseconds (diagnostic only; never affects the samples).
|
|
31
|
+
*/
|
|
32
|
+
diagnostics: { sampleCount: number; siteEvaluations: number; sceneLookups: number; detailPixelCount: number; uniformPixelCount: number; ahdCorrectedPixelCount: number;
|
|
33
|
+
timingsMs: { view: number; total: number } };
|
|
34
|
+
provenance: { scientificStatus: "approximation"; cameraPresetId: string; cameraPresetVersion: string; evidence: readonly EvidenceProvenance[];
|
|
35
|
+
composedContracts: readonly string[]; limitations: readonly string[] };
|
|
36
|
+
/** The capture request's assumptions, then `view.definition`, `view.estimation`, `view.noise`, `view.demosaic` and `view.source`. */
|
|
37
|
+
assumptions: readonly SimulateCaptureAssumption[];
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
5
41
|
## IdealReferenceResult
|
|
6
42
|
|
|
7
43
|
Result of {@link simulateIdealReference}: an ideal reference image, not a capture (no RAW, plan or request).
|
|
@@ -65,6 +101,14 @@ Version of how the facade builds requests. It changes whenever the same input wo
|
|
|
65
101
|
SIMULATE_CAPTURE_FACADE_VERSION = "1.0.0" as const
|
|
66
102
|
```
|
|
67
103
|
|
|
104
|
+
## SIMULATE_DISPLAY_VIEW_DEFAULT_SAMPLES_PER_AXIS
|
|
105
|
+
|
|
106
|
+
Default stratified samples per axis of a display view (`view.samplesPerAxis`): 16 × 16 sampled real pixels per display pixel with detail, 4 × 4 where it is uniform.
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
SIMULATE_DISPLAY_VIEW_DEFAULT_SAMPLES_PER_AXIS = 16
|
|
110
|
+
```
|
|
111
|
+
|
|
68
112
|
## simulateCapture
|
|
69
113
|
|
|
70
114
|
Simulates one still capture of a planar scene and returns the developed image, the packed RAW, the full
|
|
@@ -220,7 +264,7 @@ export interface SimulateCaptureOptions {
|
|
|
220
264
|
compensationStops?: number;
|
|
221
265
|
};
|
|
222
266
|
/** Required: an engine chart (for example `{ preset: "uniform" }`, an 18 % grey card) or a caller reflectance/radiance function. */
|
|
223
|
-
scene: ChartScene | CallerReflectanceScene | CallerRadianceScene;
|
|
267
|
+
scene: ChartScene | CallerReflectanceScene | CallerRadianceScene | CallerMaterialScene;
|
|
224
268
|
/** Distance from the camera to the scene plane in metres. Default 5. */
|
|
225
269
|
subjectDistanceM?: number;
|
|
226
270
|
/**
|
|
@@ -285,7 +329,9 @@ export interface SimulateCaptureOptions {
|
|
|
285
329
|
* the whole render: framing, metering, exposure, noise seeds (absolute native site indices), rolling-shutter row
|
|
286
330
|
* timing, diffraction and demosaic all use the whole frame. Use it to tile a large frame across workers or to
|
|
287
331
|
* render a 100 % crop. `result.image` is the region; `result.region` reports it. Not combinable with
|
|
288
|
-
* `whiteBalance: "auto"`, whose statistics would come from the region only.
|
|
332
|
+
* `whiteBalance: "auto"`, whose statistics would come from the region only. From 2.6 the diffraction convolution
|
|
333
|
+
* covers only the region plus the kernel and filter reach, so a small region of a large frame costs about as much
|
|
334
|
+
* as a capture of the region's size.
|
|
289
335
|
*/
|
|
290
336
|
region?: { x: number; y: number; width: number; height: number };
|
|
291
337
|
};
|
|
@@ -503,6 +549,24 @@ export type SimulateCaptureWhiteBalance =
|
|
|
503
549
|
| { channelGains: { red: number; green: number; blue: number } };
|
|
504
550
|
```
|
|
505
551
|
|
|
552
|
+
## SimulateDisplayViewOptions
|
|
553
|
+
|
|
554
|
+
The display size and sampling of {@link simulateDisplayView }.
|
|
555
|
+
|
|
556
|
+
```ts
|
|
557
|
+
export interface SimulateDisplayViewOptions {
|
|
558
|
+
/** Display pixels, 1 to the capture's output width and height. */
|
|
559
|
+
width: number;
|
|
560
|
+
height: number;
|
|
561
|
+
/** Stratified Monte Carlo samples per axis for each display pixel's expected value (1–32, default 8: 64 real pixels). */
|
|
562
|
+
samplesPerAxis?: number;
|
|
563
|
+
/** "srgb-8" (default) or "srgb-16". */
|
|
564
|
+
format?: "srgb-8" | "srgb-16";
|
|
565
|
+
/** Render only this rectangle of the display view, in display pixels; byte-identical to the same pixels of the whole view. */
|
|
566
|
+
region?: { x: number; y: number; width: number; height: number };
|
|
567
|
+
}
|
|
568
|
+
```
|
|
569
|
+
|
|
506
570
|
## simulateIdealReference
|
|
507
571
|
|
|
508
572
|
The ideal reference of a capture request (2.3): the same scene, framing, chart size, output size, region and pixel
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# facade/simulate-display-view.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
|
+
## PreparedDisplayView
|
|
6
|
+
|
|
7
|
+
2.7: a display-view request resolved once (`prepareDisplayView`). `render(view)` gives the same result as
|
|
8
|
+
`simulateDisplayView(options, view)` for the prepared options, byte for byte (samples, view, exposure, scene, diagnostics
|
|
9
|
+
and assumptions; only `diagnostics.timingsMs` differ, and they cover the render only), without repeating the request's
|
|
10
|
+
setup: use it to render one view in many regions, for example on each worker of a progressive display.
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
export interface PreparedDisplayView {
|
|
14
|
+
/** Renders a view of the prepared request. Renders may run concurrently; they share no state except read-only setup. */
|
|
15
|
+
render(view: SimulateDisplayViewOptions, renderOptions?: PreparedDisplayViewRenderOptions): Promise<DisplayViewResult>;
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## PreparedDisplayViewRenderOptions
|
|
20
|
+
|
|
21
|
+
Options of one {@link PreparedDisplayView} render.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
export interface PreparedDisplayViewRenderOptions {
|
|
25
|
+
/** Aborting cancels this render only; the promise rejects with a domain error. The request's own `signal` applies to preparing only. */
|
|
26
|
+
signal?: AbortSignal;
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## prepareDisplayView
|
|
31
|
+
|
|
32
|
+
2.7 (#387): resolves a {@link simulateDisplayView} request once, without a view, and returns a handle whose `render(view)`
|
|
33
|
+
renders its views. The setup it does once (request validation, the camera, lens, scene, chart and illuminant, the channel
|
|
34
|
+
responses through the environment chain, the diffraction kernels, readout moments, demosaic tables and colour matrix) costs
|
|
35
|
+
about 0.3–1.3 s per call on a 24 MP request; a region costs only its own pixels. Errors in the request are raised here;
|
|
36
|
+
errors in the view by `render`.
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
export async function prepareDisplayView(options: SimulateCaptureOptions): Promise<PreparedDisplayView>;
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
This call is asynchronous; byte hashing uses the caller runtime's Web Crypto.
|
|
43
|
+
|
|
44
|
+
## simulateDisplayView
|
|
45
|
+
|
|
46
|
+
The display view of a capture request: the image the real full-resolution capture (`simulateCapture(options)`) would
|
|
47
|
+
give after it is resized to `view.width` × `view.height` by area averaging in linear light, estimated at a cost that
|
|
48
|
+
follows the display pixels, not the capture's. Optics (lens diffraction, the anti-aliasing filter, the pixel aperture)
|
|
49
|
+
act at the real pixel pitch; the colour mosaic, demosaic and development are those of the real pixels; the noise is
|
|
50
|
+
that of the average of the real pixels, demosaic correlation included; exposure, white balance and tone are the
|
|
51
|
+
capture's. The result is labelled `kind: "display-view"`; it is not a capture.
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
export async function simulateDisplayView(options: SimulateCaptureOptions, view: SimulateDisplayViewOptions): Promise<DisplayViewResult>;
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
This call is asynchronous; byte hashing uses the caller runtime's Web Crypto.
|
|
@@ -86,6 +86,14 @@ export interface ChartFraming {
|
|
|
86
86
|
coverage: { frameWidthFraction: number; frameHeightFraction: number };
|
|
87
87
|
/** The chart's image on the sensor, mm. */
|
|
88
88
|
chartImageMm: { width: number; height: number };
|
|
89
|
+
/**
|
|
90
|
+
* 2.6: the thin-lens close-up light factor at the focus distance, 1/(1 + m)² with m = v/s: the image-plane irradiance
|
|
91
|
+
* relative to the same lens and f-number focused at infinity (working f-number N·(1 + m)). It is the factor
|
|
92
|
+
* `simulateCapture` applies through the scene-to-sensor irradiance (same function, same value).
|
|
93
|
+
*/
|
|
94
|
+
closeUpTransmission: number;
|
|
95
|
+
/** 2.6: the same factor in stops, 2·log₂(1 + m): how much darker the image is than at infinity focus. */
|
|
96
|
+
closeUpLossStops: number;
|
|
89
97
|
/** The generic prime of this equivalent focal length, when there is one. */
|
|
90
98
|
prime: GenericPrimeLens | null;
|
|
91
99
|
/** The generic prime rule max(0.10 m, f·(1 + 1/0.15)) at this focal length (the prime's own value when there is one). */
|
|
@@ -119,6 +127,14 @@ export interface SubjectFraming {
|
|
|
119
127
|
framedHeightM: number;
|
|
120
128
|
/** Fraction of the frame width and height the chart covers (1 on the fitted side; above 1 means cropped). */
|
|
121
129
|
coverage: { frameWidthFraction: number; frameHeightFraction: number };
|
|
130
|
+
/**
|
|
131
|
+
* 2.6: the thin-lens close-up light factor at the focus distance, 1/(1 + m)² with m = v/s: the image-plane irradiance
|
|
132
|
+
* relative to the same lens and f-number focused at infinity (working f-number N·(1 + m)). It is the factor
|
|
133
|
+
* `simulateCapture` applies through the scene-to-sensor irradiance (same function, same value).
|
|
134
|
+
*/
|
|
135
|
+
closeUpTransmission: number;
|
|
136
|
+
/** 2.6: the same factor in stops, 2·log₂(1 + m): how much darker the image is than at infinity focus. */
|
|
137
|
+
closeUpLossStops: number;
|
|
122
138
|
/** The generic prime of this equivalent focal length, when there is one. */
|
|
123
139
|
prime: GenericPrimeLens | null;
|
|
124
140
|
/** The generic prime rule max(0.10 m, f·(1 + 1/0.15)) at this focal length (the prime's own value when there is one). */
|
|
@@ -5,7 +5,11 @@ The package and root API versions are recorded once in [Navigation](../API_REFER
|
|
|
5
5
|
## calculatePrintPlan
|
|
6
6
|
|
|
7
7
|
Finds the smallest file, at the exact native aspect ratio, that meets the declared sampling and lab constraints.
|
|
8
|
-
The angular guidance is an approximate rule of thumb; the geometry itself is calculated exactly.
|
|
8
|
+
The angular guidance is an approximate rule of thumb; the geometry itself is calculated exactly. A common criterion,
|
|
9
|
+
one arcminute per pixel, is normal (20/20) visual acuity under optimal conditions: the minimum angle of resolution of an
|
|
10
|
+
ideal eye is about 1 minute of arc, and 20/20 letters are built from elements that each subtend 1 arcmin (Z. Alomar, K. D. Bojikian, "Visual Acuity Testing in Adults", EyeWiki, American Academy of Ophthalmology,
|
|
11
|
+
https://eyewiki.aao.org/Visual_Acuity_Testing_in_Adults, last edited June 16, 2026).
|
|
12
|
+
Some viewers resolve finer detail and many resolve coarser, so it stays a rule of thumb, and the criterion is the caller's.
|
|
9
13
|
It does not assess source detail, perceived quality, file writing or available resources.
|
|
10
14
|
|
|
11
15
|
```ts
|
|
@@ -18,6 +22,9 @@ This call is synchronous; any supplied provider must follow its explicit synchro
|
|
|
18
22
|
|
|
19
23
|
Calculates the largest print size under the stated conditions. Lab constraints need individual plans,
|
|
20
24
|
so this size query reports them as unsupported rather than ignoring them.
|
|
25
|
+
As in `calculatePrintPlan`, an angular criterion is an approximate rule of thumb: one arcminute per pixel is normal
|
|
26
|
+
(20/20) acuity under optimal conditions, which some viewers exceed and many do not (Z. Alomar, K. D. Bojikian, "Visual Acuity Testing in Adults", EyeWiki, American Academy of Ophthalmology,
|
|
27
|
+
https://eyewiki.aao.org/Visual_Acuity_Testing_in_Adults, last edited June 16, 2026).
|
|
21
28
|
|
|
22
29
|
```ts
|
|
23
30
|
export function calculatePrintSizeLimit(value: PrintSizeLimitInput): CalculationResult<PrintSizeLimit>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@photivra/engine",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.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",
|
|
@@ -108,7 +108,8 @@
|
|
|
108
108
|
"typescript-eslint": "8.70.0",
|
|
109
109
|
"vitest": "5.0.1",
|
|
110
110
|
"@types/node": "24.13.6",
|
|
111
|
-
"@vitest/coverage-v8": "5.0.1"
|
|
111
|
+
"@vitest/coverage-v8": "5.0.1",
|
|
112
|
+
"rolldown": "1.2.9"
|
|
112
113
|
},
|
|
113
114
|
"keywords": [
|
|
114
115
|
"photography",
|