Refine EXIF text formatting

This commit is contained in:
Sam Becker 2023-10-21 21:30:45 -05:00
parent 883fa7eeb3
commit 6cd0d0b285
4 changed files with 88 additions and 26 deletions

29
__tests__/exif.test.ts Normal file
View File

@ -0,0 +1,29 @@
import { formatExposureCompensation, formatExposureTime } from '@/utility/exif';
describe('EXIF', () => {
describe('formats', () => {
it('exposure time', () => {
expect(formatExposureTime(0)).toBe(undefined);
expect(formatExposureTime(undefined)).toBe(undefined);
expect(formatExposureTime(0.5)).toBe('1/2s');
expect(formatExposureTime(0.1)).toBe('1/10s');
expect(formatExposureTime(0.01)).toBe('1/100s');
expect(formatExposureTime(1)).toBe('1s');
expect(formatExposureTime(1.5)).toBe('1.5s');
});
it('exposure compensation', () => {
expect(formatExposureCompensation(-1)).toBe('-1ev');
expect(formatExposureCompensation(0)).toBe(undefined);
expect(formatExposureCompensation(0.33)).toBe('+1/3ev');
expect(formatExposureCompensation(0.333)).toBe('+1/3ev');
expect(formatExposureCompensation(0.5)).toBe('+1/2ev');
expect(formatExposureCompensation(0.66)).toBe('+2/3ev');
expect(formatExposureCompensation(0.67)).toBe('+2/3ev');
expect(formatExposureCompensation(0.015625)).toBe('+1/64ev');
expect(formatExposureCompensation(-0.015625)).toBe('-1/64ev');
expect(formatExposureCompensation(1)).toBe('+1ev');
expect(formatExposureCompensation(1.5)).toBe('+1 1/2ev');
expect(formatExposureCompensation(1.9960938)).toBe('+2ev');
});
});
});

View File

@ -90,17 +90,20 @@ export default function PhotoLarge({
{photo.focalLengthIn35MmFormatFormatted &&
<>
{' '}
<span className={cc(
'text-gray-400/80',
'dark:text-gray-400/50',
)}>
<span
className={cc(
'text-gray-400/80',
'dark:text-gray-400/50',
)}
title="35mm equivalent"
>
{photo.focalLengthIn35MmFormatFormatted}
</span>
</>}
</li>
<li>{photo.fNumberFormatted}</li>
<li>{photo.isoFormatted}</li>
<li>{photo.exposureTimeFormatted}</li>
<li>{photo.isoFormatted}</li>
<li>{photo.exposureCompensationFormatted ?? '—'}</li>
</ul>}
<div className={cc(

View File

@ -1,4 +1,5 @@
import type { ExifData } from 'ts-exif-parser';
import { formatNumberToFraction } from './number';
const OFFSET_REGEX = /[+-]\d\d:\d\d/;
@ -18,33 +19,19 @@ export const formatAperture = (aperture?: number) =>
export const formatIso = (iso?: number) =>
iso ? `ISO ${iso}` : undefined;
export const formatExposureTime = (exposureTime?: number) =>
exposureTime
? `Shutter 1/${Math.floor(1 / (exposureTime ?? 1))}`
export const formatExposureTime = (exposureTime = 0) =>
exposureTime > 0
? exposureTime < 1
? `1/${Math.floor(1 / exposureTime)}s`
: `${exposureTime}s`
: undefined;
const fractionForDecimal = (decimal: number, fractionCharacter?: boolean) => {
switch (Math.abs(Math.floor(decimal * 100))) {
case 33:
return fractionCharacter ? '⅓' : '1/3';
case 50:
return fractionCharacter ? '½' : '1/2';
case 66:
case 67:
return fractionCharacter ? '⅔' : '2/3';
}
};
export const formatExposureCompensation = (exposureCompensation?: number) => {
if (
exposureCompensation &&
Math.abs(exposureCompensation) >= 0.33
Math.abs(exposureCompensation) > 0.01
) {
const decimal = exposureCompensation % 1;
const whole = Math.abs(exposureCompensation - decimal);
const fraction = fractionForDecimal(decimal);
const sign = exposureCompensation > 0 ? '+' : '-';
return `${sign}${whole ? `${whole} ` : ''}${fraction ?? ''} EV`;
return `${formatNumberToFraction(exposureCompensation)}ev`;
} else {
return undefined;
}

View File

@ -5,3 +5,46 @@ export const toFixedNumber = (
const pow = Math.pow(base ?? 10, digits);
return Math.round(number * pow) / pow;
};
const gcd = (a: number, b: number): number => {
if (b <= 0.0000001) {
return a;
} else {
return gcd(b, a % b);
}
};
const formatDecimalToFraction = (decimal: number) => {
if (Math.abs(decimal - 0.33) < 0.011) {
return '1/3';
} else if (Math.abs(decimal - 0.66) <= 0.011) {
return '2/3';
} else {
const length = decimal.toString().length - 2;
let denominator = Math.pow(10, length);
let numerator = decimal * denominator;
const divisor = gcd(numerator, denominator);
numerator /= divisor;
denominator /= divisor;
return `${Math.floor(numerator)}/${Math.floor(denominator)}`;
}
};
export const formatNumberToFraction = (number: number) => {
const decimal = (1 - number % 1) > 0.01
? number % 1
: 0;
const integer = Math.round(Math.abs(number - decimal));
const fraction = decimal !== 0
? formatDecimalToFraction(Math.abs(decimal))
: '';
const sign = number > 0 ? '+' : '-';
const whole = integer > 0
? fraction ? `${integer} ` : integer
: '';
return `${sign}${whole}${fraction}`;
};