@photivra/engine 2.19.0 → 2.21.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 +289 -1
- package/CITATION.cff +1 -1
- package/NODE_SUPPORT.md +7 -5
- package/README.md +7 -6
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +21 -10
- package/dist/capture/dng-lens-correction-opcodes.d.ts +32 -0
- package/dist/capture/dng-lens-correction-opcodes.js +119 -0
- package/dist/capture/dng-lens-correction-opcodes.js.map +1 -0
- package/dist/capture/exposure-build-up-stage.d.ts +16 -0
- package/dist/capture/exposure-build-up-stage.js +4 -0
- package/dist/capture/exposure-build-up-stage.js.map +1 -0
- package/dist/capture/native-environment-lean.d.ts +2 -1
- package/dist/capture/native-environment-lean.js +42 -78
- package/dist/capture/native-environment-lean.js.map +1 -1
- package/dist/capture/native-environment-tabulated.js +4 -3
- package/dist/capture/native-environment-tabulated.js.map +1 -1
- package/dist/capture/native-raw-dng.d.ts +6 -0
- package/dist/capture/native-raw-dng.js +16 -2
- package/dist/capture/native-raw-dng.js.map +1 -1
- package/dist/capture/native-raw.d.ts +3 -2
- package/dist/capture/native-raw.js +5 -4
- package/dist/capture/native-raw.js.map +1 -1
- package/dist/capture/sensor-raw-producer.d.ts +4 -1
- package/dist/capture/sensor-raw-producer.js +15 -8
- package/dist/capture/sensor-raw-producer.js.map +1 -1
- package/dist/core/shared-helpers.d.ts +2 -0
- package/dist/core/shared-helpers.js +4 -0
- package/dist/core/shared-helpers.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/generic-lens-catalogue.d.ts +11 -4
- package/dist/equipment/generic-lens-catalogue.js +49 -7
- package/dist/equipment/generic-lens-catalogue.js.map +1 -1
- package/dist/equipment/generic-tier-assets.d.ts +32 -0
- package/dist/equipment/generic-tier-assets.js +18 -14
- package/dist/equipment/generic-tier-assets.js.map +1 -1
- package/dist/equipment/generic-tier-presets.js +2 -3
- package/dist/equipment/generic-tier-presets.js.map +1 -1
- package/dist/equipment/sensor-profile-body.d.ts +78 -0
- package/dist/equipment/sensor-profile-body.js +77 -0
- package/dist/equipment/sensor-profile-body.js.map +1 -0
- package/dist/equipment/sensor-profiles.d.ts +8 -0
- package/dist/equipment/sensor-profiles.js +82 -0
- package/dist/equipment/sensor-profiles.js.map +1 -0
- package/dist/facade/catalogue-lens-signal.d.ts +2 -2
- package/dist/facade/catalogue-lens-signal.js +58 -71
- package/dist/facade/catalogue-lens-signal.js.map +1 -1
- package/dist/facade/chart-radiance-batch.js +2 -1
- package/dist/facade/chart-radiance-batch.js.map +1 -1
- package/dist/facade/defocus-image-space-provider.d.ts +7 -0
- package/dist/facade/defocus-image-space-provider.js +64 -76
- package/dist/facade/defocus-image-space-provider.js.map +1 -1
- package/dist/facade/display-view-gpu-session.js +2 -0
- package/dist/facade/display-view-gpu-session.js.map +1 -1
- package/dist/facade/display-view-gpu.d.ts +13 -5
- package/dist/facade/display-view-gpu.js +4 -4
- package/dist/facade/display-view-gpu.js.map +1 -1
- package/dist/facade/display-view-lens-signal.d.ts +1 -1
- package/dist/facade/display-view-lens-signal.js +30 -26
- package/dist/facade/display-view-lens-signal.js.map +1 -1
- package/dist/facade/display-view.d.ts +3 -1
- package/dist/facade/display-view.js +6 -24
- package/dist/facade/display-view.js.map +1 -1
- package/dist/facade/expected-signal-setup.d.ts +3 -1
- package/dist/facade/expected-signal-setup.js +1 -1
- package/dist/facade/expected-signal-setup.js.map +1 -1
- package/dist/facade/expected-signal.d.ts +47 -14
- package/dist/facade/expected-signal.js +117 -112
- package/dist/facade/expected-signal.js.map +1 -1
- package/dist/facade/exposure-build-up.d.ts +70 -0
- package/dist/facade/exposure-build-up.js +164 -0
- package/dist/facade/exposure-build-up.js.map +1 -0
- package/dist/facade/fast-methods.d.ts +50 -0
- package/dist/facade/fast-methods.js +58 -0
- package/dist/facade/fast-methods.js.map +1 -0
- package/dist/facade/fast-resolution.d.ts +12 -0
- package/dist/facade/fast-resolution.js +18 -0
- package/dist/facade/fast-resolution.js.map +1 -0
- package/dist/facade/generic-lens-capture.d.ts +5 -4
- package/dist/facade/generic-lens-capture.js +5 -4
- package/dist/facade/generic-lens-capture.js.map +1 -1
- package/dist/facade/image-plane-enable.d.ts +7 -0
- package/dist/facade/image-plane-enable.js +17 -0
- package/dist/facade/image-plane-enable.js.map +1 -0
- package/dist/facade/image-plane-lattice-data.d.ts +6 -0
- package/dist/facade/image-plane-lattice-data.js +10 -0
- package/dist/facade/image-plane-lattice-data.js.map +1 -0
- package/dist/facade/image-plane-support.d.ts +33 -0
- package/dist/facade/image-plane-support.js +11 -0
- package/dist/facade/image-plane-support.js.map +1 -0
- package/dist/facade/image-plane.d.ts +36 -0
- package/dist/facade/image-plane.js +353 -0
- package/dist/facade/image-plane.js.map +1 -0
- package/dist/facade/mixed-illumination.d.ts +108 -0
- package/dist/facade/mixed-illumination.js +174 -0
- package/dist/facade/mixed-illumination.js.map +1 -0
- package/dist/facade/representative-light-sources.d.ts +72 -0
- package/dist/facade/representative-light-sources.js +124 -0
- package/dist/facade/representative-light-sources.js.map +1 -0
- package/dist/facade/scene-radiance.d.ts +129 -4
- package/dist/facade/scene-radiance.js +48 -13
- package/dist/facade/scene-radiance.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +88 -19
- package/dist/facade/simulate-capture.js +374 -145
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.d.ts +3 -2
- package/dist/facade/simulate-display-view.js +36 -18
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/chart-composite-gpu.d.ts +13 -1
- package/dist/gpu/chart-composite-gpu.js +48 -9
- package/dist/gpu/chart-composite-gpu.js.map +1 -1
- package/dist/gpu/chart-radiance-gpu.js +4 -30
- package/dist/gpu/chart-radiance-gpu.js.map +1 -1
- package/dist/gpu/display-view-composite-material-module.d.ts +10 -4
- package/dist/gpu/display-view-composite-material-module.js +39 -13
- package/dist/gpu/display-view-composite-material-module.js.map +1 -1
- package/dist/gpu/display-view-image-material-module.d.ts +86 -0
- package/dist/gpu/display-view-image-material-module.js +339 -0
- package/dist/gpu/display-view-image-material-module.js.map +1 -0
- package/dist/gpu/display-view-image-quality.d.ts +45 -0
- package/dist/gpu/display-view-image-quality.js +380 -0
- package/dist/gpu/display-view-image-quality.js.map +1 -0
- package/dist/gpu/display-view-quality-gpu.d.ts +67 -1
- package/dist/gpu/display-view-quality-gpu.js +136 -205
- package/dist/gpu/display-view-quality-gpu.js.map +1 -1
- package/dist/gpu/display-view-speed-gpu.d.ts +51 -3
- package/dist/gpu/display-view-speed-gpu.js +131 -93
- package/dist/gpu/display-view-speed-gpu.js.map +1 -1
- package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +29 -15
- package/dist/gpu/wgsl-display-view-quality-kernel.js +58 -36
- package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
- package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +21 -6
- package/dist/gpu/wgsl-display-view-speed-kernel.js +46 -11
- package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
- package/dist/gpu/wgsl-exact-sum.d.ts +20 -0
- package/dist/gpu/wgsl-exact-sum.js +42 -0
- package/dist/gpu/wgsl-exact-sum.js.map +1 -0
- package/dist/index.d.ts +18 -3
- package/dist/index.js +12 -1
- package/dist/index.js.map +1 -1
- package/dist/motion/subject-motion.d.ts +66 -0
- package/dist/motion/subject-motion.js +52 -0
- package/dist/motion/subject-motion.js.map +1 -0
- package/dist/optics/aperture-diameter.d.ts +47 -0
- package/dist/optics/aperture-diameter.js +50 -0
- package/dist/optics/aperture-diameter.js.map +1 -0
- package/dist/optics/detail-limit.d.ts +182 -0
- package/dist/optics/detail-limit.js +336 -0
- package/dist/optics/detail-limit.js.map +1 -0
- package/dist/optics/generic-lens-lateral-color.d.ts +83 -0
- package/dist/optics/generic-lens-lateral-color.js +77 -0
- package/dist/optics/generic-lens-lateral-color.js.map +1 -0
- package/dist/optics/generic-lens-psf.d.ts +18 -2
- package/dist/optics/generic-lens-psf.js +79 -48
- package/dist/optics/generic-lens-psf.js.map +1 -1
- package/dist/optics/lens-psf-profile.d.ts +7 -0
- package/dist/optics/lens-psf-profile.js +2 -1
- package/dist/optics/lens-psf-profile.js.map +1 -1
- package/dist/optics/scene-to-sensor-irradiance.d.ts +13 -0
- package/dist/optics/scene-to-sensor-irradiance.js +7 -0
- package/dist/optics/scene-to-sensor-irradiance.js.map +1 -1
- package/dist/output/crop-to-match-focal-length.d.ts +62 -0
- package/dist/output/crop-to-match-focal-length.js +63 -0
- package/dist/output/crop-to-match-focal-length.js.map +1 -0
- package/dist/output/developed-lens-corrections.d.ts +19 -5
- package/dist/output/developed-lens-corrections.js +30 -11
- package/dist/output/developed-lens-corrections.js.map +1 -1
- package/dist/output/generic-lens-corrections.d.ts +41 -3
- package/dist/output/generic-lens-corrections.js +57 -9
- package/dist/output/generic-lens-corrections.js.map +1 -1
- package/dist/output/lens-corrections.d.ts +12 -2
- package/dist/output/lens-corrections.js +22 -7
- package/dist/output/lens-corrections.js.map +1 -1
- package/dist/output/print-contrast-reference.js +1 -1
- package/dist/output/print-contrast-reference.js.map +1 -1
- package/dist/output/print-plan.d.ts +41 -5
- package/dist/output/print-plan.js +59 -14
- package/dist/output/print-plan.js.map +1 -1
- package/dist/output/print-size-blur-noise.d.ts +110 -0
- package/dist/output/print-size-blur-noise.js +138 -0
- package/dist/output/print-size-blur-noise.js.map +1 -0
- package/dist/sensor/binomial-sample.d.ts +3 -0
- package/dist/sensor/binomial-sample.js +86 -0
- package/dist/sensor/binomial-sample.js.map +1 -0
- package/dist/sensor/eqe-electron-rate.js +1 -7
- package/dist/sensor/eqe-electron-rate.js.map +1 -1
- package/dist/sensor/exposure-window.d.ts +2 -0
- package/dist/sensor/exposure-window.js +2 -2
- package/dist/sensor/exposure-window.js.map +1 -1
- package/dist/sensor/readout-timing.js +2 -23
- package/dist/sensor/readout-timing.js.map +1 -1
- package/dist/sensor/response-application-compatibility.js +1 -6
- package/dist/sensor/response-application-compatibility.js.map +1 -1
- package/dist/sensor/responsivity-photocurrent.js +1 -7
- package/dist/sensor/responsivity-photocurrent.js.map +1 -1
- package/dist/sensor/sensor-characteristics.d.ts +147 -0
- package/dist/sensor/sensor-characteristics.js +126 -0
- package/dist/sensor/sensor-characteristics.js.map +1 -0
- package/dist/sensor/spatial-sample-reduction.js +1 -5
- package/dist/sensor/spatial-sample-reduction.js.map +1 -1
- package/dist/sensor/spectral-quadrature.js +1 -6
- package/dist/sensor/spectral-quadrature.js.map +1 -1
- package/dist/sensor/temporal-photo-signal.d.ts +3 -0
- package/dist/sensor/temporal-photo-signal.js +5 -2
- package/dist/sensor/temporal-photo-signal.js.map +1 -1
- package/dist/stabilization/system.d.ts +9 -0
- package/dist/stabilization/system.js +5 -6
- package/dist/stabilization/system.js.map +1 -1
- package/docs/AGENT_GUIDE.md +5 -0
- package/docs/API_REFERENCE.md +173 -2
- package/docs/QUICK_START.md +213 -6
- package/docs/USAGE.md +157 -3
- package/docs/api/capture-native-raw-dng.md +6 -0
- package/docs/api/core-version.md +1 -1
- package/docs/api/equipment-generic-lens-catalogue.md +17 -7
- package/docs/api/equipment-sensor-profile-body.md +64 -0
- package/docs/api/equipment-sensor-profiles.md +86 -0
- package/docs/api/exports.json +357 -2
- package/docs/api/facade-display-view-gpu.md +5 -2
- package/docs/api/facade-exposure-build-up.md +105 -0
- package/docs/api/facade-fast-methods.md +99 -0
- package/docs/api/facade-image-plane-enable.md +16 -0
- package/docs/api/facade-mixed-illumination.md +67 -0
- package/docs/api/facade-representative-light-sources.md +131 -0
- package/docs/api/facade-scene-radiance.md +54 -3
- package/docs/api/facade-simulate-capture.md +60 -15
- package/docs/api/facade-simulate-display-view.md +3 -2
- package/docs/api/motion-subject-motion.md +23 -0
- package/docs/api/optics-aperture-diameter.md +51 -0
- package/docs/api/optics-detail-limit.md +203 -0
- package/docs/api/optics-generic-lens-lateral-color.md +79 -0
- package/docs/api/output-crop-to-match-focal-length.md +67 -0
- package/docs/api/output-developed-lens-corrections.md +10 -1
- package/docs/api/output-generic-lens-corrections.md +40 -2
- package/docs/api/output-lens-corrections.md +11 -1
- package/docs/api/output-print-plan.md +35 -1
- package/docs/api/output-print-size-blur-noise.md +115 -0
- package/docs/api/sensor-sensor-characteristics.md +105 -0
- package/llms.txt +6 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"print-plan.js","sourceRoot":"","sources":["../../src/output/print-plan.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC;;;GAGG;AACH,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAA0B,MAAM,+BAA+B,CAAC;AAC9G,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAC/F,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AACtF,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAoC,MAAM,uBAAuB,CAAC;AA2DjG,MAAM,OAAO,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAW,CAAC;AAClE,MAAM,cAAc,GAAG,IAAI,CAAC,EAAE,GAAG,KAAK,CAAC;AACvC,SAAS,MAAM,CAAC,KAAc,EAAE,IAAuB;IACrD,OAAO,wBAAwB,CAAC,KAAK,EAAE,IAAI,EAAE,2CAA2C,CAAC,CAAC;AAC5F,CAAC;AACD,SAAS,MAAM,CAAC,KAAc,EAAE,IAAY;IAC1C,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,MAAM,IAAI,yBAAyB,CAAC,GAAG,IAAI,mBAAmB,CAAC,CAAC;IAC/F,qBAAqB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAAC,OAAO,KAAK,CAAC;AACnD,CAAC;AACD,SAAS,IAAI,CAAC,KAAc;IAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC;QAAE,MAAM,IAAI,yBAAyB,CAAC,4BAA4B,CAAC,CAAC;IACnI,OAAO,KAA4B,CAAC;AACtC,CAAC;AACD,SAAS,MAAM,CAAC,KAAc;IAC5B,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,EAAE,cAAc,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;AACxE,CAAC;AACD,SAAS,MAAM,CAAC,KAAc;IAC5B,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC;IACvD,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;IAC1G,sBAAsB,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IAAC,sBAAsB,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC;IACrG,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;AACrC,CAAC;AACD,SAAS,QAAQ,CAAC,KAAc;IAC9B,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,cAAc,CAAC,CAAC,CAAC;IAC/H,MAAM,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC;IAAC,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;IACvE,KAAK,MAAM,GAAG,IAAI,CAAC,mBAAmB,EAAE,gBAAgB,CAAU,EAAE,CAAC;QACnE,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,SAAS;YAAE,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC1E,CAAC;IACD,IAAI,CAAC,CAAC,YAAY,KAAK,SAAS;QAAE,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;IACzD,wFAAwF;IACxF,MAAM,MAAM,GAAG,CAA2C,CAAC;IAC3D,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAC/B,OAAO,uBAAuB,CAAC,MAAM,CAAC,CAAC;AACzC,CAAC;AACD,SAAS,uBAAuB,CAAC,CAA8B;IAC7D,OAAO,EAAE,WAAW,EAAE,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,YAAY,EAAE,EAAE,GAAG,CAAC,CAAC,YAAY,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW;QACzG,GAAG,CAAC,CAAC,CAAC,iBAAiB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,EAAE,GAAG,CAAC,CAAC,iBAAiB,EAAE,EAAE,CAAC;QAC/F,GAAG,CAAC,CAAC,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,EAAE,GAAG,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC;QACtF,GAAG,CAAC,CAAC,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,EAAE,GAAG,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,EAAE,CAAC;AACvF,CAAC;AACD,SAAS,QAAQ,CAAC,KAAc;IAC9B,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,2BAA2B,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,eAAe,CAAC,CAAC,CAAC;IACvK,IAAI,MAA8B,CAAC;IACnC,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;QACjB,KAAK,YAAY,CAAC,CAAC,CAAC;YAClB,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC;YACnD,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC,aAAa,EAAE,eAAe,CAAC,EAAE,CAAC;QACzF,CAAC;QACD,KAAK,qBAAqB,CAAC,CAAC,CAAC;YAC3B,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,2BAA2B,CAAC,CAAC,CAAC;YAC/D,MAAM,GAAG,EAAE,IAAI,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,MAAM,CAAC,CAAC,CAAC,yBAAyB,EAAE,2BAA2B,CAAC,EAAE,CAAC;YAAC,MAAM;QAC/I,CAAC;QACD,KAAK,gBAAgB,CAAC,CAAC,CAAC;YACtB,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,uBAAuB,EAAE,kBAAkB,CAAC,CAAC,CAAC;YAC/E,MAAM,gBAAgB,GAAG,MAAM,CAAC,CAAC,CAAC,gBAAgB,EAAE,kBAAkB,CAAC,CAAC;YAAC,sBAAsB,CAAC,kBAAkB,EAAE,gBAAgB,CAAC,CAAC;YACtI,MAAM,GAAG,EAAE,IAAI,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,CAAC,CAAC,CAAC,qBAAqB,EAAE,uBAAuB,CAAC,EAAE,gBAAgB,EAAE,CAAC;YAAC,MAAM;QAChJ,CAAC;QACD,KAAK,mBAAmB,CAAC,CAAC,CAAC;YACzB,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,kBAAkB,EAAE,kBAAkB,CAAC,CAAC,CAAC;YAC1E,MAAM,gBAAgB,GAAG,MAAM,CAAC,CAAC,CAAC,gBAAgB,EAAE,kBAAkB,CAAC,CAAC;YAAC,sBAAsB,CAAC,kBAAkB,EAAE,gBAAgB,CAAC,CAAC;YACtI,IAAI,gBAAgB,GAAG,CAAC;gBAAE,MAAM,IAAI,yBAAyB,CAAC,mDAAmD,CAAC,CAAC;YACnH,MAAM,GAAG,EAAE,IAAI,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,CAAC,CAAC,CAAC,gBAAgB,EAAE,kBAAkB,CAAC,EAAE,gBAAgB,EAAE,CAAC;YAAC,MAAM;QACpI,CAAC;QACD,OAAO,CAAC,CAAC,MAAM,IAAI,yBAAyB,CAAC,oCAAoC,CAAC,CAAC;IACrF,CAAC;IACD,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACnC,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,KAAK;QAAE,MAAM,IAAI,yBAAyB,CAAC,kEAAkE,CAAC,CAAC;IAC1I,OAAO,MAAM,CAAC;AAChB,CAAC;AACD,SAAS,YAAY,CAAC,CAA0D;IAC9E,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;QACf,KAAK,qBAAqB,CAAC,CAAC,OAAO,CAAC,CAAC,yBAAyB,CAAC;QAC/D,KAAK,gBAAgB,CAAC,CAAC,OAAO,CAAC,CAAC,qBAAqB,GAAG,CAAC,CAAC,gBAAgB,CAAC;QAC3E,KAAK,mBAAmB,CAAC,CAAC,OAAO,CAAC,CAAC,gBAAgB,GAAG,CAAC,CAAC,gBAAgB,CAAC;IAC3E,CAAC;AACH,CAAC;AAED,sHAAsH;AACtH,MAAM,UAAU,mBAAmB,CAAC,KAAc;IAChD,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,cAAc,EAAE,iBAAiB,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC;IACtG,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC7E,IAAI,YAA+B,CAAC;IACpC,IAAI,MAAM,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;QACtC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC;QACpD,YAAY,GAAG,EAAE,IAAI,EAAE,iBAAiB,EAAE,SAAS,EAAE,qBAAqB,CAAC,MAAM,CAAC,SAAS,EAAE,4BAA4B,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;IACpK,CAAC;SAAM,IAAI,MAAM,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QACzC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;QACrC,IAAI,MAAM,CAAC,MAAM,KAAK,uBAAuB,IAAI,MAAM,CAAC,MAAM,KAAK,4BAA4B;YAAE,MAAM,IAAI,yBAAyB,CAAC,oCAAoC,CAAC,CAAC;QAC3K,YAAY,GAAG,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;IAChE,CAAC;;QAAM,MAAM,IAAI,yBAAyB,CAAC,6BAA6B,CAAC,CAAC;IAC1E,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;IAClE,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,4BAA4B,CAAC,CAAC,CAAC;IAClE,IAAI,GAAG,CAAC,IAAI,KAAK,yBAAyB,IAAI,OAAO,GAAG,CAAC,0BAA0B,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,0BAA0B,CAAC,IAAI,GAAG,CAAC,0BAA0B,GAAG,CAAC,IAAI,GAAG,CAAC,0BAA0B,GAAG,IAAI;QAAE,MAAM,IAAI,yBAAyB,CAAC,qDAAqD,CAAC,CAAC;IAChU,MAAM,MAAM,GAAmB,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,oBAAoB,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,qBAAqB,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,eAAe,EAAE,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,yBAAyB,EAAE,0BAA0B,EAAE,GAAG,CAAC,0BAA0B,EAAE,EAAE,CAAC;IAC3X,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC7B,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,eAAe,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,CAAC,CAAC,CAAC;QACzF,MAAM,CAAC,QAAQ,GAAG;YAChB,GAAG,CAAC,CAAC,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC;YACpF,GAAG,CAAC,CAAC,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC;YAC9E,GAAG,CAAC,CAAC,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE,CAAC;YACzF,GAAG,CAAC,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,CAAC;SAClF,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AACD,SAAS,GAAG,CAAC,CAAS,EAAE,CAAS,IAAY,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;IAAC,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IAAC,CAAC,GAAG,CAAC,CAAC;IAAC,CAAC,GAAG,IAAI,CAAC;AAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AACjH,SAAS,iBAAiB,CAAC,GAAG,MAAgB,IAAa,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACnH,0FAA0F;AAC1F,SAAS,gBAAgB,CAAC,KAAa,EAAE,SAAwB;IAC/D,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IAAC,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACzE,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7E,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;AAC5B,CAAC;AACD,SAAS,UAAU,CAAC,CAAmB,EAAE,CAAmB;IAC1D,OAAO,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;AACvG,CAAC;AACD,SAAS,aAAa,CAAC,MAAwB,EAAE,KAAa,EAAE,MAAc;IAC5E,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;IAC3D,MAAM,CAAC,GAAG,MAAM,CAAC,UAAU,GAAG,OAAO,EAAE,CAAC,GAAG,MAAM,CAAC,WAAW,GAAG,OAAO,CAAC;IACxE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;IACvE,MAAM,UAAU,GAAG,KAAK,GAAG,CAAC,EAAE,WAAW,GAAG,KAAK,GAAG,CAAC,CAAC;IACtD,OAAO,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACpH,CAAC;AACD,SAAS,WAAW,CAAC,MAAwB,EAAE,KAAa,EAAE,MAAc,EAAE,QAAgB;IAC5F,MAAM,EAAE,GAAG,KAAK,GAAG,MAAM,CAAC,UAAU,EAAE,EAAE,GAAG,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC;IACvE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,EAAE,CAAC,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC,EAAE,IAAI,GAAG,EAAE,EAAE,EAAE,2BAA2B,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,cAAc,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,cAAc,EAAE,EAAE,CAAC;AACpN,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAqB;IACtD,MAAM,KAAK,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;IACzC,MAAM,IAAI,GAAc,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,sBAAsB,EAAE,eAAe,EAAE,OAAO,EAAE,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,qBAAqB,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,wBAAwB,EAAE,YAAY,EAAE,kBAAkB,EAAE,YAAY,EAAE,CAAC;IACpa,MAAM,MAAM,GAAG,GAAiC,EAAE,CAAC,gBAAgB,CAAC,IAAI,EAAE,wBAAwB,EAAE,OAAO,EAAE,CAAC,wEAAwE,EAAE,sEAAsE,EAAE,+FAA+F,CAAC,CAAC,CAAC;IAClW,MAAM,WAAW,GAAG,GAAiC,EAAE,GAAG,IAAI,CAAC,MAAM,GAAG,aAAa,CAAC,CAAC,IAAI,CAAC,sBAAsB,GAAG,eAAe,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC,CAAC,OAAO,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IACzM,MAAM,KAAK,GAAG,KAAK,CAAC,YAAY,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAC,eAAe,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;IAC/N,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC;QAAE,OAAO,WAAW,EAAE,CAAC;IACtE,IAAI,CAAC,cAAc,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,CAAC,iBAAiB,GAAG,QAAQ,CAAC;IAC3E,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACzF,MAAM,WAAW,GAAG,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;IACvF,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC;IACzH,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC;IAC/F,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,GAAG,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,OAAO,WAAW,EAAE,CAAC;IACxF,MAAM,QAAQ,GAAG,EAAE,oBAAoB,EAAE,GAAG,EAAE,mBAAmB,EAAE,KAAK,EAAE,yBAAyB,EAAE,KAAK,EAAE,CAAC;IAC7G,IAAI,CAAC,QAAQ,GAAG,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,QAAQ,EAAE,sBAAsB,EAAE,OAAO,EAAE,CAAC,8DAA8D,CAAC,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,QAAQ,EAAE,iCAAiC,EAAE,OAAO,EAAE,CAAC,4FAA4F,EAAE,2EAA2E,CAAC,CAAC,CAAC;IACxZ,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAAC,OAAO,MAAM,EAAE,CAAC;IAAC,CAAC;IACrG,MAAM,QAAQ,GAAG,sBAAsB,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC;IACrE,MAAM,MAAM,GAAG,EAAE,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;IAC5G,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9F,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC;QAAE,OAAO,WAAW,EAAE,CAAC;IACxD,IAAI,CAAC,mBAAmB,GAAG,WAAW,CAAC;IACvC,MAAM,iBAAiB,GAAG,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;IACvE,IAAI,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC,EAAE,iBAAiB,CAAC,aAAa,CAAC,CAAC,EAAE,iBAAiB,CAAC,2BAA2B,CAAC,CAAC,EAAE,iBAAiB,CAAC,2BAA2B,CAAC,CAAC,CAAC;QAAE,OAAO,WAAW,EAAE,CAAC;IACrN,IAAI,CAAC,MAAM,GAAG,iBAAiB,CAAC;IAChC,IAAI,WAAW,GAAG,KAAK,CAAC,GAAG,CAAC,0BAA0B,EAAE,CAAC;QAAC,IAAI,CAAC,MAAM,GAAG,4BAA4B,CAAC;QAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;QAAC,OAAO,MAAM,EAAE,CAAC;IAAC,CAAC;IAC/L,MAAM,WAAW,GAAG,KAAK,CAAC,YAAY,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC;IACnG,MAAM,YAAY,GAAG,KAAK,CAAC,YAAY,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC;IACtG,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY;QACnD,CAAC,CAAC,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,GAAG,EAAE,YAAY,GAAG,GAAG,CAAC;QAC9D,CAAC,CAAC,aAAa,CAAC,MAAM,EAAE,KAAK,GAAG,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC;IACzD,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,WAAW,EAAE,CAAC;IAC5C,IAAI,CAAC,qBAAqB,GAAG,QAAQ,CAAC;IACtC,MAAM,UAAU,GAAG,QAAQ,CAAC,UAAU,IAAI,MAAM,CAAC,UAAU,IAAI,QAAQ,CAAC,WAAW,IAAI,MAAM,CAAC,WAAW,CAAC;IAC1G,IAAI,CAAC,sBAAsB,GAAG,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,cAAc,CAAC;IACzE,IAAI,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,GAAG,KAAK,GAAG,KAAK,EAAE,MAAM,CAAC,WAAW,GAAG,KAAK,GAAG,MAAM,CAAC,CAAC;IAChG,IAAI,eAAe,GAAG,WAAW,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,WAAW,CAAC;IACnI,IAAI,QAAQ,GAAG,KAAK,GAAG,QAAQ,EAAE,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC/D,IAAI,CAAC,iBAAiB,CAAC,QAAQ,EAAE,SAAS,CAAC,IAAI,CAAC,eAAe,KAAK,IAAI,IAAI,CAAC,iBAAiB,CAAC,eAAe,CAAC,CAAC;QAAE,OAAO,WAAW,EAAE,CAAC;IACvI,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,CAAS,EAAE,CAAS,EAAW,EAAE;QACxD,MAAM,SAAS,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,GAAG,GAAG,EAAE,CAAC,GAAG,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACrJ,OAAO,SAAS,KAAK,IAAI,IAAI,SAAS,CAAC,UAAU,IAAI,MAAM,CAAC,UAAU,IAAI,SAAS,CAAC,WAAW,IAAI,MAAM,CAAC,WAAW,CAAC;IACxH,CAAC,CAAC;IACF,8EAA8E;IAC9E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAChE,QAAQ,GAAG,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAAC,QAAQ,GAAG,KAAK,GAAG,QAAQ,CAAC;QAAC,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC5G,CAAC;IACD,IAAI,CAAC,iBAAiB,CAAC,QAAQ,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,KAAK,CAAC;QAAE,OAAO,WAAW,EAAE,CAAC;IACvG,IAAI,eAAe,KAAK,IAAI,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;QACrD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,eAAe,GAAG,WAAW,CAAC,EAAE,CAAC,EAAE;YAAE,eAAe,GAAG,gBAAgB,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;QAC7I,IAAI,CAAC,iBAAiB,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,eAAe,GAAG,WAAW,CAAC;YAAE,OAAO,WAAW,EAAE,CAAC;IACvH,CAAC;IACD,IAAI,CAAC,YAAY,GAAG,EAAE,+BAA+B,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,qCAAqC,EAAE,eAAe,EAAE,CAAC;IACxJ,IAAI,CAAC,UAAU,EAAE,CAAC;QAAC,IAAI,CAAC,MAAM,GAAG,4BAA4B,CAAC;QAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;IAAC,CAAC;IACvH,IAAI,IAAI,GAAG,QAAQ,CAAC;IACpB,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;IAChC,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,IAAI,CAAC,EAAE,QAAQ,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC;QAC3E,MAAM,WAAW,GAAG,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,aAAa,EAAE,UAAU,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,aAAa,EAAE,WAAW,IAAI,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,CAAC,CAAC;QAC3O,IAAI,WAAW,KAAK,IAAI;YAAE,OAAO,WAAW,EAAE,CAAC;QAC/C,IAAI,GAAG,QAAQ,CAAC,WAAW,IAAI,WAAW,CAAC;QAC3C,IAAI,QAAQ,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YACpC,MAAM,KAAK,GAAG,EAAE,UAAU,EAAE,WAAW,GAAG,QAAQ,CAAC,QAAQ,EAAE,WAAW,EAAE,YAAY,GAAG,QAAQ,CAAC,QAAQ,EAAE,CAAC;YAC7G,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,WAAW,CAAC;gBAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;iBACtJ,IAAI,QAAQ,CAAC,WAAW,IAAI,CAAC,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC,UAAU,IAAI,KAAK,CAAC,WAAW,KAAK,IAAI,CAAC,WAAW,CAAC;gBAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;;gBAC1K,IAAI,GAAG,KAAK,CAAC;QACpB,CAAC;QACD,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC;YAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;QACpF,IAAI,IAAI,CAAC,UAAU,GAAG,WAAW,CAAC,UAAU,IAAI,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC,WAAW;YAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;QAC1J,IAAI,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW;YAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;QACxI,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC,EAAE,CAAC;YAAC,IAAI,CAAC,MAAM,GAAG,mBAAmB,CAAC;YAAC,OAAO,MAAM,EAAE,CAAC;QAAC,CAAC;IACnJ,CAAC;IACD,IAAI,CAAC,UAAU;QAAE,OAAO,MAAM,EAAE,CAAC;IACjC,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC5D,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC,EAAE,QAAQ,CAAC,2BAA2B,CAAC,CAAC,EAAE,QAAQ,CAAC,2BAA2B,CAAC,CAAC,CAAC;QAAE,OAAO,WAAW,EAAE,CAAC;IACjL,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC;IAAC,IAAI,CAAC,WAAW,GAAG,QAAQ,CAAC;IACnD,IAAI,CAAC,eAAe,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC,EAAE,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IAC5G,OAAO,MAAM,EAAE,CAAC;AAClB,CAAC;AAiBD;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAA0B;IAChE,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC,CAAC;IAC3D,IAAI,CAAC,CAAC,qBAAqB,KAAK,OAAO,IAAI,CAAC,CAAC,qBAAqB,KAAK,4BAA4B;QAAE,MAAM,IAAI,yBAAyB,CAAC,0CAA0C,CAAC,CAAC;IACrL,MAAM,aAAa,GAAG,kBAAkB,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;IAC5E,MAAM,MAAM,GAAmB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,EAAE,eAAe,EAAE,cAAc,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,GAAG,aAAa,CAAC,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAC3K,IAAI,aAAa,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvG,MAAM,CAAC,MAAM,GAAG,aAAa,CAAC;QAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;IACzG,CAAC;SAAM,IAAI,aAAa,CAAC,MAAM,KAAK,aAAa;QAAE,MAAM,CAAC,MAAM,GAAG,aAAa,CAAC;SAC5E,IAAI,aAAa,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;QAC7C,IAAI,CAAC,CAAC,qBAAqB,KAAK,4BAA4B,IAAI,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YACnH,MAAM,CAAC,MAAM,GAAG,aAAa,CAAC,sBAAsB,KAAK,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,kBAAkB,CAAC;QAC3G,CAAC;aAAM,CAAC;YAAC,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC;YAAC,MAAM,CAAC,cAAc,GAAG,aAAa,CAAC,YAAY,CAAC,+BAA+B,CAAC;QAAC,CAAC;IAC1H,CAAC;IACD,OAAO,gBAAgB,CAAC,MAAM,EAAE,qCAAqC,EAAE,OAAO,EAAE,CAAC,gFAAgF,EAAE,uEAAuE,CAAC,CAAC,CAAC;AAC/O,CAAC","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/** Print planning: how many native pixels a print of a given size and viewing distance needs.\n * It uses native capture pixels only. It does not make an image or certify perceived quality.\n * @see docs/PRINT_PLANNING.md for equations, rounding and support limits.\n */\nimport { calculatedResult, approximationResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { requireAllowlistedRecord, requirePublicOpaqueId } from \"../core/record-validation.js\";\nimport { requirePositiveFinite, requirePositiveInteger } from \"../core/validation.js\";\nimport { ENGINE_API_VERSION } from \"../core/version.js\";\nimport { resolveCaptureGeometry, type ResolveCaptureGeometryInput } from \"./capture-geometry.js\";\nimport type { RasterDimensions } from \"../sensor/sensor-geometry.js\";\n\n/** A physical length. Not to be confused with image pixels or printer dots. */\nexport interface PrintLength { value: number; unit: \"mm\" | \"cm\" | \"inches\" | \"m\" }\n/** The flat printed image area. It excludes paper margins, borders and canvas wraps. */\nexport interface PrintedImageSize { width: number; height: number; unit: PrintLength[\"unit\"] }\n/** How to choose pixel density: one of three angular criteria at the viewing distance, or a manual PPI (pixels per inch), which gives a different kind of plan. */\nexport type PrintSamplingCriterion =\n | { kind: \"angular-pixel-pitch\"; maximumArcminutesPerPixel: number }\n | { kind: \"angular-stroke\"; strokeWidthArcminutes: number; samplesPerStroke: number }\n | { kind: \"angular-line-pair\"; periodArcminutes: number; samplesPerPeriod: number }\n | { kind: \"manual-ppi\"; pixelsPerInch: number };\n/** The declared native source geometry. Not a preview or a resampled pixel count. */\nexport type PrintNativeSource =\n | { kind: \"native-retained\"; captureId: string; geometry: ResolveCaptureGeometryInput }\n | { kind: \"unavailable\"; reason: \"missing-native-raster\" | \"unverified-native-identity\" };\n/** Print lab requirements supplied by the caller. There is no vendor catalogue and no guarantee of what a lab will accept. */\nexport interface PrintProviderConstraints {\n minimumRaster?: RasterDimensions;\n exactRaster?: RasterDimensions;\n minimumPpi?: number;\n exactPpi?: number;\n}\n/** Plan input. The output keeps the exact native aspect ratio; the allowed physical aspect error is explicit and reported. */\nexport interface PrintPlanInput {\n source: PrintNativeSource;\n printedImage: PrintedImageSize;\n viewingDistance: PrintLength;\n sampling: PrintSamplingCriterion;\n fit: { kind: \"confirmed-native-aspect\"; maximumRelativeAspectError: number };\n provider?: PrintProviderConstraints;\n}\n/** Pixel density and angular pixel pitch for each axis. The angles are pure geometry, not a measure of visual acuity. */\nexport interface PrintRasterSampling {\n raster: RasterDimensions;\n pixelsPerInch: { x: number; y: number };\n angularPixelPitchArcminutes: { x: number; y: number };\n}\n/** A plan, valid only under its stated conditions. A null recommendation never means it is fine to upscale. */\nexport interface PrintPlan {\n engineApiVersion: string;\n input: PrintPlanInput;\n status: \"ready\" | \"insufficient-native-pixels\" | \"provider-conflict\" | \"crop-confirmation-required\" | \"cannot-assess\" | \"unsupported\";\n nativePixelSufficiency: \"sufficient\" | \"insufficient\" | \"cannot-assess\";\n reasons: string[];\n printedImageMm: { width: number; height: number } | null;\n viewingDistanceMm: number | null;\n guidance: CalculationResult<{ derivedPixelsPerInch: number; maximumPixelPitchMm: number; maximumArcminutesPerPixel: number | null }> | null;\n relativeAspectError: number | null;\n native: PrintRasterSampling | null;\n minimumSamplingRaster: RasterDimensions | null;\n recommended: PrintRasterSampling | null;\n resamplingScale: { x: number; y: number } | null;\n alternatives: { maximumImageMmAtCurrentDistance: { width: number; height: number }; minimumViewingDistanceMmAtCurrentSize: number | null } | null;\n capturedDetailAssessment: \"unassessed\";\n deliveryAssessment: \"unassessed\";\n}\n\nconst UNIT_MM = { mm: 1, cm: 10, inches: 25.4, m: 1000 } as const;\nconst RAD_PER_ARCMIN = Math.PI / 10800;\nfunction record(value: unknown, keys: readonly string[]): Record<string, unknown> {\n return requireAllowlistedRecord(value, keys, \"Invalid or unknown Print planning fields.\");\n}\nfunction number(value: unknown, name: string): number {\n if (typeof value !== \"number\") throw new InvalidConfigurationError(`${name} must be numeric.`);\n requirePositiveFinite(name, value); return value;\n}\nfunction unit(value: unknown): PrintLength[\"unit\"] {\n if (typeof value !== \"string\" || !Object.hasOwn(UNIT_MM, value)) throw new InvalidConfigurationError(\"Invalid Print length unit.\");\n return value as PrintLength[\"unit\"];\n}\nfunction length(value: unknown): PrintLength {\n const r = record(value, [\"value\", \"unit\"]);\n return { value: number(r.value, \"length.value\"), unit: unit(r.unit) };\n}\nfunction raster(value: unknown): RasterDimensions {\n const r = record(value, [\"pixelWidth\", \"pixelHeight\"]);\n const pixelWidth = number(r.pixelWidth, \"pixelWidth\"), pixelHeight = number(r.pixelHeight, \"pixelHeight\");\n requirePositiveInteger(\"pixelWidth\", pixelWidth); requirePositiveInteger(\"pixelHeight\", pixelHeight);\n return { pixelWidth, pixelHeight };\n}\nfunction geometry(value: unknown): ResolveCaptureGeometryInput {\n const g = record(value, [\"imagingArea\", \"nativeRaster\", \"orientation\", \"activeCaptureRect\", \"outputCropRect\", \"outputRaster\"]);\n record(g.imagingArea, [\"widthMm\", \"heightMm\"]); raster(g.nativeRaster);\n for (const key of [\"activeCaptureRect\", \"outputCropRect\"] as const) {\n if (g[key] !== undefined) record(g[key], [\"x\", \"y\", \"width\", \"height\"]);\n }\n if (g.outputRaster !== undefined) raster(g.outputRaster);\n // The capture geometry module defines units, orientations, bounds and the optical axis.\n const parsed = g as unknown as ResolveCaptureGeometryInput;\n resolveCaptureGeometry(parsed);\n return structuredCloneGeometry(parsed);\n}\nfunction structuredCloneGeometry(g: ResolveCaptureGeometryInput): ResolveCaptureGeometryInput {\n return { imagingArea: { ...g.imagingArea }, nativeRaster: { ...g.nativeRaster }, orientation: g.orientation,\n ...(g.activeCaptureRect === undefined ? {} : { activeCaptureRect: { ...g.activeCaptureRect } }),\n ...(g.outputCropRect === undefined ? {} : { outputCropRect: { ...g.outputCropRect } }),\n ...(g.outputRaster === undefined ? {} : { outputRaster: { ...g.outputRaster } }) };\n}\nfunction sampling(value: unknown): PrintSamplingCriterion {\n const tag = record(value, [\"kind\", \"maximumArcminutesPerPixel\", \"strokeWidthArcminutes\", \"samplesPerStroke\", \"periodArcminutes\", \"samplesPerPeriod\", \"pixelsPerInch\"]);\n let result: PrintSamplingCriterion;\n switch (tag.kind) {\n case \"manual-ppi\": {\n const r = record(value, [\"kind\", \"pixelsPerInch\"]);\n return { kind: \"manual-ppi\", pixelsPerInch: number(r.pixelsPerInch, \"pixelsPerInch\") };\n }\n case \"angular-pixel-pitch\": {\n const r = record(value, [\"kind\", \"maximumArcminutesPerPixel\"]);\n result = { kind: \"angular-pixel-pitch\", maximumArcminutesPerPixel: number(r.maximumArcminutesPerPixel, \"maximumArcminutesPerPixel\") }; break;\n }\n case \"angular-stroke\": {\n const r = record(value, [\"kind\", \"strokeWidthArcminutes\", \"samplesPerStroke\"]);\n const samplesPerStroke = number(r.samplesPerStroke, \"samplesPerStroke\"); requirePositiveInteger(\"samplesPerStroke\", samplesPerStroke);\n result = { kind: \"angular-stroke\", strokeWidthArcminutes: number(r.strokeWidthArcminutes, \"strokeWidthArcminutes\"), samplesPerStroke }; break;\n }\n case \"angular-line-pair\": {\n const r = record(value, [\"kind\", \"periodArcminutes\", \"samplesPerPeriod\"]);\n const samplesPerPeriod = number(r.samplesPerPeriod, \"samplesPerPeriod\"); requirePositiveInteger(\"samplesPerPeriod\", samplesPerPeriod);\n if (samplesPerPeriod < 2) throw new InvalidConfigurationError(\"A line-pair period requires at least two samples.\");\n result = { kind: \"angular-line-pair\", periodArcminutes: number(r.periodArcminutes, \"periodArcminutes\"), samplesPerPeriod }; break;\n }\n default: throw new InvalidConfigurationError(\"Invalid Print sampling convention.\");\n }\n const angle = angularLimit(result);\n if (angle <= 0 || angle >= 10800) throw new InvalidConfigurationError(\"Effective angular pixel pitch must lie in (0, 10800) arcminutes.\");\n return result;\n}\nfunction angularLimit(s: Exclude<PrintSamplingCriterion, { kind: \"manual-ppi\" }>): number {\n switch (s.kind) {\n case \"angular-pixel-pitch\": return s.maximumArcminutesPerPixel;\n case \"angular-stroke\": return s.strokeWidthArcminutes / s.samplesPerStroke;\n case \"angular-line-pair\": return s.periodArcminutes / s.samplesPerPeriod;\n }\n}\n\n/** Validates and copies an untrusted print request. It never assumes the source is native if that is not declared. */\nexport function parsePrintPlanInput(value: unknown): PrintPlanInput {\n const r = record(value, [\"source\", \"printedImage\", \"viewingDistance\", \"sampling\", \"fit\", \"provider\"]);\n const source = record(r.source, [\"kind\", \"captureId\", \"geometry\", \"reason\"]);\n let parsedSource: PrintNativeSource;\n if (source.kind === \"native-retained\") {\n record(r.source, [\"kind\", \"captureId\", \"geometry\"]);\n parsedSource = { kind: \"native-retained\", captureId: requirePublicOpaqueId(source.captureId, \"Invalid public capture ID.\"), geometry: geometry(source.geometry) };\n } else if (source.kind === \"unavailable\") {\n record(r.source, [\"kind\", \"reason\"]);\n if (source.reason !== \"missing-native-raster\" && source.reason !== \"unverified-native-identity\") throw new InvalidConfigurationError(\"Invalid native-unavailable reason.\");\n parsedSource = { kind: \"unavailable\", reason: source.reason };\n } else throw new InvalidConfigurationError(\"Invalid native source kind.\");\n const image = record(r.printedImage, [\"width\", \"height\", \"unit\"]);\n const fit = record(r.fit, [\"kind\", \"maximumRelativeAspectError\"]);\n if (fit.kind !== \"confirmed-native-aspect\" || typeof fit.maximumRelativeAspectError !== \"number\" || !Number.isFinite(fit.maximumRelativeAspectError) || fit.maximumRelativeAspectError < 0 || fit.maximumRelativeAspectError > 0.01) throw new InvalidConfigurationError(\"Declare a native-aspect fit tolerance in [0, 0.01].\");\n const parsed: PrintPlanInput = { source: parsedSource, printedImage: { width: number(image.width, \"printedImage.width\"), height: number(image.height, \"printedImage.height\"), unit: unit(image.unit) }, viewingDistance: length(r.viewingDistance), sampling: sampling(r.sampling), fit: { kind: \"confirmed-native-aspect\", maximumRelativeAspectError: fit.maximumRelativeAspectError } };\n if (r.provider !== undefined) {\n const p = record(r.provider, [\"minimumRaster\", \"exactRaster\", \"minimumPpi\", \"exactPpi\"]);\n parsed.provider = {\n ...(p.minimumRaster === undefined ? {} : { minimumRaster: raster(p.minimumRaster) }),\n ...(p.exactRaster === undefined ? {} : { exactRaster: raster(p.exactRaster) }),\n ...(p.minimumPpi === undefined ? {} : { minimumPpi: number(p.minimumPpi, \"minimumPpi\") }),\n ...(p.exactPpi === undefined ? {} : { exactPpi: number(p.exactPpi, \"exactPpi\") })\n };\n }\n return parsed;\n}\nfunction gcd(a: number, b: number): number { while (b !== 0) { const next = a % b; a = b; b = next; } return a; }\nfunction positiveSupported(...values: number[]): boolean { return values.every(v => Number.isFinite(v) && v > 0); }\n// Stepping to the adjacent double rounds outward. It is not a tolerance on the criterion.\nfunction adjacentPositive(value: number, direction: \"up\" | \"down\"): number {\n const view = new DataView(new ArrayBuffer(8)); view.setFloat64(0, value);\n view.setBigUint64(0, view.getBigUint64(0) + (direction === \"up\" ? 1n : -1n));\n return view.getFloat64(0);\n}\nfunction exactRatio(a: RasterDimensions, b: RasterDimensions): boolean {\n return BigInt(a.pixelWidth) * BigInt(b.pixelHeight) === BigInt(a.pixelHeight) * BigInt(b.pixelWidth);\n}\nfunction minimumRaster(native: RasterDimensions, width: number, height: number): RasterDimensions | null {\n const divisor = gcd(native.pixelWidth, native.pixelHeight);\n const x = native.pixelWidth / divisor, y = native.pixelHeight / divisor;\n const count = Math.max(1, Math.ceil(width / x), Math.ceil(height / y));\n const pixelWidth = count * x, pixelHeight = count * y;\n return Number.isSafeInteger(pixelWidth) && Number.isSafeInteger(pixelHeight) ? { pixelWidth, pixelHeight } : null;\n}\nfunction diagnostics(raster: RasterDimensions, width: number, height: number, distance: number): PrintRasterSampling {\n const px = width / raster.pixelWidth, py = height / raster.pixelHeight;\n return { raster, pixelsPerInch: { x: 25.4 / px, y: 25.4 / py }, angularPixelPitchArcminutes: { x: 2 * Math.atan((px / distance) / 2) / RAD_PER_ARCMIN, y: 2 * Math.atan((py / distance) / 2) / RAD_PER_ARCMIN } };\n}\n\n/** Finds the smallest file, at the exact native aspect ratio, that meets the declared sampling and lab constraints.\n * The angular guidance is an approximate rule of thumb; the geometry itself is calculated exactly. A common criterion,\n * one arcminute per pixel, is normal (20/20) visual acuity under optimal conditions: the minimum angle of resolution of an\n * 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,\n * https://eyewiki.aao.org/Visual_Acuity_Testing_in_Adults, last edited June 16, 2026).\n * Some viewers resolve finer detail and many resolve coarser, so it stays a rule of thumb, and the criterion is the caller's.\n * It does not assess source detail, perceived quality, file writing or available resources.\n */\nexport function calculatePrintPlan(value: PrintPlanInput): CalculationResult<PrintPlan> {\n const input = parsePrintPlanInput(value);\n const plan: PrintPlan = { engineApiVersion: ENGINE_API_VERSION, input, status: \"cannot-assess\", nativePixelSufficiency: \"cannot-assess\", reasons: [], printedImageMm: null, viewingDistanceMm: null, guidance: null, relativeAspectError: null, native: null, minimumSamplingRaster: null, recommended: null, resamplingScale: null, alternatives: null, capturedDetailAssessment: \"unassessed\", deliveryAssessment: \"unassessed\" };\n const finish = (): CalculationResult<PrintPlan> => calculatedResult(plan, \"native-only-print-plan\", \"0.1.0\", [\"Declared native authority; flat image, normal viewing at image center.\", \"Output preserves the exact retained native aspect ratio; no upscale.\", \"Angular sampling is conditional guidance, not captured-detail or print-quality certification.\"]);\n const unsupported = (): CalculationResult<PrintPlan> => { plan.status = \"unsupported\"; plan.nativePixelSufficiency = \"cannot-assess\"; plan.reasons.push(\"unsupported-numeric-range\"); return finish(); };\n const width = input.printedImage.width * UNIT_MM[input.printedImage.unit], height = input.printedImage.height * UNIT_MM[input.printedImage.unit], distance = input.viewingDistance.value * UNIT_MM[input.viewingDistance.unit];\n if (!positiveSupported(width, height, distance)) return unsupported();\n plan.printedImageMm = { width, height }; plan.viewingDistanceMm = distance;\n const angle = input.sampling.kind === \"manual-ppi\" ? null : angularLimit(input.sampling);\n const pitchFactor = angle === null ? null : 2 * Math.tan((angle * RAD_PER_ARCMIN) / 2);\n const pitch = input.sampling.kind === \"manual-ppi\" ? 25.4 / input.sampling.pixelsPerInch : distance * (pitchFactor ?? 0);\n const ppi = input.sampling.kind === \"manual-ppi\" ? input.sampling.pixelsPerInch : 25.4 / pitch;\n if (!positiveSupported(pitch, ppi, width / pitch, height / pitch)) return unsupported();\n const guidance = { derivedPixelsPerInch: ppi, maximumPixelPitchMm: pitch, maximumArcminutesPerPixel: angle };\n plan.guidance = angle === null ? calculatedResult(guidance, \"manual-print-density\", \"0.1.0\", [\"Explicit manual PPI plan; no angular criterion is certified.\"]) : approximationResult(guidance, \"declared-angular-print-sampling\", \"0.1.0\", [\"Exact centered pixel angle uses 2 atan(pitch / (2 distance)); no small-angle substitution.\", \"Declared angular sampling convention is an uncalibrated usage assumption.\"]);\n if (input.source.kind === \"unavailable\") { plan.reasons.push(input.source.reason); return finish(); }\n const resolved = resolveCaptureGeometry(input.source.geometry).value;\n const native = { pixelWidth: resolved.output.cropRect.width, pixelHeight: resolved.output.cropRect.height };\n const aspectError = Math.abs((width / height) / (native.pixelWidth / native.pixelHeight) - 1);\n if (!Number.isFinite(aspectError)) return unsupported();\n plan.relativeAspectError = aspectError;\n const nativeDiagnostics = diagnostics(native, width, height, distance);\n if (!positiveSupported(nativeDiagnostics.pixelsPerInch.x, nativeDiagnostics.pixelsPerInch.y, nativeDiagnostics.angularPixelPitchArcminutes.x, nativeDiagnostics.angularPixelPitchArcminutes.y)) return unsupported();\n plan.native = nativeDiagnostics;\n if (aspectError > input.fit.maximumRelativeAspectError) { plan.status = \"crop-confirmation-required\"; plan.reasons.push(\"printed-image-and-confirmed-crop-aspect-mismatch\"); return finish(); }\n const widthInches = input.printedImage.unit === \"inches\" ? input.printedImage.width : width / 25.4;\n const heightInches = input.printedImage.unit === \"inches\" ? input.printedImage.height : height / 25.4;\n const required = input.sampling.kind === \"manual-ppi\"\n ? minimumRaster(native, widthInches * ppi, heightInches * ppi)\n : minimumRaster(native, width / pitch, height / pitch);\n if (required === null) return unsupported();\n plan.minimumSamplingRaster = required;\n const sufficient = required.pixelWidth <= native.pixelWidth && required.pixelHeight <= native.pixelHeight;\n plan.nativePixelSufficiency = sufficient ? \"sufficient\" : \"insufficient\";\n let maxScale = Math.min(native.pixelWidth * pitch / width, native.pixelHeight * pitch / height);\n let minimumDistance = pitchFactor === null ? null : Math.max(width / native.pixelWidth, height / native.pixelHeight) / pitchFactor;\n let maxWidth = width * maxScale, maxHeight = height * maxScale;\n if (!positiveSupported(maxWidth, maxHeight) || (minimumDistance !== null && !positiveSupported(minimumDistance))) return unsupported();\n const fits = (w: number, h: number, p: number): boolean => {\n const candidate = input.sampling.kind === \"manual-ppi\" ? minimumRaster(native, w / 25.4 * ppi, h / 25.4 * ppi) : minimumRaster(native, w / p, h / p);\n return candidate !== null && candidate.pixelWidth <= native.pixelWidth && candidate.pixelHeight <= native.pixelHeight;\n };\n // Return only alternatives that still pass their own documented integer rule.\n for (let i = 0; i < 8 && !fits(maxWidth, maxHeight, pitch); i++) {\n maxScale = adjacentPositive(maxScale, \"down\"); maxWidth = width * maxScale; maxHeight = height * maxScale;\n }\n if (!positiveSupported(maxWidth, maxHeight) || !fits(maxWidth, maxHeight, pitch)) return unsupported();\n if (minimumDistance !== null && pitchFactor !== null) {\n for (let i = 0; i < 8 && !fits(width, height, minimumDistance * pitchFactor); i++) minimumDistance = adjacentPositive(minimumDistance, \"up\");\n if (!positiveSupported(minimumDistance) || !fits(width, height, minimumDistance * pitchFactor)) return unsupported();\n }\n plan.alternatives = { maximumImageMmAtCurrentDistance: { width: maxWidth, height: maxHeight }, minimumViewingDistanceMmAtCurrentSize: minimumDistance };\n if (!sufficient) { plan.status = \"insufficient-native-pixels\"; plan.reasons.push(\"sampling-requires-native-upscale\"); }\n let file = required;\n const provider = input.provider;\n if (provider) {\n const density = Math.max(provider.minimumPpi ?? 0, provider.exactPpi ?? 0);\n const constrained = minimumRaster(native, Math.max(required.pixelWidth, provider.minimumRaster?.pixelWidth ?? 0, widthInches * density), Math.max(required.pixelHeight, provider.minimumRaster?.pixelHeight ?? 0, heightInches * density));\n if (constrained === null) return unsupported();\n file = provider.exactRaster ?? constrained;\n if (provider.exactPpi !== undefined) {\n const exact = { pixelWidth: widthInches * provider.exactPpi, pixelHeight: heightInches * provider.exactPpi };\n if (!Number.isSafeInteger(exact.pixelWidth) || !Number.isSafeInteger(exact.pixelHeight)) plan.reasons.push(\"exact-provider-density-has-no-integer-raster\");\n else if (provider.exactRaster && (exact.pixelWidth !== file.pixelWidth || exact.pixelHeight !== file.pixelHeight)) plan.reasons.push(\"provider-exact-raster-density-conflict\");\n else file = exact;\n }\n if (!exactRatio(file, native)) plan.reasons.push(\"provider-raster-aspect-conflict\");\n if (file.pixelWidth < constrained.pixelWidth || file.pixelHeight < constrained.pixelHeight) plan.reasons.push(\"provider-exact-requirement-below-minimum\");\n if (file.pixelWidth > native.pixelWidth || file.pixelHeight > native.pixelHeight) plan.reasons.push(\"provider-requires-native-upscale\");\n if (plan.reasons.some(r => r.startsWith(\"provider-\") || r.startsWith(\"exact-provider-\"))) { plan.status = \"provider-conflict\"; return finish(); }\n }\n if (!sufficient) return finish();\n const achieved = diagnostics(file, width, height, distance);\n if (!positiveSupported(achieved.pixelsPerInch.x, achieved.pixelsPerInch.y, achieved.angularPixelPitchArcminutes.x, achieved.angularPixelPitchArcminutes.y)) return unsupported();\n plan.status = \"ready\"; plan.recommended = achieved;\n plan.resamplingScale = { x: file.pixelWidth / native.pixelWidth, y: file.pixelHeight / native.pixelHeight };\n return finish();\n}\n\n/** Input for the largest-print-size query. It keeps the reference plan's aspect ratio and criterion.\n * With proportional viewing, the reference viewing distance scales with the image size.\n */\nexport interface PrintSizeLimitInput {\n plan: PrintPlanInput;\n viewingDistancePolicy: \"fixed\" | \"proportional-to-image-size\";\n}\n/** Result of the size query. A status says \"unbounded\" rather than using Infinity. \"no-positive-size\" is a valid answer, not an input error. */\nexport interface PrintSizeLimit {\n engineApiVersion: string;\n status: \"finite\" | \"unbounded\" | \"no-positive-size\" | \"cannot-assess\" | \"unsupported\";\n maximumImageMm: { width: number; height: number } | null;\n reasons: string[];\n referencePlan: PrintPlan;\n}\n/** Calculates the largest print size under the stated conditions. Lab constraints need individual plans,\n * so this size query reports them as unsupported rather than ignoring them.\n * As in `calculatePrintPlan`, an angular criterion is an approximate rule of thumb: one arcminute per pixel is normal\n * (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,\n * https://eyewiki.aao.org/Visual_Acuity_Testing_in_Adults, last edited June 16, 2026).\n */\nexport function calculatePrintSizeLimit(value: PrintSizeLimitInput): CalculationResult<PrintSizeLimit> {\n const r = record(value, [\"plan\", \"viewingDistancePolicy\"]);\n if (r.viewingDistancePolicy !== \"fixed\" && r.viewingDistancePolicy !== \"proportional-to-image-size\") throw new InvalidConfigurationError(\"Invalid viewing-distance scaling policy.\");\n const referencePlan = calculatePrintPlan(parsePrintPlanInput(r.plan)).value;\n const result: PrintSizeLimit = { engineApiVersion: ENGINE_API_VERSION, status: \"cannot-assess\", maximumImageMm: null, reasons: [...referencePlan.reasons], referencePlan };\n if (referencePlan.input.provider !== undefined && Object.keys(referencePlan.input.provider).length > 0) {\n result.status = \"unsupported\"; result.reasons.push(\"size-query-with-provider-constraints-unsupported\");\n } else if (referencePlan.status === \"unsupported\") result.status = \"unsupported\";\n else if (referencePlan.alternatives !== null) {\n if (r.viewingDistancePolicy === \"proportional-to-image-size\" && referencePlan.input.sampling.kind !== \"manual-ppi\") {\n result.status = referencePlan.nativePixelSufficiency === \"sufficient\" ? \"unbounded\" : \"no-positive-size\";\n } else { result.status = \"finite\"; result.maximumImageMm = referencePlan.alternatives.maximumImageMmAtCurrentDistance; }\n }\n return calculatedResult(result, \"conditional-native-print-size-limit\", \"0.1.0\", [\"Native sampling capacity only; no perceived-quality or physical-printer bound.\", \"Proportional distance and image scaling preserve angular pixel pitch.\"]);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"print-plan.js","sourceRoot":"","sources":["../../src/output/print-plan.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/** Print planning: how many native pixels a print of a given size and viewing distance needs.\n * It uses native capture pixels only. It does not make an image or certify perceived quality.\n * @see docs/PRINT_PLANNING.md for equations, rounding and support limits.\n */\nimport { calculatedResult, approximationResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { requireAllowlistedRecord, requirePublicOpaqueId } from \"../core/record-validation.js\";\nimport { requirePositiveFinite, requirePositiveInteger } from \"../core/validation.js\";\nimport { ENGINE_API_VERSION } from \"../core/version.js\";\nimport { resolveCaptureGeometry, type ResolveCaptureGeometryInput } from \"./capture-geometry.js\";\nimport type { RasterDimensions } from \"../sensor/sensor-geometry.js\";\n\n/** A physical length. Not to be confused with image pixels or printer dots. */\nexport interface PrintLength { value: number; unit: \"mm\" | \"cm\" | \"inches\" | \"m\" }\n/** The flat printed image area. It excludes paper margins, borders and canvas wraps.\n * With a `contain` fit it is instead the paper, or the printable area of it, that the image is fitted inside.\n */\nexport interface PrintedImageSize { width: number; height: number; unit: PrintLength[\"unit\"] }\n/** How to choose pixel density: one of three angular criteria at the viewing distance, or a manual PPI (pixels per inch), which gives a different kind of plan. */\nexport type PrintSamplingCriterion =\n | { kind: \"angular-pixel-pitch\"; maximumArcminutesPerPixel: number }\n | { kind: \"angular-stroke\"; strokeWidthArcminutes: number; samplesPerStroke: number }\n | { kind: \"angular-line-pair\"; periodArcminutes: number; samplesPerPeriod: number }\n | { kind: \"manual-ppi\"; pixelsPerInch: number };\n/** The declared native source geometry. Not a preview or a resampled pixel count. */\nexport type PrintNativeSource =\n | { kind: \"native-retained\"; captureId: string; geometry: ResolveCaptureGeometryInput }\n | { kind: \"unavailable\"; reason: \"missing-native-raster\" | \"unverified-native-identity\" };\n/** Print lab requirements supplied by the caller. There is no vendor catalogue and no guarantee of what a lab will accept. */\nexport interface PrintProviderConstraints {\n minimumRaster?: RasterDimensions;\n exactRaster?: RasterDimensions;\n minimumPpi?: number;\n exactPpi?: number;\n}\n/** How the printed image relates to `printedImage`.\n * - `confirmed-native-aspect`: `printedImage` is the printed image itself and must already have the native aspect ratio,\n * within the stated relative error.\n * - `contain`: `printedImage` is the paper. The image keeps the native aspect ratio and is scaled to the largest size that\n * fits inside it, centered; the rest is unprinted border. `match-image` turns the paper a quarter turn when its\n * orientation (portrait or landscape) differs from the image's; `as-given` keeps it. Layout only: nothing is cropped or resampled.\n */\nexport type PrintFit =\n | { kind: \"confirmed-native-aspect\"; maximumRelativeAspectError: number }\n | { kind: \"contain\"; paperOrientation: \"as-given\" | \"match-image\" };\n/** The layout of a `contain` fit, in millimetres. The printed image itself is the plan's `printedImageMm`. */\nexport interface PrintContainLayout {\n /** The paper as placed: after the quarter turn when `paperRotated`. */\n paperMm: { width: number; height: number };\n paperRotated: boolean;\n /** Unprinted border on each side of the centered image; zero when the paper has the image's aspect ratio. */\n bordersMm: { left: number; right: number; top: number; bottom: number };\n}\n/** Plan input. The output keeps the exact native aspect ratio; the allowed physical aspect error is explicit and reported. */\nexport interface PrintPlanInput {\n source: PrintNativeSource;\n printedImage: PrintedImageSize;\n viewingDistance: PrintLength;\n sampling: PrintSamplingCriterion;\n fit: PrintFit;\n provider?: PrintProviderConstraints;\n}\n/** Pixel density and angular pixel pitch for each axis. The angles are pure geometry, not a measure of visual acuity. */\nexport interface PrintRasterSampling {\n raster: RasterDimensions;\n pixelsPerInch: { x: number; y: number };\n angularPixelPitchArcminutes: { x: number; y: number };\n}\n/** A plan, valid only under its stated conditions. A null recommendation never means it is fine to upscale. */\nexport interface PrintPlan {\n engineApiVersion: string;\n input: PrintPlanInput;\n status: \"ready\" | \"insufficient-native-pixels\" | \"provider-conflict\" | \"crop-confirmation-required\" | \"cannot-assess\" | \"unsupported\";\n nativePixelSufficiency: \"sufficient\" | \"insufficient\" | \"cannot-assess\";\n reasons: string[];\n printedImageMm: { width: number; height: number } | null;\n viewingDistanceMm: number | null;\n guidance: CalculationResult<{ derivedPixelsPerInch: number; maximumPixelPitchMm: number; maximumArcminutesPerPixel: number | null }> | null;\n relativeAspectError: number | null;\n native: PrintRasterSampling | null;\n minimumSamplingRaster: RasterDimensions | null;\n recommended: PrintRasterSampling | null;\n resamplingScale: { x: number; y: number } | null;\n alternatives: { maximumImageMmAtCurrentDistance: { width: number; height: number }; minimumViewingDistanceMmAtCurrentSize: number | null } | null;\n capturedDetailAssessment: \"unassessed\";\n deliveryAssessment: \"unassessed\";\n /** Present only with a `contain` fit; null when the native raster is unknown. */\n contain?: PrintContainLayout | null;\n}\n\nconst UNIT_MM = { mm: 1, cm: 10, inches: 25.4, m: 1000 } as const;\nconst RAD_PER_ARCMIN = Math.PI / 10800;\nfunction record(value: unknown, keys: readonly string[]): Record<string, unknown> {\n return requireAllowlistedRecord(value, keys, \"Invalid or unknown Print planning fields.\");\n}\nfunction number(value: unknown, name: string): number {\n if (typeof value !== \"number\") throw new InvalidConfigurationError(`${name} must be numeric.`);\n requirePositiveFinite(name, value); return value;\n}\nfunction unit(value: unknown): PrintLength[\"unit\"] {\n if (typeof value !== \"string\" || !Object.hasOwn(UNIT_MM, value)) throw new InvalidConfigurationError(\"Invalid Print length unit.\");\n return value as PrintLength[\"unit\"];\n}\nfunction length(value: unknown): PrintLength {\n const r = record(value, [\"value\", \"unit\"]);\n return { value: number(r.value, \"length.value\"), unit: unit(r.unit) };\n}\nfunction raster(value: unknown): RasterDimensions {\n const r = record(value, [\"pixelWidth\", \"pixelHeight\"]);\n const pixelWidth = number(r.pixelWidth, \"pixelWidth\"), pixelHeight = number(r.pixelHeight, \"pixelHeight\");\n requirePositiveInteger(\"pixelWidth\", pixelWidth); requirePositiveInteger(\"pixelHeight\", pixelHeight);\n return { pixelWidth, pixelHeight };\n}\nfunction geometry(value: unknown): ResolveCaptureGeometryInput {\n const g = record(value, [\"imagingArea\", \"nativeRaster\", \"orientation\", \"activeCaptureRect\", \"outputCropRect\", \"outputRaster\"]);\n record(g.imagingArea, [\"widthMm\", \"heightMm\"]); raster(g.nativeRaster);\n for (const key of [\"activeCaptureRect\", \"outputCropRect\"] as const) {\n if (g[key] !== undefined) record(g[key], [\"x\", \"y\", \"width\", \"height\"]);\n }\n if (g.outputRaster !== undefined) raster(g.outputRaster);\n // The capture geometry module defines units, orientations, bounds and the optical axis.\n const parsed = g as unknown as ResolveCaptureGeometryInput;\n resolveCaptureGeometry(parsed);\n return structuredCloneGeometry(parsed);\n}\nfunction structuredCloneGeometry(g: ResolveCaptureGeometryInput): ResolveCaptureGeometryInput {\n return { imagingArea: { ...g.imagingArea }, nativeRaster: { ...g.nativeRaster }, orientation: g.orientation,\n ...(g.activeCaptureRect === undefined ? {} : { activeCaptureRect: { ...g.activeCaptureRect } }),\n ...(g.outputCropRect === undefined ? {} : { outputCropRect: { ...g.outputCropRect } }),\n ...(g.outputRaster === undefined ? {} : { outputRaster: { ...g.outputRaster } }) };\n}\nfunction sampling(value: unknown): PrintSamplingCriterion {\n const tag = record(value, [\"kind\", \"maximumArcminutesPerPixel\", \"strokeWidthArcminutes\", \"samplesPerStroke\", \"periodArcminutes\", \"samplesPerPeriod\", \"pixelsPerInch\"]);\n let result: PrintSamplingCriterion;\n switch (tag.kind) {\n case \"manual-ppi\": {\n const r = record(value, [\"kind\", \"pixelsPerInch\"]);\n return { kind: \"manual-ppi\", pixelsPerInch: number(r.pixelsPerInch, \"pixelsPerInch\") };\n }\n case \"angular-pixel-pitch\": {\n const r = record(value, [\"kind\", \"maximumArcminutesPerPixel\"]);\n result = { kind: \"angular-pixel-pitch\", maximumArcminutesPerPixel: number(r.maximumArcminutesPerPixel, \"maximumArcminutesPerPixel\") }; break;\n }\n case \"angular-stroke\": {\n const r = record(value, [\"kind\", \"strokeWidthArcminutes\", \"samplesPerStroke\"]);\n const samplesPerStroke = number(r.samplesPerStroke, \"samplesPerStroke\"); requirePositiveInteger(\"samplesPerStroke\", samplesPerStroke);\n result = { kind: \"angular-stroke\", strokeWidthArcminutes: number(r.strokeWidthArcminutes, \"strokeWidthArcminutes\"), samplesPerStroke }; break;\n }\n case \"angular-line-pair\": {\n const r = record(value, [\"kind\", \"periodArcminutes\", \"samplesPerPeriod\"]);\n const samplesPerPeriod = number(r.samplesPerPeriod, \"samplesPerPeriod\"); requirePositiveInteger(\"samplesPerPeriod\", samplesPerPeriod);\n if (samplesPerPeriod < 2) throw new InvalidConfigurationError(\"A line-pair period requires at least two samples.\");\n result = { kind: \"angular-line-pair\", periodArcminutes: number(r.periodArcminutes, \"periodArcminutes\"), samplesPerPeriod }; break;\n }\n default: throw new InvalidConfigurationError(\"Invalid Print sampling convention.\");\n }\n const angle = angularLimit(result);\n if (angle <= 0 || angle >= 10800) throw new InvalidConfigurationError(\"Effective angular pixel pitch must lie in (0, 10800) arcminutes.\");\n return result;\n}\nfunction angularLimit(s: Exclude<PrintSamplingCriterion, { kind: \"manual-ppi\" }>): number {\n switch (s.kind) {\n case \"angular-pixel-pitch\": return s.maximumArcminutesPerPixel;\n case \"angular-stroke\": return s.strokeWidthArcminutes / s.samplesPerStroke;\n case \"angular-line-pair\": return s.periodArcminutes / s.samplesPerPeriod;\n }\n}\n\n/** Validates and copies an untrusted print request. It never assumes the source is native if that is not declared. */\nexport function parsePrintPlanInput(value: unknown): PrintPlanInput {\n const r = record(value, [\"source\", \"printedImage\", \"viewingDistance\", \"sampling\", \"fit\", \"provider\"]);\n const source = record(r.source, [\"kind\", \"captureId\", \"geometry\", \"reason\"]);\n let parsedSource: PrintNativeSource;\n if (source.kind === \"native-retained\") {\n record(r.source, [\"kind\", \"captureId\", \"geometry\"]);\n parsedSource = { kind: \"native-retained\", captureId: requirePublicOpaqueId(source.captureId, \"Invalid public capture ID.\"), geometry: geometry(source.geometry) };\n } else if (source.kind === \"unavailable\") {\n record(r.source, [\"kind\", \"reason\"]);\n if (source.reason !== \"missing-native-raster\" && source.reason !== \"unverified-native-identity\") throw new InvalidConfigurationError(\"Invalid native-unavailable reason.\");\n parsedSource = { kind: \"unavailable\", reason: source.reason };\n } else throw new InvalidConfigurationError(\"Invalid native source kind.\");\n const image = record(r.printedImage, [\"width\", \"height\", \"unit\"]);\n const parsed: PrintPlanInput = { source: parsedSource, printedImage: { width: number(image.width, \"printedImage.width\"), height: number(image.height, \"printedImage.height\"), unit: unit(image.unit) }, viewingDistance: length(r.viewingDistance), sampling: sampling(r.sampling), fit: parseFit(r.fit) };\n if (r.provider !== undefined) {\n const p = record(r.provider, [\"minimumRaster\", \"exactRaster\", \"minimumPpi\", \"exactPpi\"]);\n parsed.provider = {\n ...(p.minimumRaster === undefined ? {} : { minimumRaster: raster(p.minimumRaster) }),\n ...(p.exactRaster === undefined ? {} : { exactRaster: raster(p.exactRaster) }),\n ...(p.minimumPpi === undefined ? {} : { minimumPpi: number(p.minimumPpi, \"minimumPpi\") }),\n ...(p.exactPpi === undefined ? {} : { exactPpi: number(p.exactPpi, \"exactPpi\") })\n };\n }\n return parsed;\n}\nfunction parseFit(value: unknown): PrintFit {\n const fit = record(value, [\"kind\", \"maximumRelativeAspectError\", \"paperOrientation\"]);\n if (fit.kind === \"contain\") {\n record(value, [\"kind\", \"paperOrientation\"]);\n if (fit.paperOrientation !== \"as-given\" && fit.paperOrientation !== \"match-image\") throw new InvalidConfigurationError(\"Declare a contain-fit paper orientation: as-given or match-image.\");\n return { kind: \"contain\", paperOrientation: fit.paperOrientation };\n }\n if (fit.kind !== \"confirmed-native-aspect\" || fit.paperOrientation !== undefined || typeof fit.maximumRelativeAspectError !== \"number\" || !Number.isFinite(fit.maximumRelativeAspectError) || fit.maximumRelativeAspectError < 0 || fit.maximumRelativeAspectError > 0.01) throw new InvalidConfigurationError(\"Declare a native-aspect fit tolerance in [0, 0.01].\");\n return { kind: \"confirmed-native-aspect\", maximumRelativeAspectError: fit.maximumRelativeAspectError };\n}\n/** The largest image at the native aspect ratio inside the paper, in the paper's unit, and its centered layout.\n * The binding axis equals the paper exactly; the other is clamped so rounding never makes a negative border.\n */\nfunction containImage(native: RasterDimensions, paperWidth: number, paperHeight: number, orientation: \"as-given\" | \"match-image\"): { width: number; height: number; paperWidth: number; paperHeight: number; paperRotated: boolean } {\n const imageLandscape = native.pixelWidth > native.pixelHeight, imagePortrait = native.pixelWidth < native.pixelHeight;\n const paperRotated = orientation === \"match-image\" && ((imageLandscape && paperWidth < paperHeight) || (imagePortrait && paperWidth > paperHeight));\n const pw = paperRotated ? paperHeight : paperWidth, ph = paperRotated ? paperWidth : paperHeight;\n const widthBinds = native.pixelWidth * ph >= native.pixelHeight * pw;\n const width = widthBinds ? pw : Math.min(pw, ph * native.pixelWidth / native.pixelHeight);\n const height = widthBinds ? Math.min(ph, pw * native.pixelHeight / native.pixelWidth) : ph;\n return { width, height, paperWidth: pw, paperHeight: ph, paperRotated };\n}\nfunction gcd(a: number, b: number): number { while (b !== 0) { const next = a % b; a = b; b = next; } return a; }\nfunction positiveSupported(...values: number[]): boolean { return values.every(v => Number.isFinite(v) && v > 0); }\n// Stepping to the adjacent double rounds outward. It is not a tolerance on the criterion.\nfunction adjacentPositive(value: number, direction: \"up\" | \"down\"): number {\n const view = new DataView(new ArrayBuffer(8)); view.setFloat64(0, value);\n view.setBigUint64(0, view.getBigUint64(0) + (direction === \"up\" ? 1n : -1n));\n return view.getFloat64(0);\n}\nfunction exactRatio(a: RasterDimensions, b: RasterDimensions): boolean {\n return BigInt(a.pixelWidth) * BigInt(b.pixelHeight) === BigInt(a.pixelHeight) * BigInt(b.pixelWidth);\n}\nfunction minimumRaster(native: RasterDimensions, width: number, height: number): RasterDimensions | null {\n const divisor = gcd(native.pixelWidth, native.pixelHeight);\n const x = native.pixelWidth / divisor, y = native.pixelHeight / divisor;\n const count = Math.max(1, Math.ceil(width / x), Math.ceil(height / y));\n const pixelWidth = count * x, pixelHeight = count * y;\n return Number.isSafeInteger(pixelWidth) && Number.isSafeInteger(pixelHeight) ? { pixelWidth, pixelHeight } : null;\n}\nfunction diagnostics(raster: RasterDimensions, width: number, height: number, distance: number): PrintRasterSampling {\n const px = width / raster.pixelWidth, py = height / raster.pixelHeight;\n return { raster, pixelsPerInch: { x: 25.4 / px, y: 25.4 / py }, angularPixelPitchArcminutes: { x: 2 * Math.atan((px / distance) / 2) / RAD_PER_ARCMIN, y: 2 * Math.atan((py / distance) / 2) / RAD_PER_ARCMIN } };\n}\n\n/** Finds the smallest file, at the exact native aspect ratio, that meets the declared sampling and lab constraints.\n * The angular guidance is an approximate rule of thumb; the geometry itself is calculated exactly. A common criterion,\n * one arcminute per pixel, is normal (20/20) visual acuity under optimal conditions: the minimum angle of resolution of an\n * 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,\n * https://eyewiki.aao.org/Visual_Acuity_Testing_in_Adults, last edited June 16, 2026).\n * Some viewers resolve finer detail and many resolve coarser, so it stays a rule of thumb, and the criterion is the caller's.\n * It does not assess source detail, perceived quality, file writing or available resources.\n */\nexport function calculatePrintPlan(value: PrintPlanInput): CalculationResult<PrintPlan> {\n const input = parsePrintPlanInput(value);\n const plan: PrintPlan = { engineApiVersion: ENGINE_API_VERSION, input, status: \"cannot-assess\", nativePixelSufficiency: \"cannot-assess\", reasons: [], printedImageMm: null, viewingDistanceMm: null, guidance: null, relativeAspectError: null, native: null, minimumSamplingRaster: null, recommended: null, resamplingScale: null, alternatives: null, capturedDetailAssessment: \"unassessed\", deliveryAssessment: \"unassessed\" };\n const contain = input.fit.kind === \"contain\" ? input.fit : null;\n if (contain !== null) plan.contain = null;\n const finish = (): CalculationResult<PrintPlan> => calculatedResult(plan, \"native-only-print-plan\", \"0.1.0\", [\"Declared native authority; flat image, normal viewing at image center.\", \"Output preserves the exact retained native aspect ratio; no upscale.\", \"Angular sampling is conditional guidance, not captured-detail or print-quality certification.\", ...(contain === null ? [] : [\"Contain fit: the image is centered inside the paper; the borders are unprinted and the paper is not a print area.\"])]);\n const unsupported = (): CalculationResult<PrintPlan> => { plan.status = \"unsupported\"; plan.nativePixelSufficiency = \"cannot-assess\"; plan.reasons.push(\"unsupported-numeric-range\"); return finish(); };\n const unitMm = UNIT_MM[input.printedImage.unit], distance = input.viewingDistance.value * UNIT_MM[input.viewingDistance.unit];\n // A contain fit needs the native aspect ratio before the printed image size is known.\n const resolved = input.source.kind === \"native-retained\" ? resolveCaptureGeometry(input.source.geometry).value : null;\n const native = resolved === null ? null : { pixelWidth: resolved.output.cropRect.width, pixelHeight: resolved.output.cropRect.height };\n const fitted = contain === null || native === null ? null : containImage(native, input.printedImage.width, input.printedImage.height, contain.paperOrientation);\n const imageWidth = fitted?.width ?? input.printedImage.width, imageHeight = fitted?.height ?? input.printedImage.height;\n const width = imageWidth * unitMm, height = imageHeight * unitMm;\n if (!positiveSupported(width, height, distance)) return unsupported();\n if (contain === null || fitted !== null) plan.printedImageMm = { width, height };\n plan.viewingDistanceMm = distance;\n const angle = input.sampling.kind === \"manual-ppi\" ? null : angularLimit(input.sampling);\n const pitchFactor = angle === null ? null : 2 * Math.tan((angle * RAD_PER_ARCMIN) / 2);\n const pitch = input.sampling.kind === \"manual-ppi\" ? 25.4 / input.sampling.pixelsPerInch : distance * (pitchFactor ?? 0);\n const ppi = input.sampling.kind === \"manual-ppi\" ? input.sampling.pixelsPerInch : 25.4 / pitch;\n if (!positiveSupported(pitch, ppi, width / pitch, height / pitch)) return unsupported();\n const guidance = { derivedPixelsPerInch: ppi, maximumPixelPitchMm: pitch, maximumArcminutesPerPixel: angle };\n plan.guidance = angle === null ? calculatedResult(guidance, \"manual-print-density\", \"0.1.0\", [\"Explicit manual PPI plan; no angular criterion is certified.\"]) : approximationResult(guidance, \"declared-angular-print-sampling\", \"0.1.0\", [\"Exact centered pixel angle uses 2 atan(pitch / (2 distance)); no small-angle substitution.\", \"Declared angular sampling convention is an uncalibrated usage assumption.\"]);\n if (input.source.kind === \"unavailable\" || native === null) { if (input.source.kind === \"unavailable\") plan.reasons.push(input.source.reason); return finish(); }\n if (fitted !== null) {\n const paperWidth = fitted.paperWidth * unitMm, paperHeight = fitted.paperHeight * unitMm;\n // Borders are taken in the paper's unit, then converted, so whole-unit borders stay exact.\n const side = (paper: number, image: number): number => Math.max(0, (paper - image) / 2) * unitMm;\n const x = side(fitted.paperWidth, imageWidth), y = side(fitted.paperHeight, imageHeight);\n if (!positiveSupported(paperWidth, paperHeight) || !Number.isFinite(x) || !Number.isFinite(y)) return unsupported();\n plan.contain = { paperMm: { width: paperWidth, height: paperHeight }, paperRotated: fitted.paperRotated, bordersMm: { left: x, right: x, top: y, bottom: y } };\n }\n const aspectError = Math.abs((width / height) / (native.pixelWidth / native.pixelHeight) - 1);\n if (!Number.isFinite(aspectError)) return unsupported();\n plan.relativeAspectError = aspectError;\n const nativeDiagnostics = diagnostics(native, width, height, distance);\n if (!positiveSupported(nativeDiagnostics.pixelsPerInch.x, nativeDiagnostics.pixelsPerInch.y, nativeDiagnostics.angularPixelPitchArcminutes.x, nativeDiagnostics.angularPixelPitchArcminutes.y)) return unsupported();\n plan.native = nativeDiagnostics;\n // A contain fit has the native aspect ratio by construction; its rounding error is reported, not checked.\n if (input.fit.kind === \"confirmed-native-aspect\" && aspectError > input.fit.maximumRelativeAspectError) { plan.status = \"crop-confirmation-required\"; plan.reasons.push(\"printed-image-and-confirmed-crop-aspect-mismatch\"); return finish(); }\n const widthInches = input.printedImage.unit === \"inches\" ? imageWidth : width / 25.4;\n const heightInches = input.printedImage.unit === \"inches\" ? imageHeight : height / 25.4;\n const required = input.sampling.kind === \"manual-ppi\"\n ? minimumRaster(native, widthInches * ppi, heightInches * ppi)\n : minimumRaster(native, width / pitch, height / pitch);\n if (required === null) return unsupported();\n plan.minimumSamplingRaster = required;\n const sufficient = required.pixelWidth <= native.pixelWidth && required.pixelHeight <= native.pixelHeight;\n plan.nativePixelSufficiency = sufficient ? \"sufficient\" : \"insufficient\";\n let maxScale = Math.min(native.pixelWidth * pitch / width, native.pixelHeight * pitch / height);\n let minimumDistance = pitchFactor === null ? null : Math.max(width / native.pixelWidth, height / native.pixelHeight) / pitchFactor;\n let maxWidth = width * maxScale, maxHeight = height * maxScale;\n if (!positiveSupported(maxWidth, maxHeight) || (minimumDistance !== null && !positiveSupported(minimumDistance))) return unsupported();\n const fits = (w: number, h: number, p: number): boolean => {\n const candidate = input.sampling.kind === \"manual-ppi\" ? minimumRaster(native, w / 25.4 * ppi, h / 25.4 * ppi) : minimumRaster(native, w / p, h / p);\n return candidate !== null && candidate.pixelWidth <= native.pixelWidth && candidate.pixelHeight <= native.pixelHeight;\n };\n // Return only alternatives that still pass their own documented integer rule.\n for (let i = 0; i < 8 && !fits(maxWidth, maxHeight, pitch); i++) {\n maxScale = adjacentPositive(maxScale, \"down\"); maxWidth = width * maxScale; maxHeight = height * maxScale;\n }\n if (!positiveSupported(maxWidth, maxHeight) || !fits(maxWidth, maxHeight, pitch)) return unsupported();\n if (minimumDistance !== null && pitchFactor !== null) {\n for (let i = 0; i < 8 && !fits(width, height, minimumDistance * pitchFactor); i++) minimumDistance = adjacentPositive(minimumDistance, \"up\");\n if (!positiveSupported(minimumDistance) || !fits(width, height, minimumDistance * pitchFactor)) return unsupported();\n }\n plan.alternatives = { maximumImageMmAtCurrentDistance: { width: maxWidth, height: maxHeight }, minimumViewingDistanceMmAtCurrentSize: minimumDistance };\n if (!sufficient) { plan.status = \"insufficient-native-pixels\"; plan.reasons.push(\"sampling-requires-native-upscale\"); }\n let file = required;\n const provider = input.provider;\n if (provider) {\n const density = Math.max(provider.minimumPpi ?? 0, provider.exactPpi ?? 0);\n const constrained = minimumRaster(native, Math.max(required.pixelWidth, provider.minimumRaster?.pixelWidth ?? 0, widthInches * density), Math.max(required.pixelHeight, provider.minimumRaster?.pixelHeight ?? 0, heightInches * density));\n if (constrained === null) return unsupported();\n file = provider.exactRaster ?? constrained;\n if (provider.exactPpi !== undefined) {\n const exact = { pixelWidth: widthInches * provider.exactPpi, pixelHeight: heightInches * provider.exactPpi };\n if (!Number.isSafeInteger(exact.pixelWidth) || !Number.isSafeInteger(exact.pixelHeight)) plan.reasons.push(\"exact-provider-density-has-no-integer-raster\");\n else if (provider.exactRaster && (exact.pixelWidth !== file.pixelWidth || exact.pixelHeight !== file.pixelHeight)) plan.reasons.push(\"provider-exact-raster-density-conflict\");\n else file = exact;\n }\n if (!exactRatio(file, native)) plan.reasons.push(\"provider-raster-aspect-conflict\");\n if (file.pixelWidth < constrained.pixelWidth || file.pixelHeight < constrained.pixelHeight) plan.reasons.push(\"provider-exact-requirement-below-minimum\");\n if (file.pixelWidth > native.pixelWidth || file.pixelHeight > native.pixelHeight) plan.reasons.push(\"provider-requires-native-upscale\");\n if (plan.reasons.some(r => r.startsWith(\"provider-\") || r.startsWith(\"exact-provider-\"))) { plan.status = \"provider-conflict\"; return finish(); }\n }\n if (!sufficient) return finish();\n const achieved = diagnostics(file, width, height, distance);\n if (!positiveSupported(achieved.pixelsPerInch.x, achieved.pixelsPerInch.y, achieved.angularPixelPitchArcminutes.x, achieved.angularPixelPitchArcminutes.y)) return unsupported();\n plan.status = \"ready\"; plan.recommended = achieved;\n plan.resamplingScale = { x: file.pixelWidth / native.pixelWidth, y: file.pixelHeight / native.pixelHeight };\n return finish();\n}\n\n/** Input for the largest-print-size query. It keeps the reference plan's aspect ratio and criterion.\n * With proportional viewing, the reference viewing distance scales with the image size.\n */\nexport interface PrintSizeLimitInput {\n plan: PrintPlanInput;\n viewingDistancePolicy: \"fixed\" | \"proportional-to-image-size\";\n}\n/** Result of the size query. A status says \"unbounded\" rather than using Infinity. \"no-positive-size\" is a valid answer, not an input error. */\nexport interface PrintSizeLimit {\n engineApiVersion: string;\n status: \"finite\" | \"unbounded\" | \"no-positive-size\" | \"cannot-assess\" | \"unsupported\";\n maximumImageMm: { width: number; height: number } | null;\n reasons: string[];\n referencePlan: PrintPlan;\n /** Present only with a `contain` fit: the paper that holds `maximumImageMm` with the same layout (borders scale with it). */\n maximumPaperMm?: { width: number; height: number } | null;\n}\n/** Calculates the largest print size under the stated conditions. Lab constraints need individual plans,\n * so this size query reports them as unsupported rather than ignoring them.\n * As in `calculatePrintPlan`, an angular criterion is an approximate rule of thumb: one arcminute per pixel is normal\n * (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,\n * https://eyewiki.aao.org/Visual_Acuity_Testing_in_Adults, last edited June 16, 2026).\n */\nexport function calculatePrintSizeLimit(value: PrintSizeLimitInput): CalculationResult<PrintSizeLimit> {\n const r = record(value, [\"plan\", \"viewingDistancePolicy\"]);\n if (r.viewingDistancePolicy !== \"fixed\" && r.viewingDistancePolicy !== \"proportional-to-image-size\") throw new InvalidConfigurationError(\"Invalid viewing-distance scaling policy.\");\n const referencePlan = calculatePrintPlan(parsePrintPlanInput(r.plan)).value;\n const result: PrintSizeLimit = { engineApiVersion: ENGINE_API_VERSION, status: \"cannot-assess\", maximumImageMm: null, reasons: [...referencePlan.reasons], referencePlan };\n if (referencePlan.input.provider !== undefined && Object.keys(referencePlan.input.provider).length > 0) {\n result.status = \"unsupported\"; result.reasons.push(\"size-query-with-provider-constraints-unsupported\");\n } else if (referencePlan.status === \"unsupported\") result.status = \"unsupported\";\n else if (referencePlan.alternatives !== null) {\n if (r.viewingDistancePolicy === \"proportional-to-image-size\" && referencePlan.input.sampling.kind !== \"manual-ppi\") {\n result.status = referencePlan.nativePixelSufficiency === \"sufficient\" ? \"unbounded\" : \"no-positive-size\";\n } else { result.status = \"finite\"; result.maximumImageMm = referencePlan.alternatives.maximumImageMmAtCurrentDistance; }\n }\n if (referencePlan.input.fit.kind === \"contain\") {\n const layout = referencePlan.contain, image = referencePlan.printedImageMm, maximum = result.maximumImageMm;\n const scale = layout && image && maximum ? maximum.width / image.width : null;\n result.maximumPaperMm = layout && scale !== null && positiveSupported(layout.paperMm.width * scale, layout.paperMm.height * scale) ? { width: layout.paperMm.width * scale, height: layout.paperMm.height * scale } : null;\n }\n return calculatedResult(result, \"conditional-native-print-size-limit\", \"0.1.0\", [\"Native sampling capacity only; no perceived-quality or physical-printer bound.\", \"Proportional distance and image scaling preserve angular pixel pitch.\"]);\n}\n"]}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/** Print size limit that counts the capture's blur and noise as well as its pixels (#574).
|
|
2
|
+
* A pure function: it makes no image and is not used by the capture path.
|
|
3
|
+
* @see docs/PRINT_PLANNING.md, "Print size limit with blur and noise", for the equations, sources and limits.
|
|
4
|
+
*/
|
|
5
|
+
import { type CalculationResult } from "../core/calculation-result.js";
|
|
6
|
+
import type { RasterDimensions } from "../sensor/sensor-geometry.js";
|
|
7
|
+
import type { PrintLength } from "./print-plan.js";
|
|
8
|
+
import type { SimulateCaptureSensorReport } from "../sensor/sensor-characteristics.js";
|
|
9
|
+
/** The capture's blur at the sensor plane, as blur-spot diameters in millimetres.
|
|
10
|
+
* `components` combines the parts by root-sum-square (an approximation, see the docs); `measured-total` is one measured diameter.
|
|
11
|
+
* A zero component is allowed and means that part is left out. Engine helpers give the parts: `calculateDefocusCircle` (defocus),
|
|
12
|
+
* `calculateAiryDisk` (diffraction, micrometres), `calculateProjectedMotionBlur` (motion length).
|
|
13
|
+
*/
|
|
14
|
+
export type PrintBlurInput = {
|
|
15
|
+
kind: "components";
|
|
16
|
+
lensMm?: number;
|
|
17
|
+
defocusMm?: number;
|
|
18
|
+
diffractionMm?: number;
|
|
19
|
+
motionMm?: number;
|
|
20
|
+
} | {
|
|
21
|
+
kind: "measured-total";
|
|
22
|
+
diameterMm: number;
|
|
23
|
+
};
|
|
24
|
+
/** The noise of one pixel of the printed raster, for the tone the caller cares about (for example mid-gray).
|
|
25
|
+
* `shot-and-read-noise` uses the engine's noise model (`calculateSignalToNoise`: Poisson shot noise plus RMS read noise);
|
|
26
|
+
* `pixel-snr` is a per-pixel signal-to-noise ratio the caller already has. `capture-noise-figure` (2.20.0) takes a capture's own
|
|
27
|
+
* mid-gray noise figure (#535: `result.sensor.midGrey` of a `simulateCapture` with `fast: false`), whose SNR S/σ_total counts photon
|
|
28
|
+
* shot, read, dark-current, quantization and any fixed-pattern noise; it is one sensor site's, so it applies as the pixel SNR when one
|
|
29
|
+
* printed pixel is one capture pixel. A clipped figure (the patch at white) is refused.
|
|
30
|
+
*/
|
|
31
|
+
export type PrintNoiseInput = {
|
|
32
|
+
kind: "shot-and-read-noise";
|
|
33
|
+
signalElectrons: number;
|
|
34
|
+
readNoiseElectrons: number;
|
|
35
|
+
} | {
|
|
36
|
+
kind: "pixel-snr";
|
|
37
|
+
snrLinear: number;
|
|
38
|
+
} | {
|
|
39
|
+
kind: "capture-noise-figure";
|
|
40
|
+
midGrey: NonNullable<SimulateCaptureSensorReport["midGrey"]>;
|
|
41
|
+
};
|
|
42
|
+
/** What "looks sharp and clean" means: the smallest angle the viewer resolves, and the lowest signal-to-noise ratio the viewer
|
|
43
|
+
* accepts after the eye averages over that angle. Both are the caller's choice; one arcminute is normal (20/20) acuity.
|
|
44
|
+
*/
|
|
45
|
+
export interface PrintSharpCleanCriterion {
|
|
46
|
+
kind: "visual-resolution";
|
|
47
|
+
arcminutes: number;
|
|
48
|
+
minimumVisibleSnr: number;
|
|
49
|
+
}
|
|
50
|
+
/** Input. The raster is the retained image that is printed (after any crop), with square pixels; `pixelPitchMm` is the
|
|
51
|
+
* sensor length one of those pixels covers. The print keeps the raster's aspect ratio. The viewing distance is fixed.
|
|
52
|
+
*/
|
|
53
|
+
export interface PrintSizeLimitWithBlurAndNoiseInput {
|
|
54
|
+
raster: RasterDimensions;
|
|
55
|
+
pixelPitchMm: number;
|
|
56
|
+
viewingDistance: PrintLength;
|
|
57
|
+
criterion: PrintSharpCleanCriterion;
|
|
58
|
+
blur: PrintBlurInput;
|
|
59
|
+
noise: PrintNoiseInput;
|
|
60
|
+
}
|
|
61
|
+
/** A print size in millimetres at the raster's aspect ratio. */
|
|
62
|
+
export interface PrintImageMm {
|
|
63
|
+
width: number;
|
|
64
|
+
height: number;
|
|
65
|
+
}
|
|
66
|
+
/** Result. Each factor's own limit is the largest print it allows on its own; null means that factor never limits.
|
|
67
|
+
* `limitingFactor` names the smallest; on a tie the order is pixels, blur, noise.
|
|
68
|
+
*/
|
|
69
|
+
export interface PrintSizeLimitWithBlurAndNoise {
|
|
70
|
+
engineApiVersion: string;
|
|
71
|
+
status: "finite" | "no-positive-size";
|
|
72
|
+
limitingFactor: "pixels" | "blur" | "noise";
|
|
73
|
+
maximumImageMm: PrintImageMm;
|
|
74
|
+
limits: {
|
|
75
|
+
pixels: PrintImageMm;
|
|
76
|
+
blur: PrintImageMm | null;
|
|
77
|
+
noise: PrintImageMm | null;
|
|
78
|
+
};
|
|
79
|
+
viewingDistanceMm: number;
|
|
80
|
+
/** The criterion angle as a length on the print at the viewing distance: 2 d tan(θ/2). */
|
|
81
|
+
resolvedSpotMm: number;
|
|
82
|
+
blur: {
|
|
83
|
+
diameterMm: number;
|
|
84
|
+
diameterPixels: number;
|
|
85
|
+
largestComponent: "lens" | "defocus" | "diffraction" | "motion" | "measured-total" | null;
|
|
86
|
+
};
|
|
87
|
+
noise: {
|
|
88
|
+
pixelSnr: number;
|
|
89
|
+
visibleSnrAtMaximum: number;
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/** Calculates the largest print, at a fixed viewing distance, at which the capture still looks sharp and clean, and names
|
|
93
|
+
* the factor that limits it: pixels, blur or noise. An approximation throughout; the equations are exact under its model.
|
|
94
|
+
*
|
|
95
|
+
* With θ the criterion angle, d the viewing distance, s = 2 d tan(θ/2) the length θ covers on the print, N_w the raster
|
|
96
|
+
* width in pixels, b the blur diameter in pixels and SNR_px the per-pixel SNR (print width W, height in proportion):
|
|
97
|
+
* - pixels: a print pixel (W / N_w) may cover at most s, so W ≤ N_w·s (as `calculatePrintPlan` with
|
|
98
|
+
* `angular-pixel-pitch`, before its whole-pixel rounding);
|
|
99
|
+
* - blur: the blur on the print (b·W / N_w) may cover at most s, so W ≤ N_w·s / b: the criterion of an acceptable circle
|
|
100
|
+
* of confusion with the eye's resolution as the limit;
|
|
101
|
+
* - noise: the eye averages the (W / N_w) wide pixels inside s, so the visible SNR is SNR_px·(N_w·s / W) while a pixel is
|
|
102
|
+
* smaller than s (the mean of n independent values has 1/√n of their standard deviation); W ≤ N_w·s·SNR_px / SNR_min,
|
|
103
|
+
* and noise never limits when SNR_px ≥ SNR_min.
|
|
104
|
+
* One arcminute is normal (20/20) acuity under optimal conditions (Z. Alomar, K. D. Bojikian, "Visual Acuity Testing in
|
|
105
|
+
* Adults", EyeWiki, American Academy of Ophthalmology, https://eyewiki.aao.org/Visual_Acuity_Testing_in_Adults, last edited
|
|
106
|
+
* June 16, 2026); the SNR threshold has no default and is the caller's. Blur parts add in root-sum-square, which is exact
|
|
107
|
+
* for Gaussian spots (variances of independent displacements add: C. M. Grinstead, J. L. Snell, "Introduction to
|
|
108
|
+
* Probability", 2nd rev. ed., AMS 1997, sec. 6.2, free under the GNU FDL) and an approximation for discs and Airy patterns.
|
|
109
|
+
*/
|
|
110
|
+
export declare function calculatePrintSizeLimitWithBlurAndNoise(value: PrintSizeLimitWithBlurAndNoiseInput): CalculationResult<PrintSizeLimitWithBlurAndNoise>;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/** Print size limit that counts the capture's blur and noise as well as its pixels (#574).
|
|
3
|
+
* A pure function: it makes no image and is not used by the capture path.
|
|
4
|
+
* @see docs/PRINT_PLANNING.md, "Print size limit with blur and noise", for the equations, sources and limits.
|
|
5
|
+
*/
|
|
6
|
+
import { approximationResult } from "../core/calculation-result.js";
|
|
7
|
+
import { InvalidConfigurationError } from "../core/configuration-error.js";
|
|
8
|
+
import { requireAllowlistedRecord, requireInputKeys, requireInputNonNegativeFinite, requireInputPositiveFinite } from "../core/record-validation.js";
|
|
9
|
+
import { requirePositiveInteger } from "../core/validation.js";
|
|
10
|
+
import { ENGINE_API_VERSION } from "../core/version.js";
|
|
11
|
+
import { calculateSignalToNoise } from "../sensor/signal-noise.js";
|
|
12
|
+
const UNIT_MM = { mm: 1, cm: 10, inches: 25.4, m: 1000 };
|
|
13
|
+
const RAD_PER_ARCMIN = Math.PI / 10800;
|
|
14
|
+
const BLUR_PARTS = ["lens", "defocus", "diffraction", "motion"];
|
|
15
|
+
function record(value, keys) {
|
|
16
|
+
return requireAllowlistedRecord(value, keys, "Invalid or unknown print size limit fields.");
|
|
17
|
+
}
|
|
18
|
+
function parseBlur(value) {
|
|
19
|
+
const r = record(value, ["kind", "lensMm", "defocusMm", "diffractionMm", "motionMm", "diameterMm"]);
|
|
20
|
+
if (r.kind === "measured-total") {
|
|
21
|
+
record(value, ["kind", "diameterMm"]);
|
|
22
|
+
const diameterMm = requireInputNonNegativeFinite(r.diameterMm, "blur.diameterMm");
|
|
23
|
+
return { diameterMm, largestComponent: diameterMm > 0 ? "measured-total" : null };
|
|
24
|
+
}
|
|
25
|
+
if (r.kind !== "components")
|
|
26
|
+
throw new InvalidConfigurationError("Invalid print blur kind.");
|
|
27
|
+
let sumSquares = 0, largest = 0, largestComponent = null;
|
|
28
|
+
for (const part of BLUR_PARTS) {
|
|
29
|
+
const raw = r[`${part}Mm`];
|
|
30
|
+
if (raw === undefined)
|
|
31
|
+
continue;
|
|
32
|
+
const d = requireInputNonNegativeFinite(raw, `blur.${part}Mm`);
|
|
33
|
+
sumSquares += d * d;
|
|
34
|
+
if (d > largest) {
|
|
35
|
+
largest = d;
|
|
36
|
+
largestComponent = part;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return { diameterMm: Math.sqrt(sumSquares), largestComponent };
|
|
40
|
+
}
|
|
41
|
+
/** The one place the noise input becomes a per-pixel SNR, including a capture's own noise figure (#535). */
|
|
42
|
+
function resolvePixelSnr(value) {
|
|
43
|
+
const r = record(value, ["kind", "signalElectrons", "readNoiseElectrons", "snrLinear", "midGrey"]);
|
|
44
|
+
switch (r.kind) {
|
|
45
|
+
case "shot-and-read-noise": {
|
|
46
|
+
record(value, ["kind", "signalElectrons", "readNoiseElectrons"]);
|
|
47
|
+
return calculateSignalToNoise({ signalElectrons: r.signalElectrons, readNoiseElectrons: r.readNoiseElectrons }).value.snrLinear;
|
|
48
|
+
}
|
|
49
|
+
case "pixel-snr":
|
|
50
|
+
record(value, ["kind", "snrLinear"]);
|
|
51
|
+
return requireInputNonNegativeFinite(r.snrLinear, "noise.snrLinear");
|
|
52
|
+
case "capture-noise-figure": {
|
|
53
|
+
record(value, ["kind", "midGrey"]);
|
|
54
|
+
const figure = r.midGrey;
|
|
55
|
+
if (typeof figure !== "object" || figure === null || typeof figure.noiseElectrons !== "object" || figure.noiseElectrons === null)
|
|
56
|
+
throw new InvalidConfigurationError("noise.midGrey must be a capture's result.sensor.midGrey (simulateCapture with fast: false).");
|
|
57
|
+
if (figure.clipped !== false)
|
|
58
|
+
throw new InvalidConfigurationError("noise.midGrey is clipped (or not a capture's figure): the patch reaches white, where the figure no longer describes the image.");
|
|
59
|
+
return requireInputNonNegativeFinite(figure.signalElectrons, "noise.midGrey.signalElectrons") / requireInputPositiveFinite(figure.noiseElectrons.total, "noise.midGrey.noiseElectrons.total");
|
|
60
|
+
}
|
|
61
|
+
default: throw new InvalidConfigurationError("Invalid print noise kind.");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** Calculates the largest print, at a fixed viewing distance, at which the capture still looks sharp and clean, and names
|
|
65
|
+
* the factor that limits it: pixels, blur or noise. An approximation throughout; the equations are exact under its model.
|
|
66
|
+
*
|
|
67
|
+
* With θ the criterion angle, d the viewing distance, s = 2 d tan(θ/2) the length θ covers on the print, N_w the raster
|
|
68
|
+
* width in pixels, b the blur diameter in pixels and SNR_px the per-pixel SNR (print width W, height in proportion):
|
|
69
|
+
* - pixels: a print pixel (W / N_w) may cover at most s, so W ≤ N_w·s (as `calculatePrintPlan` with
|
|
70
|
+
* `angular-pixel-pitch`, before its whole-pixel rounding);
|
|
71
|
+
* - blur: the blur on the print (b·W / N_w) may cover at most s, so W ≤ N_w·s / b: the criterion of an acceptable circle
|
|
72
|
+
* of confusion with the eye's resolution as the limit;
|
|
73
|
+
* - noise: the eye averages the (W / N_w) wide pixels inside s, so the visible SNR is SNR_px·(N_w·s / W) while a pixel is
|
|
74
|
+
* smaller than s (the mean of n independent values has 1/√n of their standard deviation); W ≤ N_w·s·SNR_px / SNR_min,
|
|
75
|
+
* and noise never limits when SNR_px ≥ SNR_min.
|
|
76
|
+
* One arcminute is normal (20/20) acuity under optimal conditions (Z. Alomar, K. D. Bojikian, "Visual Acuity Testing in
|
|
77
|
+
* Adults", EyeWiki, American Academy of Ophthalmology, https://eyewiki.aao.org/Visual_Acuity_Testing_in_Adults, last edited
|
|
78
|
+
* June 16, 2026); the SNR threshold has no default and is the caller's. Blur parts add in root-sum-square, which is exact
|
|
79
|
+
* for Gaussian spots (variances of independent displacements add: C. M. Grinstead, J. L. Snell, "Introduction to
|
|
80
|
+
* Probability", 2nd rev. ed., AMS 1997, sec. 6.2, free under the GNU FDL) and an approximation for discs and Airy patterns.
|
|
81
|
+
*/
|
|
82
|
+
export function calculatePrintSizeLimitWithBlurAndNoise(value) {
|
|
83
|
+
const keys = ["raster", "pixelPitchMm", "viewingDistance", "criterion", "blur", "noise"];
|
|
84
|
+
requireInputKeys(value, "calculatePrintSizeLimitWithBlurAndNoise", keys);
|
|
85
|
+
const r = record(value, keys);
|
|
86
|
+
const raster = record(r.raster, ["pixelWidth", "pixelHeight"]);
|
|
87
|
+
const pixelWidth = requireInputPositiveFinite(raster.pixelWidth, "raster.pixelWidth"), pixelHeight = requireInputPositiveFinite(raster.pixelHeight, "raster.pixelHeight");
|
|
88
|
+
requirePositiveInteger("raster.pixelWidth", pixelWidth);
|
|
89
|
+
requirePositiveInteger("raster.pixelHeight", pixelHeight);
|
|
90
|
+
const pixelPitchMm = requireInputPositiveFinite(r.pixelPitchMm, "pixelPitchMm");
|
|
91
|
+
const distance = record(r.viewingDistance, ["value", "unit"]);
|
|
92
|
+
if (typeof distance.unit !== "string" || !Object.hasOwn(UNIT_MM, distance.unit))
|
|
93
|
+
throw new InvalidConfigurationError("Invalid Print length unit.");
|
|
94
|
+
const viewingDistanceMm = requireInputPositiveFinite(distance.value, "viewingDistance.value") * UNIT_MM[distance.unit];
|
|
95
|
+
const criterion = record(r.criterion, ["kind", "arcminutes", "minimumVisibleSnr"]);
|
|
96
|
+
if (criterion.kind !== "visual-resolution")
|
|
97
|
+
throw new InvalidConfigurationError("Invalid print criterion kind.");
|
|
98
|
+
const arcminutes = requireInputPositiveFinite(criterion.arcminutes, "criterion.arcminutes");
|
|
99
|
+
if (arcminutes >= 10800)
|
|
100
|
+
throw new InvalidConfigurationError("criterion.arcminutes must lie in (0, 10800).");
|
|
101
|
+
const minimumVisibleSnr = requireInputPositiveFinite(criterion.minimumVisibleSnr, "criterion.minimumVisibleSnr");
|
|
102
|
+
const blur = parseBlur(r.blur);
|
|
103
|
+
const pixelSnr = resolvePixelSnr(r.noise);
|
|
104
|
+
const resolvedSpotMm = 2 * viewingDistanceMm * Math.tan((arcminutes * RAD_PER_ARCMIN) / 2);
|
|
105
|
+
const pixelLimitWidth = pixelWidth * resolvedSpotMm;
|
|
106
|
+
const blurPixels = blur.diameterMm / pixelPitchMm;
|
|
107
|
+
const blurLimitWidth = blurPixels > 0 ? pixelLimitWidth / blurPixels : null;
|
|
108
|
+
const noiseLimitWidth = pixelSnr < minimumVisibleSnr ? pixelLimitWidth * (pixelSnr / minimumVisibleSnr) : null;
|
|
109
|
+
let limitingFactor = "pixels", width = pixelLimitWidth;
|
|
110
|
+
if (blurLimitWidth !== null && blurLimitWidth < width) {
|
|
111
|
+
limitingFactor = "blur";
|
|
112
|
+
width = blurLimitWidth;
|
|
113
|
+
}
|
|
114
|
+
if (noiseLimitWidth !== null && noiseLimitWidth < width) {
|
|
115
|
+
limitingFactor = "noise";
|
|
116
|
+
width = noiseLimitWidth;
|
|
117
|
+
}
|
|
118
|
+
const size = (w) => ({ width: w, height: w * (pixelHeight / pixelWidth) });
|
|
119
|
+
const visibleSnrAtMaximum = width > 0 ? pixelSnr * Math.max(1, pixelLimitWidth / width) : 0;
|
|
120
|
+
const result = {
|
|
121
|
+
engineApiVersion: ENGINE_API_VERSION, status: width > 0 ? "finite" : "no-positive-size", limitingFactor,
|
|
122
|
+
maximumImageMm: size(width),
|
|
123
|
+
limits: { pixels: size(pixelLimitWidth), blur: blurLimitWidth === null ? null : size(blurLimitWidth), noise: noiseLimitWidth === null ? null : size(noiseLimitWidth) },
|
|
124
|
+
viewingDistanceMm, resolvedSpotMm,
|
|
125
|
+
blur: { diameterMm: blur.diameterMm, diameterPixels: blurPixels, largestComponent: blur.largestComponent },
|
|
126
|
+
noise: { pixelSnr, visibleSnrAtMaximum }
|
|
127
|
+
};
|
|
128
|
+
const numbers = [viewingDistanceMm, resolvedSpotMm, pixelLimitWidth, size(pixelLimitWidth).height, blurPixels, blurLimitWidth ?? 0, width, visibleSnrAtMaximum];
|
|
129
|
+
if (!numbers.every(Number.isFinite) || !(pixelLimitWidth > 0))
|
|
130
|
+
throw new InvalidConfigurationError("Print size limit lies outside the supported numeric range.");
|
|
131
|
+
return approximationResult(result, "print-size-limit-blur-noise", "1.0.0", [
|
|
132
|
+
"Flat print viewed at a fixed distance, at its center; one visual-resolution angle for pixels, blur and noise.",
|
|
133
|
+
"Blur parts combine in root-sum-square: exact for Gaussian spots, an approximation for discs and Airy patterns.",
|
|
134
|
+
"Pixel noise is white (independent pixels); demosaic, denoise and sharpening correlations are not modeled.",
|
|
135
|
+
"The eye averages noise over a square of the criterion angle; contrast sensitivity and print medium are not modeled."
|
|
136
|
+
]);
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=print-size-blur-noise.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"print-size-blur-noise.js","sourceRoot":"","sources":["../../src/output/print-size-blur-noise.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/** Print size limit that counts the capture's blur and noise as well as its pixels (#574).\n * A pure function: it makes no image and is not used by the capture path.\n * @see docs/PRINT_PLANNING.md, \"Print size limit with blur and noise\", for the equations, sources and limits.\n */\nimport { approximationResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { requireAllowlistedRecord, requireInputKeys, requireInputNonNegativeFinite, requireInputPositiveFinite } from \"../core/record-validation.js\";\nimport { requirePositiveInteger } from \"../core/validation.js\";\nimport { ENGINE_API_VERSION } from \"../core/version.js\";\nimport { calculateSignalToNoise } from \"../sensor/signal-noise.js\";\nimport type { RasterDimensions } from \"../sensor/sensor-geometry.js\";\nimport type { PrintLength } from \"./print-plan.js\";\nimport type { SimulateCaptureSensorReport } from \"../sensor/sensor-characteristics.js\";\n\n/** The capture's blur at the sensor plane, as blur-spot diameters in millimetres.\n * `components` combines the parts by root-sum-square (an approximation, see the docs); `measured-total` is one measured diameter.\n * A zero component is allowed and means that part is left out. Engine helpers give the parts: `calculateDefocusCircle` (defocus),\n * `calculateAiryDisk` (diffraction, micrometres), `calculateProjectedMotionBlur` (motion length).\n */\nexport type PrintBlurInput =\n | { kind: \"components\"; lensMm?: number; defocusMm?: number; diffractionMm?: number; motionMm?: number }\n | { kind: \"measured-total\"; diameterMm: number };\n/** The noise of one pixel of the printed raster, for the tone the caller cares about (for example mid-gray).\n * `shot-and-read-noise` uses the engine's noise model (`calculateSignalToNoise`: Poisson shot noise plus RMS read noise);\n * `pixel-snr` is a per-pixel signal-to-noise ratio the caller already has. `capture-noise-figure` (2.20.0) takes a capture's own\n * mid-gray noise figure (#535: `result.sensor.midGrey` of a `simulateCapture` with `fast: false`), whose SNR S/σ_total counts photon\n * shot, read, dark-current, quantization and any fixed-pattern noise; it is one sensor site's, so it applies as the pixel SNR when one\n * printed pixel is one capture pixel. A clipped figure (the patch at white) is refused.\n */\nexport type PrintNoiseInput =\n | { kind: \"shot-and-read-noise\"; signalElectrons: number; readNoiseElectrons: number }\n | { kind: \"pixel-snr\"; snrLinear: number }\n | { kind: \"capture-noise-figure\"; midGrey: NonNullable<SimulateCaptureSensorReport[\"midGrey\"]> };\n/** What \"looks sharp and clean\" means: the smallest angle the viewer resolves, and the lowest signal-to-noise ratio the viewer\n * accepts after the eye averages over that angle. Both are the caller's choice; one arcminute is normal (20/20) acuity.\n */\nexport interface PrintSharpCleanCriterion {\n kind: \"visual-resolution\";\n arcminutes: number;\n minimumVisibleSnr: number;\n}\n/** Input. The raster is the retained image that is printed (after any crop), with square pixels; `pixelPitchMm` is the\n * sensor length one of those pixels covers. The print keeps the raster's aspect ratio. The viewing distance is fixed.\n */\nexport interface PrintSizeLimitWithBlurAndNoiseInput {\n raster: RasterDimensions;\n pixelPitchMm: number;\n viewingDistance: PrintLength;\n criterion: PrintSharpCleanCriterion;\n blur: PrintBlurInput;\n noise: PrintNoiseInput;\n}\n/** A print size in millimetres at the raster's aspect ratio. */\nexport interface PrintImageMm { width: number; height: number }\n/** Result. Each factor's own limit is the largest print it allows on its own; null means that factor never limits.\n * `limitingFactor` names the smallest; on a tie the order is pixels, blur, noise.\n */\nexport interface PrintSizeLimitWithBlurAndNoise {\n engineApiVersion: string;\n status: \"finite\" | \"no-positive-size\";\n limitingFactor: \"pixels\" | \"blur\" | \"noise\";\n maximumImageMm: PrintImageMm;\n limits: { pixels: PrintImageMm; blur: PrintImageMm | null; noise: PrintImageMm | null };\n viewingDistanceMm: number;\n /** The criterion angle as a length on the print at the viewing distance: 2 d tan(θ/2). */\n resolvedSpotMm: number;\n blur: { diameterMm: number; diameterPixels: number; largestComponent: \"lens\" | \"defocus\" | \"diffraction\" | \"motion\" | \"measured-total\" | null };\n noise: { pixelSnr: number; visibleSnrAtMaximum: number };\n}\n\nconst UNIT_MM = { mm: 1, cm: 10, inches: 25.4, m: 1000 } as const;\nconst RAD_PER_ARCMIN = Math.PI / 10800;\nconst BLUR_PARTS = [\"lens\", \"defocus\", \"diffraction\", \"motion\"] as const;\n\nfunction record(value: unknown, keys: readonly string[]): Record<string, unknown> {\n return requireAllowlistedRecord(value, keys, \"Invalid or unknown print size limit fields.\");\n}\nfunction parseBlur(value: unknown): { diameterMm: number; largestComponent: PrintSizeLimitWithBlurAndNoise[\"blur\"][\"largestComponent\"] } {\n const r = record(value, [\"kind\", \"lensMm\", \"defocusMm\", \"diffractionMm\", \"motionMm\", \"diameterMm\"]);\n if (r.kind === \"measured-total\") {\n record(value, [\"kind\", \"diameterMm\"]);\n const diameterMm = requireInputNonNegativeFinite(r.diameterMm, \"blur.diameterMm\");\n return { diameterMm, largestComponent: diameterMm > 0 ? \"measured-total\" : null };\n }\n if (r.kind !== \"components\") throw new InvalidConfigurationError(\"Invalid print blur kind.\");\n let sumSquares = 0, largest = 0, largestComponent: PrintSizeLimitWithBlurAndNoise[\"blur\"][\"largestComponent\"] = null;\n for (const part of BLUR_PARTS) {\n const raw = r[`${part}Mm`];\n if (raw === undefined) continue;\n const d = requireInputNonNegativeFinite(raw, `blur.${part}Mm`);\n sumSquares += d * d;\n if (d > largest) { largest = d; largestComponent = part; }\n }\n return { diameterMm: Math.sqrt(sumSquares), largestComponent };\n}\n/** The one place the noise input becomes a per-pixel SNR, including a capture's own noise figure (#535). */\nfunction resolvePixelSnr(value: unknown): number {\n const r = record(value, [\"kind\", \"signalElectrons\", \"readNoiseElectrons\", \"snrLinear\", \"midGrey\"]);\n switch (r.kind) {\n case \"shot-and-read-noise\": {\n record(value, [\"kind\", \"signalElectrons\", \"readNoiseElectrons\"]);\n return calculateSignalToNoise({ signalElectrons: r.signalElectrons as number, readNoiseElectrons: r.readNoiseElectrons as number }).value.snrLinear;\n }\n case \"pixel-snr\":\n record(value, [\"kind\", \"snrLinear\"]);\n return requireInputNonNegativeFinite(r.snrLinear, \"noise.snrLinear\");\n case \"capture-noise-figure\": {\n record(value, [\"kind\", \"midGrey\"]);\n const figure = r.midGrey as { signalElectrons?: unknown; noiseElectrons?: { total?: unknown }; clipped?: unknown } | null;\n if (typeof figure !== \"object\" || figure === null || typeof figure.noiseElectrons !== \"object\" || figure.noiseElectrons === null)\n throw new InvalidConfigurationError(\"noise.midGrey must be a capture's result.sensor.midGrey (simulateCapture with fast: false).\");\n if (figure.clipped !== false) throw new InvalidConfigurationError(\"noise.midGrey is clipped (or not a capture's figure): the patch reaches white, where the figure no longer describes the image.\");\n return requireInputNonNegativeFinite(figure.signalElectrons, \"noise.midGrey.signalElectrons\") / requireInputPositiveFinite(figure.noiseElectrons.total, \"noise.midGrey.noiseElectrons.total\");\n }\n default: throw new InvalidConfigurationError(\"Invalid print noise kind.\");\n }\n}\n\n/** Calculates the largest print, at a fixed viewing distance, at which the capture still looks sharp and clean, and names\n * the factor that limits it: pixels, blur or noise. An approximation throughout; the equations are exact under its model.\n *\n * With θ the criterion angle, d the viewing distance, s = 2 d tan(θ/2) the length θ covers on the print, N_w the raster\n * width in pixels, b the blur diameter in pixels and SNR_px the per-pixel SNR (print width W, height in proportion):\n * - pixels: a print pixel (W / N_w) may cover at most s, so W ≤ N_w·s (as `calculatePrintPlan` with\n * `angular-pixel-pitch`, before its whole-pixel rounding);\n * - blur: the blur on the print (b·W / N_w) may cover at most s, so W ≤ N_w·s / b: the criterion of an acceptable circle\n * of confusion with the eye's resolution as the limit;\n * - noise: the eye averages the (W / N_w) wide pixels inside s, so the visible SNR is SNR_px·(N_w·s / W) while a pixel is\n * smaller than s (the mean of n independent values has 1/√n of their standard deviation); W ≤ N_w·s·SNR_px / SNR_min,\n * and noise never limits when SNR_px ≥ SNR_min.\n * One arcminute is normal (20/20) acuity under optimal conditions (Z. Alomar, K. D. Bojikian, \"Visual Acuity Testing in\n * Adults\", EyeWiki, American Academy of Ophthalmology, https://eyewiki.aao.org/Visual_Acuity_Testing_in_Adults, last edited\n * June 16, 2026); the SNR threshold has no default and is the caller's. Blur parts add in root-sum-square, which is exact\n * for Gaussian spots (variances of independent displacements add: C. M. Grinstead, J. L. Snell, \"Introduction to\n * Probability\", 2nd rev. ed., AMS 1997, sec. 6.2, free under the GNU FDL) and an approximation for discs and Airy patterns.\n */\nexport function calculatePrintSizeLimitWithBlurAndNoise(value: PrintSizeLimitWithBlurAndNoiseInput): CalculationResult<PrintSizeLimitWithBlurAndNoise> {\n const keys = [\"raster\", \"pixelPitchMm\", \"viewingDistance\", \"criterion\", \"blur\", \"noise\"];\n requireInputKeys(value, \"calculatePrintSizeLimitWithBlurAndNoise\", keys);\n const r = record(value, keys);\n const raster = record(r.raster, [\"pixelWidth\", \"pixelHeight\"]);\n const pixelWidth = requireInputPositiveFinite(raster.pixelWidth, \"raster.pixelWidth\"), pixelHeight = requireInputPositiveFinite(raster.pixelHeight, \"raster.pixelHeight\");\n requirePositiveInteger(\"raster.pixelWidth\", pixelWidth); requirePositiveInteger(\"raster.pixelHeight\", pixelHeight);\n const pixelPitchMm = requireInputPositiveFinite(r.pixelPitchMm, \"pixelPitchMm\");\n const distance = record(r.viewingDistance, [\"value\", \"unit\"]);\n if (typeof distance.unit !== \"string\" || !Object.hasOwn(UNIT_MM, distance.unit)) throw new InvalidConfigurationError(\"Invalid Print length unit.\");\n const viewingDistanceMm = requireInputPositiveFinite(distance.value, \"viewingDistance.value\") * UNIT_MM[distance.unit as PrintLength[\"unit\"]];\n const criterion = record(r.criterion, [\"kind\", \"arcminutes\", \"minimumVisibleSnr\"]);\n if (criterion.kind !== \"visual-resolution\") throw new InvalidConfigurationError(\"Invalid print criterion kind.\");\n const arcminutes = requireInputPositiveFinite(criterion.arcminutes, \"criterion.arcminutes\");\n if (arcminutes >= 10800) throw new InvalidConfigurationError(\"criterion.arcminutes must lie in (0, 10800).\");\n const minimumVisibleSnr = requireInputPositiveFinite(criterion.minimumVisibleSnr, \"criterion.minimumVisibleSnr\");\n const blur = parseBlur(r.blur);\n const pixelSnr = resolvePixelSnr(r.noise);\n\n const resolvedSpotMm = 2 * viewingDistanceMm * Math.tan((arcminutes * RAD_PER_ARCMIN) / 2);\n const pixelLimitWidth = pixelWidth * resolvedSpotMm;\n const blurPixels = blur.diameterMm / pixelPitchMm;\n const blurLimitWidth = blurPixels > 0 ? pixelLimitWidth / blurPixels : null;\n const noiseLimitWidth = pixelSnr < minimumVisibleSnr ? pixelLimitWidth * (pixelSnr / minimumVisibleSnr) : null;\n let limitingFactor: PrintSizeLimitWithBlurAndNoise[\"limitingFactor\"] = \"pixels\", width = pixelLimitWidth;\n if (blurLimitWidth !== null && blurLimitWidth < width) { limitingFactor = \"blur\"; width = blurLimitWidth; }\n if (noiseLimitWidth !== null && noiseLimitWidth < width) { limitingFactor = \"noise\"; width = noiseLimitWidth; }\n const size = (w: number): PrintImageMm => ({ width: w, height: w * (pixelHeight / pixelWidth) });\n const visibleSnrAtMaximum = width > 0 ? pixelSnr * Math.max(1, pixelLimitWidth / width) : 0;\n const result: PrintSizeLimitWithBlurAndNoise = {\n engineApiVersion: ENGINE_API_VERSION, status: width > 0 ? \"finite\" : \"no-positive-size\", limitingFactor,\n maximumImageMm: size(width),\n limits: { pixels: size(pixelLimitWidth), blur: blurLimitWidth === null ? null : size(blurLimitWidth), noise: noiseLimitWidth === null ? null : size(noiseLimitWidth) },\n viewingDistanceMm, resolvedSpotMm,\n blur: { diameterMm: blur.diameterMm, diameterPixels: blurPixels, largestComponent: blur.largestComponent },\n noise: { pixelSnr, visibleSnrAtMaximum }\n };\n const numbers = [viewingDistanceMm, resolvedSpotMm, pixelLimitWidth, size(pixelLimitWidth).height, blurPixels, blurLimitWidth ?? 0, width, visibleSnrAtMaximum];\n if (!numbers.every(Number.isFinite) || !(pixelLimitWidth > 0)) throw new InvalidConfigurationError(\"Print size limit lies outside the supported numeric range.\");\n return approximationResult(result, \"print-size-limit-blur-noise\", \"1.0.0\", [\n \"Flat print viewed at a fixed distance, at its center; one visual-resolution angle for pixels, blur and noise.\",\n \"Blur parts combine in root-sum-square: exact for Gaussian spots, an approximation for discs and Airy patterns.\",\n \"Pixel noise is white (independent pixels); demosaic, denoise and sharpening correlations are not modeled.\",\n \"The eye averages noise over a square of the criterion angle; contrast sensitivity and print medium are not modeled.\"\n ]);\n}\n"]}
|