Add lens presets and contact footer
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
export const NORMAL_RATIO_RANGE = {
|
||||
min: 0.7,
|
||||
max: 1.5,
|
||||
};
|
||||
|
||||
export const scoreLensForNormalView = (lens, sensor, ratioRange = NORMAL_RATIO_RANGE) => {
|
||||
const aperture = Number.isFinite(lens?.maxAperture) ? lens.maxAperture : Infinity;
|
||||
const sensorDiagonal = Number.isFinite(sensor?.diagonal) ? sensor.diagonal : Infinity;
|
||||
const cropFactor = Number.isFinite(sensor?.cropFactor) ? sensor.cropFactor : 1;
|
||||
const delta = Number.isFinite(sensorDiagonal) ? Math.abs(lens.focalLength - sensorDiagonal) : Infinity;
|
||||
const ratio = sensorDiagonal > 0 ? lens.focalLength / sensorDiagonal : Infinity;
|
||||
const withinNormalBand = ratio >= ratioRange.min && ratio <= ratioRange.max;
|
||||
const effectiveFocalLength = lens.focalLength * cropFactor;
|
||||
|
||||
return {
|
||||
lens,
|
||||
aperture,
|
||||
delta,
|
||||
ratio,
|
||||
withinNormalBand,
|
||||
cropFactor,
|
||||
effectiveFocalLength,
|
||||
};
|
||||
};
|
||||
|
||||
const groupLensesBySensor = (lenses) =>
|
||||
lenses.reduce((acc, lens) => {
|
||||
if (!acc[lens.sensorId]) {
|
||||
acc[lens.sensorId] = [];
|
||||
}
|
||||
acc[lens.sensorId].push(lens);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
export const selectNormalLensForSensor = (lenses, sensor, ratioRange = NORMAL_RATIO_RANGE) => {
|
||||
if (!Array.isArray(lenses) || lenses.length === 0 || !sensor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const scored = lenses.map((lens) => scoreLensForNormalView(lens, sensor, ratioRange));
|
||||
const normalCandidates = scored.filter((entry) => entry.withinNormalBand);
|
||||
const candidates = normalCandidates.length > 0 ? normalCandidates : scored;
|
||||
|
||||
candidates.sort((a, b) => {
|
||||
if (a.aperture !== b.aperture) {
|
||||
return a.aperture - b.aperture;
|
||||
}
|
||||
if (a.delta !== b.delta) {
|
||||
return a.delta - b.delta;
|
||||
}
|
||||
return a.lens.focalLength - b.lens.focalLength;
|
||||
});
|
||||
|
||||
return candidates[0]?.lens ?? null;
|
||||
};
|
||||
|
||||
export const selectWideLensForSensor = (lenses, sensor, ratioRange = NORMAL_RATIO_RANGE) => {
|
||||
if (!Array.isArray(lenses) || lenses.length === 0 || !sensor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const scored = lenses.map((lens) => scoreLensForNormalView(lens, sensor, ratioRange));
|
||||
const wideCandidates = scored.filter((entry) => entry.ratio < ratioRange.min);
|
||||
const candidates = wideCandidates.length > 0 ? wideCandidates : scored;
|
||||
|
||||
candidates.sort((a, b) => {
|
||||
if (a.ratio !== b.ratio) {
|
||||
return a.ratio - b.ratio;
|
||||
}
|
||||
if (a.aperture !== b.aperture) {
|
||||
return a.aperture - b.aperture;
|
||||
}
|
||||
return a.lens.focalLength - b.lens.focalLength;
|
||||
});
|
||||
|
||||
return candidates[0]?.lens ?? null;
|
||||
};
|
||||
|
||||
export const selectPortraitLensForSensor = (lenses, sensor, ratioRange = NORMAL_RATIO_RANGE) => {
|
||||
if (!Array.isArray(lenses) || lenses.length === 0 || !sensor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const PORTRAIT_TARGET_EQUIV_MM = 85;
|
||||
const PORTRAIT_MIN_EQUIV_MM = 70;
|
||||
|
||||
const scored = lenses.map((lens) => scoreLensForNormalView(lens, sensor, ratioRange));
|
||||
const portraitCandidates = scored.filter((entry) => entry.effectiveFocalLength >= PORTRAIT_MIN_EQUIV_MM);
|
||||
const candidates = portraitCandidates.length > 0 ? portraitCandidates : scored;
|
||||
|
||||
candidates.sort((a, b) => {
|
||||
const deltaA = Math.abs(a.effectiveFocalLength - PORTRAIT_TARGET_EQUIV_MM);
|
||||
const deltaB = Math.abs(b.effectiveFocalLength - PORTRAIT_TARGET_EQUIV_MM);
|
||||
if (deltaA !== deltaB) {
|
||||
return deltaA - deltaB;
|
||||
}
|
||||
if (a.aperture !== b.aperture) {
|
||||
return a.aperture - b.aperture;
|
||||
}
|
||||
if (a.effectiveFocalLength !== b.effectiveFocalLength) {
|
||||
return b.effectiveFocalLength - a.effectiveFocalLength;
|
||||
}
|
||||
return b.lens.focalLength - a.lens.focalLength;
|
||||
});
|
||||
|
||||
return candidates[0]?.lens ?? null;
|
||||
};
|
||||
|
||||
const selectLensIdsForSensorsBySelector = (sensors, lenses, selector, ratioRange = NORMAL_RATIO_RANGE) => {
|
||||
if (!Array.isArray(sensors) || sensors.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const lensMap = groupLensesBySensor(lenses);
|
||||
|
||||
const selectedIds = sensors
|
||||
.map((sensor) => {
|
||||
const sensorLenses = lensMap[sensor.id];
|
||||
if (!sensorLenses || sensorLenses.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const match = selector(sensorLenses, sensor, ratioRange);
|
||||
return match?.id ?? null;
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
return selectedIds.length > 0 ? Array.from(new Set(selectedIds)) : [];
|
||||
};
|
||||
|
||||
export const selectNormalLensIdsForSensors = (sensors, lenses, ratioRange = NORMAL_RATIO_RANGE) =>
|
||||
selectLensIdsForSensorsBySelector(sensors, lenses, selectNormalLensForSensor, ratioRange);
|
||||
|
||||
export const selectWideLensIdsForSensors = (sensors, lenses, ratioRange = NORMAL_RATIO_RANGE) =>
|
||||
selectLensIdsForSensorsBySelector(sensors, lenses, selectWideLensForSensor, ratioRange);
|
||||
|
||||
export const selectPortraitLensIdsForSensors = (sensors, lenses, ratioRange = NORMAL_RATIO_RANGE) =>
|
||||
selectLensIdsForSensorsBySelector(sensors, lenses, selectPortraitLensForSensor, ratioRange);
|
||||
Reference in New Issue
Block a user