@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":"lens-corrections.js","sourceRoot":"","sources":["../../src/output/lens-corrections.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Generic lens corrections applied after capture: distortion, lateral chromatic aberration\n * (CA) and vignetting (corner darkening).\n *\n * It parses correction profiles, resolves which corrections are on for a camera state, and\n * applies them to linear RGB channels. It does not extrapolate beyond a profile and does not\n * claim to restore lost detail.\n * @see docs/DIGITAL_OPTICS_FOUNDATION.md for equations, coordinate/unit conventions, blockers and\n * support limits.\n */\n\nimport { approximationResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport { calculateIlluminationVignetting, type IlluminationVignettingProfile } from \"../optics/illumination-vignetting.js\";\nimport {\n choice, finite, list, parseEvidence, parseOpticalProfileState, positive, record,\n requireSameState, textValue, type GenericOpticalEvidence, type OpticalProfileState\n} from \"../optics/profile-contract.js\";\nimport type { LensFieldPointMm } from \"../optics/radial-distortion.js\";\nimport {\n calculateGeometricResampling, calculateGeometricSamplingPlan, parseDigitalGeometricTransform,\n prepareGeometricMapping, parseGeometricRaster, calculateValidSourceCrop, type DigitalGeometricTransform, type GeometricImageDomain,\n type GeometricRaster, type GeometricResampler, type GeometricSamplingPlan\n} from \"./geometric-transforms.js\";\nimport { requireInputObject, requireInputKeys } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\n\nexport type LensCorrectionChannel = \"red\" | \"green\" | \"blue\";\ninterface CorrectionComponentIdentity {\n id: string;\n version: string;\n domain: GeometricImageDomain;\n availability: \"toggle\" | \"automatic\" | \"mandatory\";\n defaultEnabled: boolean;\n dependencies: readonly string[];\n requiredByStabilizationModes: readonly string[];\n /** Declared remaining errors and calibration limits. They are not inferred from the optical model tier. */\n residualNote: string;\n}\n/** A distortion correction map, declared separately. It may deliberately leave some physical distortion in place. */\nexport interface GeometricLensCorrection extends CorrectionComponentIdentity {\n kind: \"geometry\";\n transform: DigitalGeometricTransform;\n}\n/** Lateral CA: shifts colour channels into register. It does not correct longitudinal CA or colour shading. */\nexport interface LateralCaLensCorrection extends CorrectionComponentIdentity {\n kind: \"lateral-ca\";\n transforms: Readonly<Record<LensCorrectionChannel, DigitalGeometricTransform>>;\n}\n/** Vignetting correction as a gain applied after capture. A strength below 1 deliberately leaves some falloff. Range 0..1. */\nexport interface IlluminationLensCorrection extends CorrectionComponentIdentity {\n kind: \"peripheral-illumination\";\n profile: IlluminationVignettingProfile;\n strength: number;\n}\nexport type LensCorrectionComponent = GeometricLensCorrection | LateralCaLensCorrection | IlluminationLensCorrection;\n/** A correction profile for one exact lens-and-camera system. Components are listed in the order they are applied. */\nexport interface GenericLensCorrectionProfile {\n schemaVersion: \"0.1.0\";\n id: string;\n version: string;\n state: OpticalProfileState;\n evidence: GenericOpticalEvidence;\n components: readonly LensCorrectionComponent[];\n}\nexport interface ResolvedLensCorrectionPlan {\n profile: GenericLensCorrectionProfile;\n outputKind: \"raw-like\" | \"processed\";\n selectionKind: \"camera-selectable\" | \"reference-bypass\";\n components: readonly { component: LensCorrectionComponent; enabled: boolean;\n reason: \"selected\" | \"default\" | \"mandatory\" | \"stabilization-dependency\" | \"component-dependency\" | \"reference-bypass\" }[];\n /** For now, a RAW-like intent is recorded as metadata only. */\n application: \"metadata-only\" | \"bake-downstream\";\n}\nfunction illumination(value: unknown): IlluminationVignettingProfile {\n const r = record(value, [\"normalizationRadiusMm\", \"maximumNormalizedRadius\", \"coefficients\"]);\n const c = record(r.coefficients, [\"r2\", \"r4\", \"r6\"]);\n const profile = { normalizationRadiusMm: positive(r.normalizationRadiusMm, \"normalizationRadiusMm\"),\n maximumNormalizedRadius: positive(r.maximumNormalizedRadius, \"maximumNormalizedRadius\"),\n coefficients: { r2: finite(c.r2, \"r2\"), r4: finite(c.r4, \"r4\"), r6: finite(c.r6, \"r6\") } };\n calculateIlluminationVignetting({ imagePointMm: { x: 0, y: 0 }, profile });\n return profile;\n}\n/** Strictly parses a generic correction profile. It does not extrapolate and makes no claim to restore lost detail. */\nexport function parseGenericLensCorrectionProfile(value: unknown): GenericLensCorrectionProfile {\n const r = record(value, [\"schemaVersion\", \"id\", \"version\", \"state\", \"evidence\", \"components\"]);\n const components = list(r.components, 16).map((value): LensCorrectionComponent => {\n const c = record(value, [\"id\", \"version\", \"domain\", \"availability\", \"defaultEnabled\", \"dependencies\",\n \"requiredByStabilizationModes\", \"residualNote\", \"kind\", \"transform\", \"transforms\", \"profile\", \"strength\"]);\n if (typeof c.defaultEnabled !== \"boolean\") throw new InvalidConfigurationError(\"defaultEnabled must be boolean.\");\n const identity = { id: textValue(c.id), version: textValue(c.version),\n domain: choice(c.domain, [\"raw-channel\", \"reconstructed-linear\", \"processed-output\"]),\n availability: choice(c.availability, [\"toggle\", \"automatic\", \"mandatory\"]), defaultEnabled: c.defaultEnabled,\n dependencies: list(c.dependencies, 16).map(textValue),\n requiredByStabilizationModes: list(c.requiredByStabilizationModes, 16).map(textValue), residualNote: textValue(c.residualNote) };\n const kind = choice(c.kind, [\"geometry\", \"lateral-ca\", \"peripheral-illumination\"]);\n if (kind === \"geometry\") {\n if (c.transforms !== undefined || c.profile !== undefined || c.strength !== undefined) throw new InvalidScientificInputError(\"Extraneous geometry parameters.\");\n const transform = parseDigitalGeometricTransform(c.transform);\n if (transform.domain !== identity.domain || ![\"distortion\", \"breathing\"].includes(transform.purpose)) throw new InvalidScientificInputError(\"Geometry domain/purpose mismatch.\");\n return { ...identity, kind, transform };\n }\n if (kind === \"lateral-ca\") {\n if (c.transform !== undefined || c.profile !== undefined || c.strength !== undefined) throw new InvalidScientificInputError(\"Extraneous CA parameters.\");\n const t = record(c.transforms, [\"red\", \"green\", \"blue\"]);\n const transforms = { red: parseDigitalGeometricTransform(t.red), green: parseDigitalGeometricTransform(t.green), blue: parseDigitalGeometricTransform(t.blue) };\n for (const transform of Object.values(transforms)) {\n if (transform.domain !== identity.domain || transform.purpose !== \"lateral-ca\") throw new InvalidScientificInputError(\"CA domain/purpose mismatch.\");\n }\n return { ...identity, kind, transforms };\n }\n if (c.transform !== undefined || c.transforms !== undefined) throw new InvalidScientificInputError(\"Extraneous illumination geometry.\");\n const strength = finite(c.strength, \"strength\");\n if (strength < 0 || strength > 1) throw new InvalidScientificInputError(\"Strength must lie in [0,1].\");\n return { ...identity, kind, profile: illumination(c.profile), strength };\n });\n const preceding = new Set<string>(), mappingIds = new Set<string>();\n for (const c of components) {\n if (preceding.has(c.id) || c.dependencies.some((id) => !preceding.has(id)) ||\n new Set(c.dependencies).size !== c.dependencies.length) throw new InvalidScientificInputError(\"Component IDs/dependency order must be unique and acyclic.\");\n preceding.add(c.id);\n // Common and per-channel transforms need stable, distinct IDs when they are combined.\n const maps = c.kind === \"geometry\" ? [c.transform] : c.kind === \"lateral-ca\" ? Object.values(c.transforms) : [];\n for (const t of maps) {\n if (mappingIds.has(t.id)) throw new InvalidScientificInputError(\"Duplicate correction transform ID.\");\n mappingIds.add(t.id);\n }\n }\n return { schemaVersion: choice(r.schemaVersion, [\"0.1.0\"]), id: textValue(r.id), version: textValue(r.version),\n state: parseOpticalProfileState(r.state), evidence: parseEvidence(r.evidence), components };\n}\n\n/**\n * Decides which corrections apply for a camera state. It accounts for per-component settings,\n * explicit dependencies between components, RAW intent and the educational \"corrections off\" bypass.\n */\nexport function resolveLensCorrectionPlan(input: {\n profile: GenericLensCorrectionProfile; state: OpticalProfileState;\n selections: Readonly<Record<string, \"on\" | \"off\" | \"auto\">>;\n outputKind: \"raw-like\" | \"processed\"; selectionKind: \"camera-selectable\" | \"reference-bypass\";\n}): CalculationResult<ResolvedLensCorrectionPlan> {\n requireInputKeys(input, \"resolveLensCorrectionPlan\", [\"profile\", \"state\", \"selections\", \"outputKind\", \"selectionKind\"]);\n const profile = parseGenericLensCorrectionProfile(input.profile);\n requireSameState(profile.state, input.state);\n const outputKind = choice(input.outputKind, [\"raw-like\", \"processed\"]);\n const selectionKind = choice(input.selectionKind, [\"camera-selectable\", \"reference-bypass\"]);\n const selections = record(input.selections, profile.components.map((c) => c.id));\n const components: ResolvedLensCorrectionPlan[\"components\"][number][] = profile.components.map((c): ResolvedLensCorrectionPlan[\"components\"][number] => {\n const selection = choice(selections[c.id] ?? \"auto\", [\"on\", \"off\", \"auto\"]);\n if (selectionKind === \"reference-bypass\") return { component: c, enabled: false, reason: \"reference-bypass\" };\n const stabilization = c.requiredByStabilizationModes.includes(profile.state.stabilizationMode);\n if (c.availability === \"mandatory\" && selection === \"off\") throw new InvalidScientificInputError(\"Mandatory correction cannot be disabled as a camera setting; use reference-bypass.\");\n if (c.availability === \"automatic\" && selection !== \"auto\") throw new InvalidScientificInputError(\"Automatic correction has no camera toggle.\");\n return { component: c, enabled: c.availability === \"mandatory\" || stabilization || (selection === \"auto\" ? c.defaultEnabled : selection === \"on\"),\n reason: c.availability === \"mandatory\" ? \"mandatory\" : stabilization ? \"stabilization-dependency\" : selection === \"auto\" ? \"default\" : \"selected\" };\n });\n for (const entry of [...components].reverse()) if (entry.enabled) for (const id of entry.component.dependencies) {\n const dependency = components.find((c) => c.component.id === id)!;\n dependency.enabled = true; dependency.reason = \"component-dependency\";\n }\n return approximationResult({ profile, outputKind, selectionKind, components,\n application: outputKind === \"raw-like\" ? \"metadata-only\" : \"bake-downstream\" },\n \"generic-camera-lens-correction-plan\", \"0.1.0\", [profile.evidence.basis, profile.evidence.residualNote,\n \"Corrections occur after the same physical capture; tier is not a correction-strength input\",\n \"Raw-like output preserves intent without baking corrections; color shading, axial CA and restoration remain unsupported\"]);\n}\n\n/**\n * Applies a gain after capture and reports the variance before clipping. It does not rewrite\n * the photon or SNR (signal-to-noise ratio) history of the capture.\n */\nexport function calculatePeripheralIlluminationCorrection(input: {\n component: IlluminationLensCorrection; imagePointMm: LensFieldPointMm;\n signal: number; noiseVariance: number; clippingLevel: number;\n}): CalculationResult<{ gain: number; signalBeforeClipping: number; signal: number; noiseVarianceBeforeClipping: number; clipped: boolean }> {\n requireInputKeys(input, \"calculatePeripheralIlluminationCorrection\", [\"component\", \"imagePointMm\", \"signal\", \"noiseVariance\", \"clippingLevel\"]);\n const component = input.component;\n requireInputObject(component, \"component input\");\n if (component.kind !== \"peripheral-illumination\" || component.strength < 0 || component.strength > 1 || !Number.isFinite(component.strength)) throw new InvalidScientificInputError(\"Invalid illumination component.\");\n const signal = finite(input.signal, \"signal\"), variance = finite(input.noiseVariance, \"noiseVariance\");\n const level = positive(input.clippingLevel, \"clippingLevel\");\n if (variance < 0) throw new InvalidScientificInputError(\"Variance must be nonnegative.\");\n const throughput = calculateIlluminationVignetting({ imagePointMm: input.imagePointMm, profile: component.profile }).value.linearThroughputFactor;\n const gain = throughput**(-component.strength), before = signal*gain;\n return approximationResult({ gain, signalBeforeClipping: before, signal: Math.min(level, before),\n noiseVarianceBeforeClipping: variance*gain*gain, clipped: before > level },\n \"post-capture-peripheral-illumination-gain\", \"0.1.0\", [\"Variance describes pre-clip samples only\", \"Gain does not increase captured photons or restore sensor SNR\"]);\n}\n\n/** The fixed capture identity travels with the output channels, so On/Off comparisons use the same capture. */\nexport interface LensCorrectionCapture {\n state: OpticalProfileState;\n captureId: string;\n noiseRealizationId: string;\n timeSeconds: number;\n raster: GeometricRaster;\n channels: Readonly<Record<LensCorrectionChannel, readonly number[]>>;\n}\n/** Applies bounded corrections to RGB channels that are already sampled and linear.\n * Compatible shared geometry and per-channel CA are combined, so each channel is resampled once.\n * A gain between geometry components, or incompatible domains, needs a separate staged caller.\n */\nexport function calculateLensCorrectedCapture(input: {\n plan: ResolvedLensCorrectionPlan; capture: LensCorrectionCapture;\n destinationRaster: GeometricRaster; resampler: GeometricResampler;\n physicalProjectionDistanceMm: number; clippingLevel: number;\n}): CalculationResult<{\n captureId: string; noiseRealizationId: string; timeSeconds: number;\n application: ResolvedLensCorrectionPlan[\"application\"];\n channels: Readonly<Record<LensCorrectionChannel, readonly (number | null)[]>>;\n samplingPlans: Partial<Record<LensCorrectionChannel, GeometricSamplingPlan>>;\n /** Area valid in every channel, before cropping. Missing colours are never made up. */\n validSourceMask: readonly boolean[];\n jointCrop: GeometricSamplingPlan[\"jointCrop\"];\n /** Clipping events in gain stages, before the shared valid-area crop. Each stage is counted separately. */\n illuminationClippingEventCount: number;\n}> {\n requireInputKeys(input, \"calculateLensCorrectedCapture\", [\"plan\", \"capture\", \"destinationRaster\", \"resampler\", \"physicalProjectionDistanceMm\", \"clippingLevel\"]);\n // Re-resolve the plan, which may be untrusted or changed, so callers cannot fake enabled or mandatory states.\n requireInputObject(input.plan, \"plan\");\n const plan = resolveLensCorrectionPlan({ profile: input.plan.profile, state: input.plan.profile.state,\n selections: Object.fromEntries(input.plan.components.filter((e) => e.component.availability === \"toggle\").map((e) => [e.component.id, e.enabled ? \"on\" : \"off\"])),\n outputKind: input.plan.outputKind, selectionKind: input.plan.selectionKind }).value;\n const capture = input.capture;\n requireSameState(plan.profile.state, capture.state);\n const sourceRaster = parseGeometricRaster(capture.raster);\n const destination = parseGeometricRaster(input.destinationRaster);\n if (destination.width !== plan.profile.state.outputWidth || destination.height !== plan.profile.state.outputHeight) {\n throw new InvalidScientificInputError(\"Destination raster differs from correction output binding.\");\n }\n textValue(capture.captureId); textValue(capture.noiseRealizationId);\n if (finite(capture.timeSeconds, \"timeSeconds\") < 0) throw new InvalidScientificInputError(\"Invalid capture time.\");\n const count = capture.raster.width*capture.raster.height;\n for (const channel of [\"red\", \"green\", \"blue\"] as const) {\n const data = list(capture.channels[channel], 65536);\n if (data.length !== count) throw new InvalidScientificInputError(\"Capture channel sample count mismatch.\");\n data.forEach((s) => finite(s, \"channel sample\"));\n }\n const active = plan.components.filter((e) => e.enabled).map((e) => e.component);\n const sameLattice = sourceRaster.width === destination.width && sourceRaster.height === destination.height &&\n sourceRaster.pitchMm === destination.pitchMm && sourceRaster.centerMm.x === destination.centerMm.x &&\n sourceRaster.centerMm.y === destination.centerMm.y;\n // Disabling corrections does not disable an explicit output crop/resampling request.\n if (plan.application === \"metadata-only\" || (!active.length && sameLattice)) {\n return approximationResult({ captureId: capture.captureId, noiseRealizationId: capture.noiseRealizationId,\n timeSeconds: capture.timeSeconds, application: plan.application, channels: capture.channels,\n samplingPlans: {}, validSourceMask: Array<boolean>(count).fill(true), illuminationClippingEventCount: 0,\n jointCrop: { x: 0, y: 0, width: capture.raster.width, height: capture.raster.height } },\n \"generic-lens-correction-capture\", \"0.1.0\", [\"Original physical capture preserved\"]);\n }\n if (active.some((c) => c.domain !== \"reconstructed-linear\")) throw new InvalidScientificInputError(\"This executor requires reconstructed-linear components; other domains need separate consumers.\");\n let seenGain = false;\n for (const c of active) {\n if (c.kind === \"peripheral-illumination\") seenGain = true;\n else if (seenGain) throw new InvalidScientificInputError(\"Cannot reorder geometry across a gain stage.\");\n }\n const channels = {} as Record<LensCorrectionChannel, readonly (number | null)[]>;\n const samplingPlans: Partial<Record<LensCorrectionChannel, GeometricSamplingPlan>> = {};\n let illuminationClippingEventCount = 0;\n for (const channel of [\"red\", \"green\", \"blue\"] as const) {\n // Corrections are listed source → destination. Inverse sampling runs them in reverse.\n const transforms = active.filter((c) => c.kind !== \"peripheral-illumination\").reverse().map((c) =>\n c.kind === \"geometry\" ? c.transform : (c as LateralCaLensCorrection).transforms[channel]);\n const mapping = prepareGeometricMapping({ transforms, frameTimeSeconds: capture.timeSeconds }).value;\n const sampling = calculateGeometricSamplingPlan({ mapping, sourceRaster: capture.raster,\n destinationRaster: input.destinationRaster, resampler: input.resampler,\n physicalProjectionDistanceMm: input.physicalProjectionDistanceMm }).value;\n samplingPlans[channel] = sampling;\n let data = calculateGeometricResampling({ plan: sampling, sourceSamples: capture.channels[channel] }).value.samples;\n for (const c of active) if (c.kind === \"peripheral-illumination\") {\n data = data.map((signal, index): number | null => {\n if (signal === null) return null;\n const raster = input.destinationRaster, x = index%raster.width, y = Math.floor(index/raster.width);\n const corrected = calculatePeripheralIlluminationCorrection({ component: c,\n imagePointMm: { x: raster.centerMm.x+(x+.5-raster.width/2)*raster.pitchMm,\n y: raster.centerMm.y-(y+.5-raster.height/2)*raster.pitchMm },\n signal, noiseVariance: 0, clippingLevel: input.clippingLevel }).value;\n if (corrected.clipped) illuminationClippingEventCount++;\n return corrected.signal;\n });\n }\n channels[channel] = data;\n }\n const mask = channels.red.map((_, i) => channels.red[i] !== null && channels.green[i] !== null && channels.blue[i] !== null);\n for (const channel of [\"red\", \"green\", \"blue\"] as const) channels[channel] = channels[channel].map((s, i) => mask[i] ? s : null);\n return approximationResult({ captureId: capture.captureId, noiseRealizationId: capture.noiseRealizationId,\n timeSeconds: capture.timeSeconds, application: plan.application, channels, samplingPlans, validSourceMask: mask,\n illuminationClippingEventCount,\n jointCrop: calculateValidSourceCrop(mask, destination.width, destination.height) },\n \"generic-lens-correction-capture\", \"0.1.0\", [\"Input capture/noise realization remains unchanged\", \"Channel support intersects before output; optical blur/noise is warped, not physically repaired\",\n \"No production-plan composition is enabled by this standalone executor\"]);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"lens-corrections.js","sourceRoot":"","sources":["../../src/output/lens-corrections.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Generic lens corrections applied after capture: distortion, lateral chromatic aberration\n * (CA) and vignetting (corner darkening).\n *\n * It parses correction profiles, resolves which corrections are on for a camera state, and\n * applies them to linear RGB channels. It does not extrapolate beyond a profile and does not\n * claim to restore lost detail.\n * @see docs/DIGITAL_OPTICS_FOUNDATION.md for equations, coordinate/unit conventions, blockers and\n * support limits.\n */\n\nimport { approximationResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport { calculateIlluminationVignetting, type IlluminationVignettingProfile } from \"../optics/illumination-vignetting.js\";\nimport {\n choice, finite, list, parseEvidence, parseOpticalProfileState, positive, record,\n requireSameState, textValue, type GenericOpticalEvidence, type OpticalProfileState\n} from \"../optics/profile-contract.js\";\nimport { calculateInverseRadialSourcePointValue, validateRadialDistortionProfile, type LensFieldPointMm, type RadialDistortionProfile } from \"../optics/radial-distortion.js\";\nimport {\n calculateGeometricResampling, calculateGeometricSamplingPlan, parseDigitalGeometricTransform,\n prepareGeometricMapping, parseGeometricRaster, calculateValidSourceCrop, type DigitalGeometricTransform, type GeometricImageDomain,\n type GeometricRaster, type GeometricResampler, type GeometricSamplingPlan\n} from \"./geometric-transforms.js\";\nimport { requireInputObject, requireInputKeys } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\n\nexport type LensCorrectionChannel = \"red\" | \"green\" | \"blue\";\ninterface CorrectionComponentIdentity {\n id: string;\n version: string;\n domain: GeometricImageDomain;\n availability: \"toggle\" | \"automatic\" | \"mandatory\";\n defaultEnabled: boolean;\n dependencies: readonly string[];\n requiredByStabilizationModes: readonly string[];\n /** Declared remaining errors and calibration limits. They are not inferred from the optical model tier. */\n residualNote: string;\n}\n/** A distortion correction map, declared separately. It may deliberately leave some physical distortion in place. */\nexport interface GeometricLensCorrection extends CorrectionComponentIdentity {\n kind: \"geometry\";\n transform: DigitalGeometricTransform;\n}\n/** Lateral CA: shifts colour channels into register. It does not correct longitudinal CA or colour shading. */\nexport interface LateralCaLensCorrection extends CorrectionComponentIdentity {\n kind: \"lateral-ca\";\n transforms: Readonly<Record<LensCorrectionChannel, DigitalGeometricTransform>>;\n}\n/** Vignetting correction as a gain applied after capture. A strength below 1 deliberately leaves some falloff. Range 0..1. */\nexport interface IlluminationLensCorrection extends CorrectionComponentIdentity {\n kind: \"peripheral-illumination\";\n profile: IlluminationVignettingProfile;\n strength: number;\n /**\n * Optional (#584): the capture's radial distortion r_d = r(1 + k1ρ² + k2ρ⁴ + k3ρ⁶), whose area factor 1/det J the capture also\n * multiplied into the irradiance (det J = (1 + k1ρ_i² + k2ρ_i⁴ + k3ρ_i⁶)(1 + 3k1ρ_i² + 5k2ρ_i⁴ + 7k3ρ_i⁶) at the ideal point ρ_i that\n * the lens images to the sensor point). With it, the relative illumination corrected is E(ρ_s)/det J(ρ_i), evaluated at the\n * capture's sensor (source) point, so strength 1 flattens a flat field. It holds whether or not the distortion itself is corrected.\n * Without it the gain is 1/E alone, as before.\n */\n distortionAreaFactor?: RadialDistortionProfile;\n}\nexport type LensCorrectionComponent = GeometricLensCorrection | LateralCaLensCorrection | IlluminationLensCorrection;\n/** A correction profile for one exact lens-and-camera system. Components are listed in the order they are applied. */\nexport interface GenericLensCorrectionProfile {\n schemaVersion: \"0.1.0\";\n id: string;\n version: string;\n state: OpticalProfileState;\n evidence: GenericOpticalEvidence;\n components: readonly LensCorrectionComponent[];\n}\nexport interface ResolvedLensCorrectionPlan {\n profile: GenericLensCorrectionProfile;\n outputKind: \"raw-like\" | \"processed\";\n selectionKind: \"camera-selectable\" | \"reference-bypass\";\n components: readonly { component: LensCorrectionComponent; enabled: boolean;\n reason: \"selected\" | \"default\" | \"mandatory\" | \"stabilization-dependency\" | \"component-dependency\" | \"reference-bypass\" }[];\n /** For now, a RAW-like intent is recorded as metadata only. */\n application: \"metadata-only\" | \"bake-downstream\";\n}\nfunction illumination(value: unknown): IlluminationVignettingProfile {\n const r = record(value, [\"normalizationRadiusMm\", \"maximumNormalizedRadius\", \"coefficients\"]);\n const c = record(r.coefficients, [\"r2\", \"r4\", \"r6\"]);\n const profile = { normalizationRadiusMm: positive(r.normalizationRadiusMm, \"normalizationRadiusMm\"),\n maximumNormalizedRadius: positive(r.maximumNormalizedRadius, \"maximumNormalizedRadius\"),\n coefficients: { r2: finite(c.r2, \"r2\"), r4: finite(c.r4, \"r4\"), r6: finite(c.r6, \"r6\") } };\n calculateIlluminationVignetting({ imagePointMm: { x: 0, y: 0 }, profile });\n return profile;\n}\n/** Strictly parses a generic correction profile. It does not extrapolate and makes no claim to restore lost detail. */\nexport function parseGenericLensCorrectionProfile(value: unknown): GenericLensCorrectionProfile {\n const r = record(value, [\"schemaVersion\", \"id\", \"version\", \"state\", \"evidence\", \"components\"]);\n const components = list(r.components, 16).map((value): LensCorrectionComponent => {\n const c = record(value, [\"id\", \"version\", \"domain\", \"availability\", \"defaultEnabled\", \"dependencies\",\n \"requiredByStabilizationModes\", \"residualNote\", \"kind\", \"transform\", \"transforms\", \"profile\", \"strength\", \"distortionAreaFactor\"]);\n if (typeof c.defaultEnabled !== \"boolean\") throw new InvalidConfigurationError(\"defaultEnabled must be boolean.\");\n const identity = { id: textValue(c.id), version: textValue(c.version),\n domain: choice(c.domain, [\"raw-channel\", \"reconstructed-linear\", \"processed-output\"]),\n availability: choice(c.availability, [\"toggle\", \"automatic\", \"mandatory\"]), defaultEnabled: c.defaultEnabled,\n dependencies: list(c.dependencies, 16).map(textValue),\n requiredByStabilizationModes: list(c.requiredByStabilizationModes, 16).map(textValue), residualNote: textValue(c.residualNote) };\n const kind = choice(c.kind, [\"geometry\", \"lateral-ca\", \"peripheral-illumination\"]);\n if (kind === \"geometry\") {\n if (c.transforms !== undefined || c.profile !== undefined || c.strength !== undefined || c.distortionAreaFactor !== undefined) throw new InvalidScientificInputError(\"Extraneous geometry parameters.\");\n const transform = parseDigitalGeometricTransform(c.transform);\n if (transform.domain !== identity.domain || ![\"distortion\", \"breathing\"].includes(transform.purpose)) throw new InvalidScientificInputError(\"Geometry domain/purpose mismatch.\");\n return { ...identity, kind, transform };\n }\n if (kind === \"lateral-ca\") {\n if (c.transform !== undefined || c.profile !== undefined || c.strength !== undefined || c.distortionAreaFactor !== undefined) throw new InvalidScientificInputError(\"Extraneous CA parameters.\");\n const t = record(c.transforms, [\"red\", \"green\", \"blue\"]);\n const transforms = { red: parseDigitalGeometricTransform(t.red), green: parseDigitalGeometricTransform(t.green), blue: parseDigitalGeometricTransform(t.blue) };\n for (const transform of Object.values(transforms)) {\n if (transform.domain !== identity.domain || transform.purpose !== \"lateral-ca\") throw new InvalidScientificInputError(\"CA domain/purpose mismatch.\");\n }\n return { ...identity, kind, transforms };\n }\n if (c.transform !== undefined || c.transforms !== undefined) throw new InvalidScientificInputError(\"Extraneous illumination geometry.\");\n const strength = finite(c.strength, \"strength\");\n if (strength < 0 || strength > 1) throw new InvalidScientificInputError(\"Strength must lie in [0,1].\");\n if (c.distortionAreaFactor === undefined) return { ...identity, kind, profile: illumination(c.profile), strength };\n const d = record(c.distortionAreaFactor, [\"normalizationRadiusMm\", \"maximumNormalizedRadius\", \"coefficients\"]), k = record(d.coefficients, [\"k1\", \"k2\", \"k3\"]);\n const distortionAreaFactor = { normalizationRadiusMm: positive(d.normalizationRadiusMm, \"normalizationRadiusMm\"), maximumNormalizedRadius: positive(d.maximumNormalizedRadius, \"maximumNormalizedRadius\"),\n coefficients: { k1: finite(k.k1, \"k1\"), k2: finite(k.k2, \"k2\"), k3: finite(k.k3, \"k3\") } };\n validateRadialDistortionProfile(distortionAreaFactor);\n return { ...identity, kind, profile: illumination(c.profile), strength, distortionAreaFactor };\n });\n const preceding = new Set<string>(), mappingIds = new Set<string>();\n for (const c of components) {\n if (preceding.has(c.id) || c.dependencies.some((id) => !preceding.has(id)) ||\n new Set(c.dependencies).size !== c.dependencies.length) throw new InvalidScientificInputError(\"Component IDs/dependency order must be unique and acyclic.\");\n preceding.add(c.id);\n // Common and per-channel transforms need stable, distinct IDs when they are combined.\n const maps = c.kind === \"geometry\" ? [c.transform] : c.kind === \"lateral-ca\" ? Object.values(c.transforms) : [];\n for (const t of maps) {\n if (mappingIds.has(t.id)) throw new InvalidScientificInputError(\"Duplicate correction transform ID.\");\n mappingIds.add(t.id);\n }\n }\n return { schemaVersion: choice(r.schemaVersion, [\"0.1.0\"]), id: textValue(r.id), version: textValue(r.version),\n state: parseOpticalProfileState(r.state), evidence: parseEvidence(r.evidence), components };\n}\n\n/**\n * Decides which corrections apply for a camera state. It accounts for per-component settings,\n * explicit dependencies between components, RAW intent and the educational \"corrections off\" bypass.\n */\nexport function resolveLensCorrectionPlan(input: {\n profile: GenericLensCorrectionProfile; state: OpticalProfileState;\n selections: Readonly<Record<string, \"on\" | \"off\" | \"auto\">>;\n outputKind: \"raw-like\" | \"processed\"; selectionKind: \"camera-selectable\" | \"reference-bypass\";\n}): CalculationResult<ResolvedLensCorrectionPlan> {\n requireInputKeys(input, \"resolveLensCorrectionPlan\", [\"profile\", \"state\", \"selections\", \"outputKind\", \"selectionKind\"]);\n const profile = parseGenericLensCorrectionProfile(input.profile);\n requireSameState(profile.state, input.state);\n const outputKind = choice(input.outputKind, [\"raw-like\", \"processed\"]);\n const selectionKind = choice(input.selectionKind, [\"camera-selectable\", \"reference-bypass\"]);\n const selections = record(input.selections, profile.components.map((c) => c.id));\n const components: ResolvedLensCorrectionPlan[\"components\"][number][] = profile.components.map((c): ResolvedLensCorrectionPlan[\"components\"][number] => {\n const selection = choice(selections[c.id] ?? \"auto\", [\"on\", \"off\", \"auto\"]);\n if (selectionKind === \"reference-bypass\") return { component: c, enabled: false, reason: \"reference-bypass\" };\n const stabilization = c.requiredByStabilizationModes.includes(profile.state.stabilizationMode);\n if (c.availability === \"mandatory\" && selection === \"off\") throw new InvalidScientificInputError(\"Mandatory correction cannot be disabled as a camera setting; use reference-bypass.\");\n if (c.availability === \"automatic\" && selection !== \"auto\") throw new InvalidScientificInputError(\"Automatic correction has no camera toggle.\");\n return { component: c, enabled: c.availability === \"mandatory\" || stabilization || (selection === \"auto\" ? c.defaultEnabled : selection === \"on\"),\n reason: c.availability === \"mandatory\" ? \"mandatory\" : stabilization ? \"stabilization-dependency\" : selection === \"auto\" ? \"default\" : \"selected\" };\n });\n for (const entry of [...components].reverse()) if (entry.enabled) for (const id of entry.component.dependencies) {\n const dependency = components.find((c) => c.component.id === id)!;\n dependency.enabled = true; dependency.reason = \"component-dependency\";\n }\n return approximationResult({ profile, outputKind, selectionKind, components,\n application: outputKind === \"raw-like\" ? \"metadata-only\" : \"bake-downstream\" },\n \"generic-camera-lens-correction-plan\", \"0.1.0\", [profile.evidence.basis, profile.evidence.residualNote,\n \"Corrections occur after the same physical capture; tier is not a correction-strength input\",\n \"Raw-like output preserves intent without baking corrections; color shading, axial CA and restoration remain unsupported\"]);\n}\n\n/**\n * Applies a gain after capture and reports the variance before clipping. It does not rewrite\n * the photon or SNR (signal-to-noise ratio) history of the capture. The gain is T^(−strength), T the relative illumination at\n * `imagePointMm`: E(ρ) of the component's profile, divided by det J(ρ_i) when the component has a `distortionAreaFactor` (#584;\n * then `imagePointMm` is the capture's sensor point, where the capture applied both).\n */\nexport function calculatePeripheralIlluminationCorrection(input: {\n component: IlluminationLensCorrection; imagePointMm: LensFieldPointMm;\n signal: number; noiseVariance: number; clippingLevel: number;\n}): CalculationResult<{ gain: number; signalBeforeClipping: number; signal: number; noiseVarianceBeforeClipping: number; clipped: boolean }> {\n requireInputKeys(input, \"calculatePeripheralIlluminationCorrection\", [\"component\", \"imagePointMm\", \"signal\", \"noiseVariance\", \"clippingLevel\"]);\n const component = input.component;\n requireInputObject(component, \"component input\");\n if (component.kind !== \"peripheral-illumination\" || component.strength < 0 || component.strength > 1 || !Number.isFinite(component.strength)) throw new InvalidScientificInputError(\"Invalid illumination component.\");\n const signal = finite(input.signal, \"signal\"), variance = finite(input.noiseVariance, \"noiseVariance\");\n const level = positive(input.clippingLevel, \"clippingLevel\");\n if (variance < 0) throw new InvalidScientificInputError(\"Variance must be nonnegative.\");\n let throughput = calculateIlluminationVignetting({ imagePointMm: input.imagePointMm, profile: component.profile }).value.linearThroughputFactor;\n if (component.distortionAreaFactor !== undefined) {\n const { k1, k2, k3 } = component.distortionAreaFactor.coefficients;\n const p2 = calculateInverseRadialSourcePointValue(input.imagePointMm, validateRadialDistortionProfile(component.distortionAreaFactor), \"imagePointMm\").sourceNormalizedRadius ** 2;\n throughput /= (1 + p2 * (k1 + p2 * (k2 + p2 * k3))) * (1 + p2 * (3 * k1 + p2 * (5 * k2 + p2 * 7 * k3)));\n }\n const gain = throughput**(-component.strength), before = signal*gain;\n return approximationResult({ gain, signalBeforeClipping: before, signal: Math.min(level, before),\n noiseVarianceBeforeClipping: variance*gain*gain, clipped: before > level },\n \"post-capture-peripheral-illumination-gain\", \"0.1.0\", [\"Variance describes pre-clip samples only\", \"Gain does not increase captured photons or restore sensor SNR\"]);\n}\n\n/** The fixed capture identity travels with the output channels, so On/Off comparisons use the same capture. */\nexport interface LensCorrectionCapture {\n state: OpticalProfileState;\n captureId: string;\n noiseRealizationId: string;\n timeSeconds: number;\n raster: GeometricRaster;\n channels: Readonly<Record<LensCorrectionChannel, readonly number[]>>;\n}\n/** Applies bounded corrections to RGB channels that are already sampled and linear.\n * Compatible shared geometry and per-channel CA are combined, so each channel is resampled once.\n * A gain between geometry components, or incompatible domains, needs a separate staged caller.\n */\nexport function calculateLensCorrectedCapture(input: {\n plan: ResolvedLensCorrectionPlan; capture: LensCorrectionCapture;\n destinationRaster: GeometricRaster; resampler: GeometricResampler;\n physicalProjectionDistanceMm: number; clippingLevel: number;\n}): CalculationResult<{\n captureId: string; noiseRealizationId: string; timeSeconds: number;\n application: ResolvedLensCorrectionPlan[\"application\"];\n channels: Readonly<Record<LensCorrectionChannel, readonly (number | null)[]>>;\n samplingPlans: Partial<Record<LensCorrectionChannel, GeometricSamplingPlan>>;\n /** Area valid in every channel, before cropping. Missing colours are never made up. */\n validSourceMask: readonly boolean[];\n jointCrop: GeometricSamplingPlan[\"jointCrop\"];\n /** Clipping events in gain stages, before the shared valid-area crop. Each stage is counted separately. */\n illuminationClippingEventCount: number;\n}> {\n requireInputKeys(input, \"calculateLensCorrectedCapture\", [\"plan\", \"capture\", \"destinationRaster\", \"resampler\", \"physicalProjectionDistanceMm\", \"clippingLevel\"]);\n // Re-resolve the plan, which may be untrusted or changed, so callers cannot fake enabled or mandatory states.\n requireInputObject(input.plan, \"plan\");\n const plan = resolveLensCorrectionPlan({ profile: input.plan.profile, state: input.plan.profile.state,\n selections: Object.fromEntries(input.plan.components.filter((e) => e.component.availability === \"toggle\").map((e) => [e.component.id, e.enabled ? \"on\" : \"off\"])),\n outputKind: input.plan.outputKind, selectionKind: input.plan.selectionKind }).value;\n const capture = input.capture;\n requireSameState(plan.profile.state, capture.state);\n const sourceRaster = parseGeometricRaster(capture.raster);\n const destination = parseGeometricRaster(input.destinationRaster);\n if (destination.width !== plan.profile.state.outputWidth || destination.height !== plan.profile.state.outputHeight) {\n throw new InvalidScientificInputError(\"Destination raster differs from correction output binding.\");\n }\n textValue(capture.captureId); textValue(capture.noiseRealizationId);\n if (finite(capture.timeSeconds, \"timeSeconds\") < 0) throw new InvalidScientificInputError(\"Invalid capture time.\");\n const count = capture.raster.width*capture.raster.height;\n for (const channel of [\"red\", \"green\", \"blue\"] as const) {\n const data = list(capture.channels[channel], 65536);\n if (data.length !== count) throw new InvalidScientificInputError(\"Capture channel sample count mismatch.\");\n data.forEach((s) => finite(s, \"channel sample\"));\n }\n const active = plan.components.filter((e) => e.enabled).map((e) => e.component);\n const sameLattice = sourceRaster.width === destination.width && sourceRaster.height === destination.height &&\n sourceRaster.pitchMm === destination.pitchMm && sourceRaster.centerMm.x === destination.centerMm.x &&\n sourceRaster.centerMm.y === destination.centerMm.y;\n // Disabling corrections does not disable an explicit output crop/resampling request.\n if (plan.application === \"metadata-only\" || (!active.length && sameLattice)) {\n return approximationResult({ captureId: capture.captureId, noiseRealizationId: capture.noiseRealizationId,\n timeSeconds: capture.timeSeconds, application: plan.application, channels: capture.channels,\n samplingPlans: {}, validSourceMask: Array<boolean>(count).fill(true), illuminationClippingEventCount: 0,\n jointCrop: { x: 0, y: 0, width: capture.raster.width, height: capture.raster.height } },\n \"generic-lens-correction-capture\", \"0.1.0\", [\"Original physical capture preserved\"]);\n }\n if (active.some((c) => c.domain !== \"reconstructed-linear\")) throw new InvalidScientificInputError(\"This executor requires reconstructed-linear components; other domains need separate consumers.\");\n let seenGain = false;\n for (const c of active) {\n if (c.kind === \"peripheral-illumination\") seenGain = true;\n else if (seenGain) throw new InvalidScientificInputError(\"Cannot reorder geometry across a gain stage.\");\n }\n const channels = {} as Record<LensCorrectionChannel, readonly (number | null)[]>;\n const samplingPlans: Partial<Record<LensCorrectionChannel, GeometricSamplingPlan>> = {};\n let illuminationClippingEventCount = 0;\n for (const channel of [\"red\", \"green\", \"blue\"] as const) {\n // Corrections are listed source → destination. Inverse sampling runs them in reverse.\n const transforms = active.filter((c) => c.kind !== \"peripheral-illumination\").reverse().map((c) =>\n c.kind === \"geometry\" ? c.transform : (c as LateralCaLensCorrection).transforms[channel]);\n const mapping = prepareGeometricMapping({ transforms, frameTimeSeconds: capture.timeSeconds }).value;\n const sampling = calculateGeometricSamplingPlan({ mapping, sourceRaster: capture.raster,\n destinationRaster: input.destinationRaster, resampler: input.resampler,\n physicalProjectionDistanceMm: input.physicalProjectionDistanceMm }).value;\n samplingPlans[channel] = sampling;\n let data = calculateGeometricResampling({ plan: sampling, sourceSamples: capture.channels[channel] }).value.samples;\n for (const c of active) if (c.kind === \"peripheral-illumination\") {\n data = data.map((signal, index): number | null => {\n if (signal === null) return null;\n const raster = input.destinationRaster, x = index%raster.width, y = Math.floor(index/raster.width);\n // #584: a gain that carries the distortion's area factor is the capture's own, so it is read at the source point.\n const corrected = calculatePeripheralIlluminationCorrection({ component: c,\n imagePointMm: c.distortionAreaFactor ? sampling.points[index]!.sourcePointMm : { x: raster.centerMm.x+(x+.5-raster.width/2)*raster.pitchMm,\n y: raster.centerMm.y-(y+.5-raster.height/2)*raster.pitchMm },\n signal, noiseVariance: 0, clippingLevel: input.clippingLevel }).value;\n if (corrected.clipped) illuminationClippingEventCount++;\n return corrected.signal;\n });\n }\n channels[channel] = data;\n }\n const mask = channels.red.map((_, i) => channels.red[i] !== null && channels.green[i] !== null && channels.blue[i] !== null);\n for (const channel of [\"red\", \"green\", \"blue\"] as const) channels[channel] = channels[channel].map((s, i) => mask[i] ? s : null);\n return approximationResult({ captureId: capture.captureId, noiseRealizationId: capture.noiseRealizationId,\n timeSeconds: capture.timeSeconds, application: plan.application, channels, samplingPlans, validSourceMask: mask,\n illuminationClippingEventCount,\n jointCrop: calculateValidSourceCrop(mask, destination.width, destination.height) },\n \"generic-lens-correction-capture\", \"0.1.0\", [\"Input capture/noise realization remains unchanged\", \"Channel support intersects before output; optical blur/noise is warped, not physically repaired\",\n \"No production-plan composition is enabled by this standalone executor\"]);\n}\n"]}
|
|
@@ -36,7 +36,7 @@ export function parsePrintContrastReferenceInput(value) {
|
|
|
36
36
|
eccentricityDegrees: number("eccentricityDegrees", false), modulationMichelson };
|
|
37
37
|
}
|
|
38
38
|
// Independently written scalar evaluation of the licensed numerical model.
|
|
39
|
-
// Using log1p/expm1 keeps the small
|
|
39
|
+
// Using log1p/expm1 keeps the hyperbolic terms accurate where the small quantity k_s4/Y (about 5.6e-10 at 1000 cd/m²) would cancel.
|
|
40
40
|
function luminanceDependency(p, luminance) {
|
|
41
41
|
if (p.length === 2)
|
|
42
42
|
return p[1] * luminance ** p[0];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"print-contrast-reference.js","sourceRoot":"","sources":["../../src/output/print-contrast-reference.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n// castleCSF numerical model material: Copyright (c) 2023 Graphics and Displays\n// group - University of Cambridge. MIT terms retained in NOTICE (the full\n// licence text). Original bounded TypeScript adapter.\n\n/** Contrast detection threshold for a static, neutral (D65 white) Gabor patch, from the castleCSF\n * contrast sensitivity function (CSF) model. A Gabor patch is a sine grating under a Gaussian envelope.\n * It does not say whether detail in a photograph will be visible.\n * @see docs/PRINT_CONTRAST_REFERENCE.md for exact source, applicability and review gates.\n */\nimport { estimatedResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { freezeOwnedData } from \"../core/owned-data.js\";\nimport { requireAllowlistedRecord, requirePublicOpaqueId } from \"../core/record-validation.js\";\nimport { ENGINE_API_VERSION } from \"../core/version.js\";\n\nexport const PRINT_CONTRAST_REFERENCE_MODEL_VERSION = \"0.1.0\" as const;\n/** Identifies the exact MIT-licensed model source. It is not a local calibration. */\nexport const PRINT_CONTRAST_REFERENCE_UPSTREAM_REVISION = \"f4b0b722af83001d7af979281e06ca642d36e4e8\" as const;\n\n/** The narrow reference stimulus. It is separate from any measured photographic region of interest (ROI).\n * Positive values outside the model's range return \"unsupported\" instead of being extrapolated.\n */\nexport interface PrintContrastReferenceInput {\n referenceId: string;\n stimulus: \"static-neutral-d65-gabor\";\n observer: \"published-binocular-natural-pupil-reference\";\n /** Mean luminance of the uniform D65 background and grating. Not the average of a photographic ROI. */\n meanLuminanceCdPerSquareMeter: number;\n spatialFrequencyCyclesPerDegree: number;\n gaussianEnvelopeSigmaDegrees: number;\n temporalFrequencyHz: number;\n eccentricityDegrees: number;\n /** Fractional amplitude of the neutral grating before the Gaussian envelope. Not taken from ROI minimum and maximum values. */\n modulationMichelson: number;\n}\nexport interface PrintContrastReference {\n referenceId: string;\n engineApiVersion: string;\n modelVersion: typeof PRINT_CONTRAST_REFERENCE_MODEL_VERSION;\n upstreamRevision: typeof PRINT_CONTRAST_REFERENCE_UPSTREAM_REVISION;\n input: PrintContrastReferenceInput;\n status: \"model-estimate\" | \"unsupported\";\n blockers: string[];\n estimate: { contrastSensitivity: number; thresholdModulationMichelson: number; suppliedModulationToModelThresholdRatio: number } | null;\n observerApplicability: \"population-reference-unverified-for-individual\";\n naturalImageVisibility: \"unassessed\";\n printStimulusMatch: \"unassessed\";\n uncertainty: \"not-quantified\";\n overallPrintVerdict: \"not-offered\";\n}\n\n/** Strictly parses and copies the explicit reference conditions. No defaults are filled in to imply an observer. */\nexport function parsePrintContrastReferenceInput(value: unknown): PrintContrastReferenceInput {\n const r = requireAllowlistedRecord(value, [\"referenceId\", \"stimulus\", \"observer\", \"meanLuminanceCdPerSquareMeter\", \"spatialFrequencyCyclesPerDegree\",\n \"gaussianEnvelopeSigmaDegrees\", \"temporalFrequencyHz\", \"eccentricityDegrees\", \"modulationMichelson\"], \"Invalid Print contrast reference fields.\");\n const number = (key: string, positive: boolean): number => {\n const v = r[key];\n if (typeof v !== \"number\" || !Number.isFinite(v) || (positive ? v <= 0 : v < 0)) throw new InvalidConfigurationError(\"Print contrast reference quantities must be finite and in their numeric domain.\");\n return v === 0 ? 0 : v;\n };\n if (r.stimulus !== \"static-neutral-d65-gabor\" || r.observer !== \"published-binocular-natural-pupil-reference\") throw new InvalidConfigurationError(\"Unknown Print reference stimulus/observer.\");\n const modulationMichelson = number(\"modulationMichelson\", false);\n if (modulationMichelson > 1) throw new InvalidConfigurationError(\"Reference Michelson modulation must be at most one.\");\n return { referenceId: requirePublicOpaqueId(r.referenceId, \"Invalid public Print contrast reference identity.\"), stimulus: r.stimulus, observer: r.observer,\n meanLuminanceCdPerSquareMeter: number(\"meanLuminanceCdPerSquareMeter\", true), spatialFrequencyCyclesPerDegree: number(\"spatialFrequencyCyclesPerDegree\", true),\n gaussianEnvelopeSigmaDegrees: number(\"gaussianEnvelopeSigmaDegrees\", true), temporalFrequencyHz: number(\"temporalFrequencyHz\", false),\n eccentricityDegrees: number(\"eccentricityDegrees\", false), modulationMichelson };\n}\n\n// Independently written scalar evaluation of the licensed numerical model.\n// Using log1p/expm1 keeps the small hyperbolic saturation term accurate.\nfunction luminanceDependency(p: readonly number[], luminance: number): number {\n if (p.length === 2) return p[1]! * luminance ** p[0]!;\n const first = p[0]! * Math.exp(-p[2]! * Math.log1p(p[1]! / luminance));\n return p.length === 3 ? first : first * -Math.expm1(-p[4]! * Math.log1p(p[3]! / luminance));\n}\nfunction component(frequency: number, area: number, peak: number, peakFrequency: number, bandwidth: number, lowFrequencyFloor: number, a0: number, f0: number): number {\n const logDifference = Math.log10(frequency) - Math.log10(peakFrequency);\n const parabola = 10 ** (-logDifference * logDifference / 2 ** bandwidth);\n const shape = frequency < peakFrequency ? Math.max(parabola, lowFrequencyFloor) : parabola;\n const criticalArea = a0 / (1 + (frequency / f0) ** 2);\n return peak * shape * Math.sqrt(criticalArea / (1 + criticalArea / area)) * frequency;\n}\nfunction staticNeutralSensitivity(luminance: number, frequency: number): number {\n const area = Math.PI * 1.5 ** 2;\n const sustained = component(frequency, area, luminanceDependency([56.4947, 7.54726, .144532, 5.58341e-7, 9.66862e9], luminance),\n luminanceDependency([1.78119, 91.5718, .256682], luminance), .000213047, 1 - .100207, 157.103, .702338);\n const transient = component(frequency, area, luminanceDependency([.193434, 2748.09], luminance), .000316696, 2.6761, 1 - .000241177, 3.81611, 3.01389);\n const omegaPeak = Math.log10(luminance) * 2.41482 + 4.7036;\n const achromatic = sustained + transient * Math.exp(-((omegaPeak ** .1898) ** 2) / .0844836);\n const redGreen = component(frequency, area, luminanceDependency([681.434, 38.0038, .480386], luminance), .0178364, 2.42104, 1, 2816.44, .0711058);\n const yellowViolet = component(frequency, area, luminanceDependency([166.683, 62.8974, .41193], luminance), .00425753, 2.68197, 1, 2.82789e7, .000635093);\n // Modulate each cone channel of the D65 LMS background [L,M,S] by the same fraction.\n // All three mechanism responses are kept, including nonzero colour-opponent parts.\n const redGreenProjection = Math.abs(.6991 - 2.3112 * .3009);\n const yellowVioletProjection = Math.abs(-.6991 - .3009 + 50.9875 * .0198);\n return Math.hypot(achromatic, redGreenProjection * redGreen, yellowVioletProjection * yellowViolet);\n}\n\n/** Estimates the detection threshold from the published model, within its limited range. It does not claim that detail is invisible to any particular person. */\nexport function calculatePrintContrastReference(value: PrintContrastReferenceInput): CalculationResult<PrintContrastReference> {\n const input = parsePrintContrastReferenceInput(value), l = input.meanLuminanceCdPerSquareMeter, f = input.spatialFrequencyCyclesPerDegree;\n const result: PrintContrastReference = { referenceId: input.referenceId, engineApiVersion: ENGINE_API_VERSION, modelVersion: PRINT_CONTRAST_REFERENCE_MODEL_VERSION,\n upstreamRevision: PRINT_CONTRAST_REFERENCE_UPSTREAM_REVISION, input, status: \"unsupported\", blockers: [], estimate: null,\n observerApplicability: \"population-reference-unverified-for-individual\", naturalImageVisibility: \"unassessed\", printStimulusMatch: \"unassessed\",\n uncertainty: \"not-quantified\", overallPrintVerdict: \"not-offered\" };\n if (l < 1 || l > 1000 || f < .25 || f > 16 || input.gaussianEnvelopeSigmaDegrees !== 1.5 || input.temporalFrequencyHz !== 0 || input.eccentricityDegrees !== 0) result.blockers.push(\"outside-static-neutral-reference-domain\");\n else {\n const sensitivity = staticNeutralSensitivity(l, f), threshold = 1 / sensitivity, ratio = input.modulationMichelson * sensitivity;\n if (![sensitivity, threshold, ratio].every(Number.isFinite) || sensitivity <= 0 || threshold <= 0) result.blockers.push(\"contrast-reference-numeric-range-unsupported\");\n else { result.status = \"model-estimate\"; result.estimate = { contrastSensitivity: sensitivity, thresholdModulationMichelson: threshold, suppliedModulationToModelThresholdRatio: ratio }; }\n }\n return freezeOwnedData(estimatedResult(result, \"castlecsf-static-neutral-d65-reference\", PRINT_CONTRAST_REFERENCE_MODEL_VERSION,\n [\"Static foveal D65 equal-fraction LMS Gabor; Gaussian envelope sigma is exactly 1.5 degrees.\",\n \"Published population model reference; personal eyesight, natural-image masking and actual print-stimulus matching remain unassessed.\",\n \"The numerical envelope is restricted; published model-fit errors do not become an individual confidence interval or a print verdict.\"], {\n validRanges: [\n { parameterPath: \"meanLuminanceCdPerSquareMeter\", unit: \"cd/m^2\", minimum: 1, maximum: 1000, note: \"Restricted numerical reference envelope; no individual calibration.\" },\n { parameterPath: \"spatialFrequencyCyclesPerDegree\", unit: \"cycles/degree\", minimum: .25, maximum: 16 },\n { parameterPath: \"gaussianEnvelopeSigmaDegrees\", unit: \"degree\", minimum: 1.5, maximum: 1.5 },\n { parameterPath: \"temporalFrequencyHz\", unit: \"Hz\", minimum: 0, maximum: 0 },\n { parameterPath: \"eccentricityDegrees\", unit: \"degree\", minimum: 0, maximum: 0 }\n ], notes: [\"Uncertainty is not quantified; numerical conformance is distinct from stimulus/observer qualification.\"]\n }));\n}\n"]}
|
|
1
|
+
{"version":3,"file":"print-contrast-reference.js","sourceRoot":"","sources":["../../src/output/print-contrast-reference.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n// castleCSF numerical model material: Copyright (c) 2023 Graphics and Displays\n// group - University of Cambridge. MIT terms retained in NOTICE (the full\n// licence text). Original bounded TypeScript adapter.\n\n/** Contrast detection threshold for a static, neutral (D65 white) Gabor patch, from the castleCSF\n * contrast sensitivity function (CSF) model. A Gabor patch is a sine grating under a Gaussian envelope.\n * It does not say whether detail in a photograph will be visible.\n * @see docs/PRINT_CONTRAST_REFERENCE.md for exact source, applicability and review gates.\n */\nimport { estimatedResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { freezeOwnedData } from \"../core/owned-data.js\";\nimport { requireAllowlistedRecord, requirePublicOpaqueId } from \"../core/record-validation.js\";\nimport { ENGINE_API_VERSION } from \"../core/version.js\";\n\nexport const PRINT_CONTRAST_REFERENCE_MODEL_VERSION = \"0.1.0\" as const;\n/** Identifies the exact MIT-licensed model source. It is not a local calibration. */\nexport const PRINT_CONTRAST_REFERENCE_UPSTREAM_REVISION = \"f4b0b722af83001d7af979281e06ca642d36e4e8\" as const;\n\n/** The narrow reference stimulus. It is separate from any measured photographic region of interest (ROI).\n * Positive values outside the model's range return \"unsupported\" instead of being extrapolated.\n */\nexport interface PrintContrastReferenceInput {\n referenceId: string;\n stimulus: \"static-neutral-d65-gabor\";\n observer: \"published-binocular-natural-pupil-reference\";\n /** Mean luminance of the uniform D65 background and grating. Not the average of a photographic ROI. */\n meanLuminanceCdPerSquareMeter: number;\n spatialFrequencyCyclesPerDegree: number;\n gaussianEnvelopeSigmaDegrees: number;\n temporalFrequencyHz: number;\n eccentricityDegrees: number;\n /** Fractional amplitude of the neutral grating before the Gaussian envelope. Not taken from ROI minimum and maximum values. */\n modulationMichelson: number;\n}\nexport interface PrintContrastReference {\n referenceId: string;\n engineApiVersion: string;\n modelVersion: typeof PRINT_CONTRAST_REFERENCE_MODEL_VERSION;\n upstreamRevision: typeof PRINT_CONTRAST_REFERENCE_UPSTREAM_REVISION;\n input: PrintContrastReferenceInput;\n status: \"model-estimate\" | \"unsupported\";\n blockers: string[];\n estimate: { contrastSensitivity: number; thresholdModulationMichelson: number; suppliedModulationToModelThresholdRatio: number } | null;\n observerApplicability: \"population-reference-unverified-for-individual\";\n naturalImageVisibility: \"unassessed\";\n printStimulusMatch: \"unassessed\";\n uncertainty: \"not-quantified\";\n overallPrintVerdict: \"not-offered\";\n}\n\n/** Strictly parses and copies the explicit reference conditions. No defaults are filled in to imply an observer. */\nexport function parsePrintContrastReferenceInput(value: unknown): PrintContrastReferenceInput {\n const r = requireAllowlistedRecord(value, [\"referenceId\", \"stimulus\", \"observer\", \"meanLuminanceCdPerSquareMeter\", \"spatialFrequencyCyclesPerDegree\",\n \"gaussianEnvelopeSigmaDegrees\", \"temporalFrequencyHz\", \"eccentricityDegrees\", \"modulationMichelson\"], \"Invalid Print contrast reference fields.\");\n const number = (key: string, positive: boolean): number => {\n const v = r[key];\n if (typeof v !== \"number\" || !Number.isFinite(v) || (positive ? v <= 0 : v < 0)) throw new InvalidConfigurationError(\"Print contrast reference quantities must be finite and in their numeric domain.\");\n return v === 0 ? 0 : v;\n };\n if (r.stimulus !== \"static-neutral-d65-gabor\" || r.observer !== \"published-binocular-natural-pupil-reference\") throw new InvalidConfigurationError(\"Unknown Print reference stimulus/observer.\");\n const modulationMichelson = number(\"modulationMichelson\", false);\n if (modulationMichelson > 1) throw new InvalidConfigurationError(\"Reference Michelson modulation must be at most one.\");\n return { referenceId: requirePublicOpaqueId(r.referenceId, \"Invalid public Print contrast reference identity.\"), stimulus: r.stimulus, observer: r.observer,\n meanLuminanceCdPerSquareMeter: number(\"meanLuminanceCdPerSquareMeter\", true), spatialFrequencyCyclesPerDegree: number(\"spatialFrequencyCyclesPerDegree\", true),\n gaussianEnvelopeSigmaDegrees: number(\"gaussianEnvelopeSigmaDegrees\", true), temporalFrequencyHz: number(\"temporalFrequencyHz\", false),\n eccentricityDegrees: number(\"eccentricityDegrees\", false), modulationMichelson };\n}\n\n// Independently written scalar evaluation of the licensed numerical model.\n// Using log1p/expm1 keeps the hyperbolic terms accurate where the small quantity k_s4/Y (about 5.6e-10 at 1000 cd/m²) would cancel.\nfunction luminanceDependency(p: readonly number[], luminance: number): number {\n if (p.length === 2) return p[1]! * luminance ** p[0]!;\n const first = p[0]! * Math.exp(-p[2]! * Math.log1p(p[1]! / luminance));\n return p.length === 3 ? first : first * -Math.expm1(-p[4]! * Math.log1p(p[3]! / luminance));\n}\nfunction component(frequency: number, area: number, peak: number, peakFrequency: number, bandwidth: number, lowFrequencyFloor: number, a0: number, f0: number): number {\n const logDifference = Math.log10(frequency) - Math.log10(peakFrequency);\n const parabola = 10 ** (-logDifference * logDifference / 2 ** bandwidth);\n const shape = frequency < peakFrequency ? Math.max(parabola, lowFrequencyFloor) : parabola;\n const criticalArea = a0 / (1 + (frequency / f0) ** 2);\n return peak * shape * Math.sqrt(criticalArea / (1 + criticalArea / area)) * frequency;\n}\nfunction staticNeutralSensitivity(luminance: number, frequency: number): number {\n const area = Math.PI * 1.5 ** 2;\n const sustained = component(frequency, area, luminanceDependency([56.4947, 7.54726, .144532, 5.58341e-7, 9.66862e9], luminance),\n luminanceDependency([1.78119, 91.5718, .256682], luminance), .000213047, 1 - .100207, 157.103, .702338);\n const transient = component(frequency, area, luminanceDependency([.193434, 2748.09], luminance), .000316696, 2.6761, 1 - .000241177, 3.81611, 3.01389);\n const omegaPeak = Math.log10(luminance) * 2.41482 + 4.7036;\n const achromatic = sustained + transient * Math.exp(-((omegaPeak ** .1898) ** 2) / .0844836);\n const redGreen = component(frequency, area, luminanceDependency([681.434, 38.0038, .480386], luminance), .0178364, 2.42104, 1, 2816.44, .0711058);\n const yellowViolet = component(frequency, area, luminanceDependency([166.683, 62.8974, .41193], luminance), .00425753, 2.68197, 1, 2.82789e7, .000635093);\n // Modulate each cone channel of the D65 LMS background [L,M,S] by the same fraction.\n // All three mechanism responses are kept, including nonzero colour-opponent parts.\n const redGreenProjection = Math.abs(.6991 - 2.3112 * .3009);\n const yellowVioletProjection = Math.abs(-.6991 - .3009 + 50.9875 * .0198);\n return Math.hypot(achromatic, redGreenProjection * redGreen, yellowVioletProjection * yellowViolet);\n}\n\n/** Estimates the detection threshold from the published model, within its limited range. It does not claim that detail is invisible to any particular person. */\nexport function calculatePrintContrastReference(value: PrintContrastReferenceInput): CalculationResult<PrintContrastReference> {\n const input = parsePrintContrastReferenceInput(value), l = input.meanLuminanceCdPerSquareMeter, f = input.spatialFrequencyCyclesPerDegree;\n const result: PrintContrastReference = { referenceId: input.referenceId, engineApiVersion: ENGINE_API_VERSION, modelVersion: PRINT_CONTRAST_REFERENCE_MODEL_VERSION,\n upstreamRevision: PRINT_CONTRAST_REFERENCE_UPSTREAM_REVISION, input, status: \"unsupported\", blockers: [], estimate: null,\n observerApplicability: \"population-reference-unverified-for-individual\", naturalImageVisibility: \"unassessed\", printStimulusMatch: \"unassessed\",\n uncertainty: \"not-quantified\", overallPrintVerdict: \"not-offered\" };\n if (l < 1 || l > 1000 || f < .25 || f > 16 || input.gaussianEnvelopeSigmaDegrees !== 1.5 || input.temporalFrequencyHz !== 0 || input.eccentricityDegrees !== 0) result.blockers.push(\"outside-static-neutral-reference-domain\");\n else {\n const sensitivity = staticNeutralSensitivity(l, f), threshold = 1 / sensitivity, ratio = input.modulationMichelson * sensitivity;\n if (![sensitivity, threshold, ratio].every(Number.isFinite) || sensitivity <= 0 || threshold <= 0) result.blockers.push(\"contrast-reference-numeric-range-unsupported\");\n else { result.status = \"model-estimate\"; result.estimate = { contrastSensitivity: sensitivity, thresholdModulationMichelson: threshold, suppliedModulationToModelThresholdRatio: ratio }; }\n }\n return freezeOwnedData(estimatedResult(result, \"castlecsf-static-neutral-d65-reference\", PRINT_CONTRAST_REFERENCE_MODEL_VERSION,\n [\"Static foveal D65 equal-fraction LMS Gabor; Gaussian envelope sigma is exactly 1.5 degrees.\",\n \"Published population model reference; personal eyesight, natural-image masking and actual print-stimulus matching remain unassessed.\",\n \"The numerical envelope is restricted; published model-fit errors do not become an individual confidence interval or a print verdict.\"], {\n validRanges: [\n { parameterPath: \"meanLuminanceCdPerSquareMeter\", unit: \"cd/m^2\", minimum: 1, maximum: 1000, note: \"Restricted numerical reference envelope; no individual calibration.\" },\n { parameterPath: \"spatialFrequencyCyclesPerDegree\", unit: \"cycles/degree\", minimum: .25, maximum: 16 },\n { parameterPath: \"gaussianEnvelopeSigmaDegrees\", unit: \"degree\", minimum: 1.5, maximum: 1.5 },\n { parameterPath: \"temporalFrequencyHz\", unit: \"Hz\", minimum: 0, maximum: 0 },\n { parameterPath: \"eccentricityDegrees\", unit: \"degree\", minimum: 0, maximum: 0 }\n ], notes: [\"Uncertainty is not quantified; numerical conformance is distinct from stimulus/observer qualification.\"]\n }));\n}\n"]}
|
|
@@ -10,7 +10,9 @@ export interface PrintLength {
|
|
|
10
10
|
value: number;
|
|
11
11
|
unit: "mm" | "cm" | "inches" | "m";
|
|
12
12
|
}
|
|
13
|
-
/** The flat printed image area. It excludes paper margins, borders and canvas wraps.
|
|
13
|
+
/** The flat printed image area. It excludes paper margins, borders and canvas wraps.
|
|
14
|
+
* With a `contain` fit it is instead the paper, or the printable area of it, that the image is fitted inside.
|
|
15
|
+
*/
|
|
14
16
|
export interface PrintedImageSize {
|
|
15
17
|
width: number;
|
|
16
18
|
height: number;
|
|
@@ -48,16 +50,43 @@ export interface PrintProviderConstraints {
|
|
|
48
50
|
minimumPpi?: number;
|
|
49
51
|
exactPpi?: number;
|
|
50
52
|
}
|
|
53
|
+
/** How the printed image relates to `printedImage`.
|
|
54
|
+
* - `confirmed-native-aspect`: `printedImage` is the printed image itself and must already have the native aspect ratio,
|
|
55
|
+
* within the stated relative error.
|
|
56
|
+
* - `contain`: `printedImage` is the paper. The image keeps the native aspect ratio and is scaled to the largest size that
|
|
57
|
+
* fits inside it, centered; the rest is unprinted border. `match-image` turns the paper a quarter turn when its
|
|
58
|
+
* orientation (portrait or landscape) differs from the image's; `as-given` keeps it. Layout only: nothing is cropped or resampled.
|
|
59
|
+
*/
|
|
60
|
+
export type PrintFit = {
|
|
61
|
+
kind: "confirmed-native-aspect";
|
|
62
|
+
maximumRelativeAspectError: number;
|
|
63
|
+
} | {
|
|
64
|
+
kind: "contain";
|
|
65
|
+
paperOrientation: "as-given" | "match-image";
|
|
66
|
+
};
|
|
67
|
+
/** The layout of a `contain` fit, in millimetres. The printed image itself is the plan's `printedImageMm`. */
|
|
68
|
+
export interface PrintContainLayout {
|
|
69
|
+
/** The paper as placed: after the quarter turn when `paperRotated`. */
|
|
70
|
+
paperMm: {
|
|
71
|
+
width: number;
|
|
72
|
+
height: number;
|
|
73
|
+
};
|
|
74
|
+
paperRotated: boolean;
|
|
75
|
+
/** Unprinted border on each side of the centered image; zero when the paper has the image's aspect ratio. */
|
|
76
|
+
bordersMm: {
|
|
77
|
+
left: number;
|
|
78
|
+
right: number;
|
|
79
|
+
top: number;
|
|
80
|
+
bottom: number;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
51
83
|
/** Plan input. The output keeps the exact native aspect ratio; the allowed physical aspect error is explicit and reported. */
|
|
52
84
|
export interface PrintPlanInput {
|
|
53
85
|
source: PrintNativeSource;
|
|
54
86
|
printedImage: PrintedImageSize;
|
|
55
87
|
viewingDistance: PrintLength;
|
|
56
88
|
sampling: PrintSamplingCriterion;
|
|
57
|
-
fit:
|
|
58
|
-
kind: "confirmed-native-aspect";
|
|
59
|
-
maximumRelativeAspectError: number;
|
|
60
|
-
};
|
|
89
|
+
fit: PrintFit;
|
|
61
90
|
provider?: PrintProviderConstraints;
|
|
62
91
|
}
|
|
63
92
|
/** Pixel density and angular pixel pitch for each axis. The angles are pure geometry, not a measure of visual acuity. */
|
|
@@ -106,6 +135,8 @@ export interface PrintPlan {
|
|
|
106
135
|
} | null;
|
|
107
136
|
capturedDetailAssessment: "unassessed";
|
|
108
137
|
deliveryAssessment: "unassessed";
|
|
138
|
+
/** Present only with a `contain` fit; null when the native raster is unknown. */
|
|
139
|
+
contain?: PrintContainLayout | null;
|
|
109
140
|
}
|
|
110
141
|
/** Validates and copies an untrusted print request. It never assumes the source is native if that is not declared. */
|
|
111
142
|
export declare function parsePrintPlanInput(value: unknown): PrintPlanInput;
|
|
@@ -135,6 +166,11 @@ export interface PrintSizeLimit {
|
|
|
135
166
|
} | null;
|
|
136
167
|
reasons: string[];
|
|
137
168
|
referencePlan: PrintPlan;
|
|
169
|
+
/** Present only with a `contain` fit: the paper that holds `maximumImageMm` with the same layout (borders scale with it). */
|
|
170
|
+
maximumPaperMm?: {
|
|
171
|
+
width: number;
|
|
172
|
+
height: number;
|
|
173
|
+
} | null;
|
|
138
174
|
}
|
|
139
175
|
/** Calculates the largest print size under the stated conditions. Lab constraints need individual plans,
|
|
140
176
|
* so this size query reports them as unsupported rather than ignoring them.
|
|
@@ -118,10 +118,7 @@ export function parsePrintPlanInput(value) {
|
|
|
118
118
|
else
|
|
119
119
|
throw new InvalidConfigurationError("Invalid native source kind.");
|
|
120
120
|
const image = record(r.printedImage, ["width", "height", "unit"]);
|
|
121
|
-
const
|
|
122
|
-
if (fit.kind !== "confirmed-native-aspect" || typeof fit.maximumRelativeAspectError !== "number" || !Number.isFinite(fit.maximumRelativeAspectError) || fit.maximumRelativeAspectError < 0 || fit.maximumRelativeAspectError > 0.01)
|
|
123
|
-
throw new InvalidConfigurationError("Declare a native-aspect fit tolerance in [0, 0.01].");
|
|
124
|
-
const parsed = { 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 } };
|
|
121
|
+
const parsed = { 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) };
|
|
125
122
|
if (r.provider !== undefined) {
|
|
126
123
|
const p = record(r.provider, ["minimumRaster", "exactRaster", "minimumPpi", "exactPpi"]);
|
|
127
124
|
parsed.provider = {
|
|
@@ -133,6 +130,30 @@ export function parsePrintPlanInput(value) {
|
|
|
133
130
|
}
|
|
134
131
|
return parsed;
|
|
135
132
|
}
|
|
133
|
+
function parseFit(value) {
|
|
134
|
+
const fit = record(value, ["kind", "maximumRelativeAspectError", "paperOrientation"]);
|
|
135
|
+
if (fit.kind === "contain") {
|
|
136
|
+
record(value, ["kind", "paperOrientation"]);
|
|
137
|
+
if (fit.paperOrientation !== "as-given" && fit.paperOrientation !== "match-image")
|
|
138
|
+
throw new InvalidConfigurationError("Declare a contain-fit paper orientation: as-given or match-image.");
|
|
139
|
+
return { kind: "contain", paperOrientation: fit.paperOrientation };
|
|
140
|
+
}
|
|
141
|
+
if (fit.kind !== "confirmed-native-aspect" || fit.paperOrientation !== undefined || typeof fit.maximumRelativeAspectError !== "number" || !Number.isFinite(fit.maximumRelativeAspectError) || fit.maximumRelativeAspectError < 0 || fit.maximumRelativeAspectError > 0.01)
|
|
142
|
+
throw new InvalidConfigurationError("Declare a native-aspect fit tolerance in [0, 0.01].");
|
|
143
|
+
return { kind: "confirmed-native-aspect", maximumRelativeAspectError: fit.maximumRelativeAspectError };
|
|
144
|
+
}
|
|
145
|
+
/** The largest image at the native aspect ratio inside the paper, in the paper's unit, and its centered layout.
|
|
146
|
+
* The binding axis equals the paper exactly; the other is clamped so rounding never makes a negative border.
|
|
147
|
+
*/
|
|
148
|
+
function containImage(native, paperWidth, paperHeight, orientation) {
|
|
149
|
+
const imageLandscape = native.pixelWidth > native.pixelHeight, imagePortrait = native.pixelWidth < native.pixelHeight;
|
|
150
|
+
const paperRotated = orientation === "match-image" && ((imageLandscape && paperWidth < paperHeight) || (imagePortrait && paperWidth > paperHeight));
|
|
151
|
+
const pw = paperRotated ? paperHeight : paperWidth, ph = paperRotated ? paperWidth : paperHeight;
|
|
152
|
+
const widthBinds = native.pixelWidth * ph >= native.pixelHeight * pw;
|
|
153
|
+
const width = widthBinds ? pw : Math.min(pw, ph * native.pixelWidth / native.pixelHeight);
|
|
154
|
+
const height = widthBinds ? Math.min(ph, pw * native.pixelHeight / native.pixelWidth) : ph;
|
|
155
|
+
return { width, height, paperWidth: pw, paperHeight: ph, paperRotated };
|
|
156
|
+
}
|
|
136
157
|
function gcd(a, b) { while (b !== 0) {
|
|
137
158
|
const next = a % b;
|
|
138
159
|
a = b;
|
|
@@ -171,12 +192,22 @@ function diagnostics(raster, width, height, distance) {
|
|
|
171
192
|
export function calculatePrintPlan(value) {
|
|
172
193
|
const input = parsePrintPlanInput(value);
|
|
173
194
|
const plan = { 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" };
|
|
174
|
-
const
|
|
195
|
+
const contain = input.fit.kind === "contain" ? input.fit : null;
|
|
196
|
+
if (contain !== null)
|
|
197
|
+
plan.contain = null;
|
|
198
|
+
const finish = () => 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."])]);
|
|
175
199
|
const unsupported = () => { plan.status = "unsupported"; plan.nativePixelSufficiency = "cannot-assess"; plan.reasons.push("unsupported-numeric-range"); return finish(); };
|
|
176
|
-
const
|
|
200
|
+
const unitMm = UNIT_MM[input.printedImage.unit], distance = input.viewingDistance.value * UNIT_MM[input.viewingDistance.unit];
|
|
201
|
+
// A contain fit needs the native aspect ratio before the printed image size is known.
|
|
202
|
+
const resolved = input.source.kind === "native-retained" ? resolveCaptureGeometry(input.source.geometry).value : null;
|
|
203
|
+
const native = resolved === null ? null : { pixelWidth: resolved.output.cropRect.width, pixelHeight: resolved.output.cropRect.height };
|
|
204
|
+
const fitted = contain === null || native === null ? null : containImage(native, input.printedImage.width, input.printedImage.height, contain.paperOrientation);
|
|
205
|
+
const imageWidth = fitted?.width ?? input.printedImage.width, imageHeight = fitted?.height ?? input.printedImage.height;
|
|
206
|
+
const width = imageWidth * unitMm, height = imageHeight * unitMm;
|
|
177
207
|
if (!positiveSupported(width, height, distance))
|
|
178
208
|
return unsupported();
|
|
179
|
-
|
|
209
|
+
if (contain === null || fitted !== null)
|
|
210
|
+
plan.printedImageMm = { width, height };
|
|
180
211
|
plan.viewingDistanceMm = distance;
|
|
181
212
|
const angle = input.sampling.kind === "manual-ppi" ? null : angularLimit(input.sampling);
|
|
182
213
|
const pitchFactor = angle === null ? null : 2 * Math.tan((angle * RAD_PER_ARCMIN) / 2);
|
|
@@ -186,12 +217,20 @@ export function calculatePrintPlan(value) {
|
|
|
186
217
|
return unsupported();
|
|
187
218
|
const guidance = { derivedPixelsPerInch: ppi, maximumPixelPitchMm: pitch, maximumArcminutesPerPixel: angle };
|
|
188
219
|
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."]);
|
|
189
|
-
if (input.source.kind === "unavailable") {
|
|
190
|
-
|
|
220
|
+
if (input.source.kind === "unavailable" || native === null) {
|
|
221
|
+
if (input.source.kind === "unavailable")
|
|
222
|
+
plan.reasons.push(input.source.reason);
|
|
191
223
|
return finish();
|
|
192
224
|
}
|
|
193
|
-
|
|
194
|
-
|
|
225
|
+
if (fitted !== null) {
|
|
226
|
+
const paperWidth = fitted.paperWidth * unitMm, paperHeight = fitted.paperHeight * unitMm;
|
|
227
|
+
// Borders are taken in the paper's unit, then converted, so whole-unit borders stay exact.
|
|
228
|
+
const side = (paper, image) => Math.max(0, (paper - image) / 2) * unitMm;
|
|
229
|
+
const x = side(fitted.paperWidth, imageWidth), y = side(fitted.paperHeight, imageHeight);
|
|
230
|
+
if (!positiveSupported(paperWidth, paperHeight) || !Number.isFinite(x) || !Number.isFinite(y))
|
|
231
|
+
return unsupported();
|
|
232
|
+
plan.contain = { paperMm: { width: paperWidth, height: paperHeight }, paperRotated: fitted.paperRotated, bordersMm: { left: x, right: x, top: y, bottom: y } };
|
|
233
|
+
}
|
|
195
234
|
const aspectError = Math.abs((width / height) / (native.pixelWidth / native.pixelHeight) - 1);
|
|
196
235
|
if (!Number.isFinite(aspectError))
|
|
197
236
|
return unsupported();
|
|
@@ -200,13 +239,14 @@ export function calculatePrintPlan(value) {
|
|
|
200
239
|
if (!positiveSupported(nativeDiagnostics.pixelsPerInch.x, nativeDiagnostics.pixelsPerInch.y, nativeDiagnostics.angularPixelPitchArcminutes.x, nativeDiagnostics.angularPixelPitchArcminutes.y))
|
|
201
240
|
return unsupported();
|
|
202
241
|
plan.native = nativeDiagnostics;
|
|
203
|
-
|
|
242
|
+
// A contain fit has the native aspect ratio by construction; its rounding error is reported, not checked.
|
|
243
|
+
if (input.fit.kind === "confirmed-native-aspect" && aspectError > input.fit.maximumRelativeAspectError) {
|
|
204
244
|
plan.status = "crop-confirmation-required";
|
|
205
245
|
plan.reasons.push("printed-image-and-confirmed-crop-aspect-mismatch");
|
|
206
246
|
return finish();
|
|
207
247
|
}
|
|
208
|
-
const widthInches = input.printedImage.unit === "inches" ?
|
|
209
|
-
const heightInches = input.printedImage.unit === "inches" ?
|
|
248
|
+
const widthInches = input.printedImage.unit === "inches" ? imageWidth : width / 25.4;
|
|
249
|
+
const heightInches = input.printedImage.unit === "inches" ? imageHeight : height / 25.4;
|
|
210
250
|
const required = input.sampling.kind === "manual-ppi"
|
|
211
251
|
? minimumRaster(native, widthInches * ppi, heightInches * ppi)
|
|
212
252
|
: minimumRaster(native, width / pitch, height / pitch);
|
|
@@ -308,6 +348,11 @@ export function calculatePrintSizeLimit(value) {
|
|
|
308
348
|
result.maximumImageMm = referencePlan.alternatives.maximumImageMmAtCurrentDistance;
|
|
309
349
|
}
|
|
310
350
|
}
|
|
351
|
+
if (referencePlan.input.fit.kind === "contain") {
|
|
352
|
+
const layout = referencePlan.contain, image = referencePlan.printedImageMm, maximum = result.maximumImageMm;
|
|
353
|
+
const scale = layout && image && maximum ? maximum.width / image.width : null;
|
|
354
|
+
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;
|
|
355
|
+
}
|
|
311
356
|
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."]);
|
|
312
357
|
}
|
|
313
358
|
//# sourceMappingURL=print-plan.js.map
|