diff --git a/src/App.jsx b/src/App.jsx
index 59e571e..8262826 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -174,17 +174,20 @@ const PreviewPanel = ({ lensCurves, previewDistanceMeters }) => {
}
const blurMm = stat?.blurMm ?? 0;
+ const blurPercent = stat?.blurPercentOfDiagonal ??
+ ((curve.sensor?.diagonal ?? 0) > 0 ? (blurMm / curve.sensor.diagonal) * 100 : 0);
return {
lens: curve.lens,
blurMm,
+ blurPercent,
sensorDiagonal: curve.sensor?.diagonal ?? 1,
colorIndex: curve.colorIndex,
};
});
- const maxBlurMm = previewItems.reduce((maxValue, item) => Math.max(maxValue, item.blurMm), 0);
- const denominator = maxBlurMm > 0 ? maxBlurMm : 1;
+ const maxBlurPercent = previewItems.reduce((maxValue, item) => Math.max(maxValue, item.blurPercent), 0);
+ const denominator = maxBlurPercent > 0 ? maxBlurPercent : 1;
const distanceLabel = Number.isFinite(previewDistanceMeters)
? `${formatNumber(previewDistanceMeters, previewDistanceMeters >= 10 ? 0 : 1)} m`
: '∞';
@@ -200,7 +203,7 @@ const PreviewPanel = ({ lensCurves, previewDistanceMeters }) => {
) : (
{previewItems.map((item) => {
- const normalized = Math.min(item.blurMm / denominator, 1);
+ const normalized = Math.min(item.blurPercent / denominator, 1);
const diameter = Math.max(18, 30 + normalized * 70);
const paletteColor = COLOR_PALETTE[item.colorIndex % COLOR_PALETTE.length];
@@ -221,7 +224,7 @@ const PreviewPanel = ({ lensCurves, previewDistanceMeters }) => {
{item.lens.name}
- Blur {formatNumber(item.blurMm, 2)} mm
+ Blur {formatNumber(item.blurPercent, 2)}% diag
diff --git a/src/utils/optics.js b/src/utils/optics.js
index daa087a..77667cd 100644
--- a/src/utils/optics.js
+++ b/src/utils/optics.js
@@ -83,13 +83,20 @@ export const calculateBlurCircle = ({ focalLengthMm, aperture, subjectDistanceM,
const safeAperture = coercePositiveNumber(aperture, 1);
const f = focalLengthMm;
const u = metersToMillimeters(coercePositiveNumber(subjectDistanceM, 0.5));
- const s = metersToMillimeters(coercePositiveNumber(sceneDistanceM, 0.51));
- if (Math.abs(s - u) < 1e-6) {
+ if (Math.abs(u - f) < 1e-6) {
return 0;
}
- if (Math.abs(u - f) < 1e-6) {
+ if (!Number.isFinite(sceneDistanceM)) {
+ const denominator = safeAperture * (u - f);
+ return Math.abs((f ** 2) / denominator);
+ }
+
+ const sceneMeters = Math.max(sceneDistanceM, 0.2);
+ const s = metersToMillimeters(sceneMeters);
+
+ if (Math.abs(s - u) < 1e-6) {
return 0;
}
@@ -197,7 +204,7 @@ export const buildStatsAtCheckpoints = ({
sceneDistanceM: Infinity,
});
} else {
- sceneDistance = safeSubjectDistance + separation;
+ sceneDistance = Math.max(safeSubjectDistance + separation, 0.2);
blurMm = calculateBlurCircle({
focalLengthMm: lens.focalLength,
aperture,