More refactoring
This commit is contained in:
+33
-22
@@ -50,6 +50,17 @@ export class LevelCalculator {
|
|||||||
return resultLevel;
|
return resultLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findNearestStandards(standards: Standard[], target: number, getValue: (x: Metrics) => number) {
|
||||||
|
const lowerStandard = [...standards]
|
||||||
|
.sort((a, b) => getValue(a.metrics) - getValue(b.metrics))
|
||||||
|
.reverse()
|
||||||
|
.find((s) => getValue(s.metrics) <= target) ?? standards.at(0)!;
|
||||||
|
const upperStandard = [...standards]
|
||||||
|
.sort((a, b) => getValue(a.metrics) - getValue(b.metrics))
|
||||||
|
.find((s) => getValue(s.metrics) >= target) ?? standards.at(-1)!;
|
||||||
|
return { lowerStandard, upperStandard };
|
||||||
|
}
|
||||||
|
|
||||||
compressLevels(
|
compressLevels(
|
||||||
levels: Levels,
|
levels: Levels,
|
||||||
targetLevelsAmount: number,
|
targetLevelsAmount: number,
|
||||||
@@ -122,25 +133,22 @@ export class LevelCalculator {
|
|||||||
return interpolatedLevels;
|
return interpolatedLevels;
|
||||||
}
|
}
|
||||||
|
|
||||||
interpolateByBodyWeight(
|
interpolateByWeight(
|
||||||
targetAge: number,
|
targetAge: number,
|
||||||
bodyWeightKG: number,
|
targetWeight: number,
|
||||||
standardsArr: Standard[],
|
standards: Standard[],
|
||||||
): Levels | null {
|
): Levels {
|
||||||
const ageFilteredStandards = standardsArr.filter((s) => s.metrics.age === targetAge);
|
const ageFilteredStandards = standards.filter((s) => s.metrics.age === targetAge);
|
||||||
if (ageFilteredStandards[0].metrics.weight === -1) return ageFilteredStandards[0].levels;
|
if (ageFilteredStandards[0].metrics.weight === -1) return ageFilteredStandards[0].levels;
|
||||||
|
|
||||||
const weightArray = [...new Set(ageFilteredStandards.map((s) => s.metrics.weight))].sort((a, b) => a - b);
|
const { upperStandard, lowerStandard } = this.findNearestStandards(ageFilteredStandards, targetWeight, m => m.weight);
|
||||||
const { lower, upper } = findNearestPoints(bodyWeightKG, weightArray);
|
const weightLower = lowerStandard.metrics.weight;
|
||||||
|
const weightUpper = upperStandard.metrics.weight;
|
||||||
|
|
||||||
const weightLower = ageFilteredStandards.find((s) => s.metrics.weight === lower);
|
let weightRatio = weightUpper === weightLower ? 1 : (targetWeight - weightLower) / (weightUpper - weightLower);
|
||||||
const weightUpper = ageFilteredStandards.find((s) => s.metrics.weight === upper);
|
|
||||||
if (!weightLower || !weightUpper) return null;
|
|
||||||
|
|
||||||
let weightRatio = upper === lower ? 1 : (bodyWeightKG - lower) / (upper - lower);
|
|
||||||
weightRatio = Math.max(0, Math.min(1, weightRatio));
|
weightRatio = Math.max(0, Math.min(1, weightRatio));
|
||||||
|
|
||||||
return this.interpolateLevels(weightLower.levels, weightUpper.levels, weightRatio);
|
return this.interpolateLevels(lowerStandard.levels, upperStandard.levels, weightRatio);
|
||||||
}
|
}
|
||||||
|
|
||||||
getInterpolatedActivityStandard(
|
getInterpolatedActivityStandard(
|
||||||
@@ -152,27 +160,30 @@ export class LevelCalculator {
|
|||||||
levels: {}
|
levels: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Filter dataset
|
// Filter by gender
|
||||||
const standardsByGender = standards[activity]
|
const standardsByGender = standards[activity]
|
||||||
.filter(a => a.metrics.gender === metrics.gender);
|
.filter(a => a.metrics.gender === metrics.gender);
|
||||||
if (standardsByGender.length === 0) return interpolatedStandard;
|
if (standardsByGender.length === 0) return interpolatedStandard;
|
||||||
|
|
||||||
// Age interpolation
|
// Find nearest age standards
|
||||||
const ageArray = [...new Set(standardsByGender.map((s) => s.metrics.age))].sort((a, b) => a - b);
|
const { lowerStandard, upperStandard } = this.findNearestStandards(standardsByGender, metrics.age, m => m.age);
|
||||||
const { lower: ageLower, upper: ageUpper } = findNearestPoints(metrics.age, ageArray);
|
const ageLower = lowerStandard.metrics.age;
|
||||||
|
const ageUpper = upperStandard.metrics.age;
|
||||||
|
|
||||||
const lowerValues = this.interpolateByBodyWeight(ageLower, metrics.weight, standardsByGender);
|
// Interpolate by weight
|
||||||
const upperValues = this.interpolateByBodyWeight(ageUpper, metrics.weight, standardsByGender);
|
const lowerLevels = this.interpolateByWeight(ageLower, metrics.weight, standardsByGender);
|
||||||
if (!lowerValues || !upperValues) return interpolatedStandard;
|
const upperLevels = this.interpolateByWeight(ageUpper, metrics.weight, standardsByGender);
|
||||||
|
|
||||||
|
// Interpolate by age
|
||||||
let ageRatio = ageUpper === ageLower ? 1 : (metrics.age - ageLower) / (ageUpper - ageLower);
|
let ageRatio = ageUpper === ageLower ? 1 : (metrics.age - ageLower) / (ageUpper - ageLower);
|
||||||
ageRatio = Math.max(0, Math.min(1, ageRatio));
|
ageRatio = Math.max(0, Math.min(1, ageRatio));
|
||||||
|
const levels = this.interpolateLevels(lowerLevels, upperLevels, ageRatio);
|
||||||
|
|
||||||
const levels = this.interpolateLevels(lowerValues, upperValues, ageRatio);
|
// Post-process
|
||||||
const expandedLevels = this.expandLevels(levels, this.cfg.expandIters);
|
const expandedLevels = this.expandLevels(levels, this.cfg.expandIters);
|
||||||
const compressedLevels = this.compressLevels(expandedLevels, this.cfg.compressTo);
|
const compressedLevels = this.compressLevels(expandedLevels, this.cfg.compressTo);
|
||||||
|
|
||||||
interpolatedStandard.levels = compressedLevels;
|
interpolatedStandard.levels = compressedLevels;
|
||||||
|
|
||||||
return interpolatedStandard;
|
return interpolatedStandard;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user