Finish up weight generation
This commit is contained in:
@@ -0,0 +1,97 @@
|
|||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"unit": "kg"
|
||||||
|
},
|
||||||
|
"weights": [
|
||||||
|
{
|
||||||
|
"gender": "Male",
|
||||||
|
"age": 10,
|
||||||
|
"weight": 38.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gender": "Male",
|
||||||
|
"age": 15,
|
||||||
|
"weight": 66.1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gender": "Male",
|
||||||
|
"age": 20,
|
||||||
|
"weight": 81.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gender": "Male",
|
||||||
|
"age": 30,
|
||||||
|
"weight": 89.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gender": "Male",
|
||||||
|
"age": 40,
|
||||||
|
"weight": 90.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gender": "Male",
|
||||||
|
"age": 50,
|
||||||
|
"weight": 89.6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gender": "Male",
|
||||||
|
"age": 60,
|
||||||
|
"weight": 89.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gender": "Male",
|
||||||
|
"age": 70,
|
||||||
|
"weight": 89.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gender": "Male",
|
||||||
|
"age": 80,
|
||||||
|
"weight": 79.6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gender": "Female",
|
||||||
|
"age": 10,
|
||||||
|
"weight": 39.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gender": "Female",
|
||||||
|
"age": 15,
|
||||||
|
"weight": 58.1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gender": "Female",
|
||||||
|
"age": 20,
|
||||||
|
"weight": 69.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gender": "Female",
|
||||||
|
"age": 30,
|
||||||
|
"weight": 73.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gender": "Female",
|
||||||
|
"age": 40,
|
||||||
|
"weight": 75.2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gender": "Female",
|
||||||
|
"age": 50,
|
||||||
|
"weight": 74.6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gender": "Female",
|
||||||
|
"age": 60,
|
||||||
|
"weight": 75.1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gender": "Female",
|
||||||
|
"age": 70,
|
||||||
|
"weight": 73.2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gender": "Female",
|
||||||
|
"age": 80,
|
||||||
|
"weight": 66.3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -4739,7 +4739,7 @@
|
|||||||
"generators": [
|
"generators": [
|
||||||
{
|
{
|
||||||
"metric": "weight",
|
"metric": "weight",
|
||||||
"spread": 4,
|
"spread": 3,
|
||||||
"step": 12,
|
"step": 12,
|
||||||
"ratio": "inverse"
|
"ratio": "inverse"
|
||||||
}
|
}
|
||||||
@@ -5229,7 +5229,14 @@
|
|||||||
"BroadJump": {
|
"BroadJump": {
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"attribute": "Power",
|
"attribute": "Power",
|
||||||
"generators": [],
|
"generators": [
|
||||||
|
{
|
||||||
|
"metric": "weight",
|
||||||
|
"spread": 3,
|
||||||
|
"step": 12,
|
||||||
|
"ratio": "inverse"
|
||||||
|
}
|
||||||
|
],
|
||||||
"unit": "cm",
|
"unit": "cm",
|
||||||
"name": "Broad Jump"
|
"name": "Broad Jump"
|
||||||
},
|
},
|
||||||
@@ -5267,7 +5274,14 @@
|
|||||||
"ConeDrill": {
|
"ConeDrill": {
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"attribute": "Agility",
|
"attribute": "Agility",
|
||||||
"generators": [],
|
"generators": [
|
||||||
|
{
|
||||||
|
"metric": "weight",
|
||||||
|
"spread": 3,
|
||||||
|
"step": 12,
|
||||||
|
"ratio": "inverse"
|
||||||
|
}
|
||||||
|
],
|
||||||
"unit": "ms",
|
"unit": "ms",
|
||||||
"name": "3 Cone Drill"
|
"name": "3 Cone Drill"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Activity, Attribute, Gender, lbToKg, type ActivityPerformance, type Player, type StandardUnit } from "./util";
|
import { Activity, Attribute, Gender, getAvgWeight, lbToKg, type ActivityPerformance, type Player, type StandardUnit } from "./util";
|
||||||
|
|
||||||
// SOURCES
|
// SOURCES
|
||||||
// Squat, Bench, Dead Lift:
|
// Squat, Bench, Dead Lift:
|
||||||
@@ -216,19 +216,16 @@ export class Standards {
|
|||||||
const weightGenerators = allGenerators.filter(g => g.metric === "weight");
|
const weightGenerators = allGenerators.filter(g => g.metric === "weight");
|
||||||
for (const weightGenerator of weightGenerators) {
|
for (const weightGenerator of weightGenerators) {
|
||||||
for (const gender of Object.values(Gender)) {
|
for (const gender of Object.values(Gender)) {
|
||||||
const standardsByGender = this.byActivity(activity).byGender(gender).getAll();
|
for (const age of this.agesFor(activity, gender)) {
|
||||||
const ages = [...new Set(standardsByGender.map(s => s.metrics.age))];
|
const referenceWeight = getAvgWeight(gender, age);
|
||||||
|
|
||||||
for (const age of ages) {
|
const skewRatio = 0.4; // 0 = normal, 1 = max skew
|
||||||
const referenceWeight = gender === Gender.Male ?
|
const minWeight = referenceWeight - (weightGenerator.step * weightGenerator.spread * (1 - skewRatio));
|
||||||
lbToKg(170) :
|
const maxWeight = referenceWeight + (weightGenerator.step * weightGenerator.spread * (1 + skewRatio));
|
||||||
lbToKg(137);
|
|
||||||
const minWeight = referenceWeight - (weightGenerator.step * weightGenerator.spread);
|
|
||||||
const maxWeight = referenceWeight + (weightGenerator.step * weightGenerator.spread);
|
|
||||||
|
|
||||||
const referenceStandard = this
|
const referenceStandard = this
|
||||||
.byActivity(activity)
|
.byActivity(activity)
|
||||||
.byMetrics({ weight: referenceWeight, gender: gender, age: age })
|
.byMetrics({ weight: referenceWeight, gender, age })
|
||||||
.getOneInterpolated();
|
.getOneInterpolated();
|
||||||
|
|
||||||
let currWeight = minWeight;
|
let currWeight = minWeight;
|
||||||
@@ -236,8 +233,8 @@ export class Standards {
|
|||||||
const newStandard: Standard = {
|
const newStandard: Standard = {
|
||||||
metrics: {
|
metrics: {
|
||||||
weight: currWeight,
|
weight: currWeight,
|
||||||
age: age,
|
age,
|
||||||
gender: gender
|
gender
|
||||||
},
|
},
|
||||||
levels: {}
|
levels: {}
|
||||||
};
|
};
|
||||||
@@ -340,6 +337,8 @@ export class Standards {
|
|||||||
let weightRatio = weightUpper === weightLower ? 1 : (metrics.weight - weightLower) / (weightUpper - weightLower);
|
let weightRatio = weightUpper === weightLower ? 1 : (metrics.weight - weightLower) / (weightUpper - weightLower);
|
||||||
weightRatio = Math.max(0, Math.min(1, weightRatio));
|
weightRatio = Math.max(0, Math.min(1, weightRatio));
|
||||||
|
|
||||||
|
// metrics.weight = lowerAgeWeightStandard.metrics.weight + (upperAgeWeightStandard.metrics.weight - lowerAgeWeightStandard.metrics.weight) * weightRatio;
|
||||||
|
|
||||||
return this.interpolateLevels(lowerAgeWeightStandard.levels, upperAgeWeightStandard.levels, weightRatio);
|
return this.interpolateLevels(lowerAgeWeightStandard.levels, upperAgeWeightStandard.levels, weightRatio);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,13 +370,8 @@ export class Standards {
|
|||||||
const getAllInterpolated = (): Standard[] => {
|
const getAllInterpolated = (): Standard[] => {
|
||||||
const metrics: Metrics = state as Metrics;
|
const metrics: Metrics = state as Metrics;
|
||||||
|
|
||||||
const weights = [... new Set(this
|
const interpolatedStandards: Standard[] = this.weightsFor(activity, metrics)
|
||||||
.byActivity(activity)
|
.map(weight => interpolateByAgeAndWeight({ ...metrics, weight }));
|
||||||
.byGender(metrics.gender)
|
|
||||||
.getAll().map((s) => s.metrics.weight))];
|
|
||||||
|
|
||||||
const interpolatedStandards: Standard[] = weights
|
|
||||||
.map((w) => interpolateByAgeAndWeight({ gender: metrics.gender, age: metrics.age, weight: w }));
|
|
||||||
|
|
||||||
return interpolatedStandards;
|
return interpolatedStandards;
|
||||||
};
|
};
|
||||||
@@ -426,6 +420,26 @@ export class Standards {
|
|||||||
return activities;
|
return activities;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public weightsFor(activity: Activity, metrics: Metrics) {
|
||||||
|
const { lower } = this
|
||||||
|
.byActivity(activity)
|
||||||
|
.byGender(metrics.gender)
|
||||||
|
.getNearest("age", metrics.age);
|
||||||
|
|
||||||
|
const weights = this
|
||||||
|
.byActivity(activity)
|
||||||
|
.byGender(metrics.gender)
|
||||||
|
.byAge(lower.metrics.age)
|
||||||
|
.getAll().map((s) => s.metrics.weight);
|
||||||
|
return weights;
|
||||||
|
}
|
||||||
|
|
||||||
|
public agesFor(activity: Activity, gender: Gender) {
|
||||||
|
const standardsByGender = this.byActivity(activity).byGender(gender).getAll();
|
||||||
|
const ages = [...new Set(standardsByGender.map(s => s.metrics.age))];
|
||||||
|
return ages;
|
||||||
|
}
|
||||||
|
|
||||||
private expandLevels(
|
private expandLevels(
|
||||||
levels: Levels,
|
levels: Levels,
|
||||||
i: number
|
i: number
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { type Metrics } from "$lib/services/calculator/main";
|
import { type Metrics } from "$lib/services/calculator/main";
|
||||||
|
import avgWeightDataRaw from "$lib/data/avg-weights.json"
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------
|
||||||
// DATA MODELS
|
// DATA MODELS
|
||||||
@@ -47,8 +48,9 @@ export const minToMs = (min: number) => min * 60000;
|
|||||||
export const secToMs = (sec: number) => sec * 1000;
|
export const secToMs = (sec: number) => sec * 1000;
|
||||||
export const msToMin = (ms: number) => ms / 60000;
|
export const msToMin = (ms: number) => ms / 60000;
|
||||||
|
|
||||||
export const feetToCm = (feet: number) => feet * 30.48;
|
export const ftToCm = (ft: number) => ft * 30.48;
|
||||||
export const inchesToCm = (inches: number) => inches * 2.54;
|
export const inToCm = (inches: number) => inches * 2.54;
|
||||||
|
export const cmToIn = (cm: number) => cm / 2.54;
|
||||||
|
|
||||||
export const msToTime = (ms: number, includeMs = false): string => {
|
export const msToTime = (ms: number, includeMs = false): string => {
|
||||||
const minutes = Math.floor(ms / 60000);
|
const minutes = Math.floor(ms / 60000);
|
||||||
@@ -64,4 +66,65 @@ export const msToTime = (ms: number, includeMs = false): string => {
|
|||||||
|
|
||||||
export const range = (length: number) => Array.from({ length: length }, (_, i) => i + 1);
|
export const range = (length: number) => Array.from({ length: length }, (_, i) => i + 1);
|
||||||
|
|
||||||
|
// export const getAvgWeight = (gender: Gender, age: number) => {
|
||||||
|
// type AvgWeightData = {
|
||||||
|
// metadata: {
|
||||||
|
// unit: StandardUnit
|
||||||
|
// },
|
||||||
|
// weights: Metrics[]
|
||||||
|
// }
|
||||||
|
// let avgWeightData = avgWeightDataRaw as AvgWeightData;
|
||||||
|
//
|
||||||
|
// const avgWeights = avgWeightData.weights.filter(a => a.gender === gender);
|
||||||
|
//
|
||||||
|
// let lower = avgWeights[0]; // best <= target
|
||||||
|
// let upper = avgWeights[0]; // best >= target
|
||||||
|
// let hasLower = false, hasUpper = false;
|
||||||
|
//
|
||||||
|
// for (const avg of avgWeights) {
|
||||||
|
// if (avg.age <= age) {
|
||||||
|
// if (!hasLower || avg.age > lower.age) { lower = avg; hasLower = true; }
|
||||||
|
// }
|
||||||
|
// if (avg.age >= age) {
|
||||||
|
// if (!hasUpper || avg.age < upper.age) { upper = avg; hasUpper = true; }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // clamp to ends if one side missing
|
||||||
|
// if (!hasLower) lower = avgWeights[0];
|
||||||
|
// if (!hasUpper) upper = avgWeights[avgWeights.length - 1];
|
||||||
|
//
|
||||||
|
// // lerp
|
||||||
|
// let ageRatio = upper === lower ? 1 : (age - lower.age) / (upper.age - lower.age);
|
||||||
|
// ageRatio = Math.max(0, Math.min(1, ageRatio));
|
||||||
|
// const interpolatedAvg = lower.weight + (upper.weight - lower.weight) * ageRatio;
|
||||||
|
//
|
||||||
|
// return interpolatedAvg;
|
||||||
|
// }
|
||||||
|
|
||||||
|
export const getAvgWeight = (gender: Gender, age: number) => {
|
||||||
|
type AvgWeightData = {
|
||||||
|
metadata: {
|
||||||
|
unit: StandardUnit
|
||||||
|
},
|
||||||
|
weights: Metrics[]
|
||||||
|
}
|
||||||
|
let avgWeightData = avgWeightDataRaw as AvgWeightData;
|
||||||
|
|
||||||
|
const avgWeights = avgWeightData.weights.filter(a => a.gender === gender);
|
||||||
|
if (!avgWeights.length) throw new Error("No average weights");
|
||||||
|
|
||||||
|
const closest = {
|
||||||
|
diff: Math.abs(avgWeights[0].age - age),
|
||||||
|
avg: avgWeights[0]
|
||||||
|
};
|
||||||
|
for (const avg of avgWeights) {
|
||||||
|
const diff = Math.abs(avg.age - age);
|
||||||
|
if (diff < closest.diff) {
|
||||||
|
closest.diff = diff;
|
||||||
|
closest.avg = avg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return closest.avg.weight;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Activity, feetToCm, Gender, inchesToCm, lbToKg, minToMs, secToMs, type ActivityPerformance, type Player } from "$lib/services/calculator/util";
|
import { Activity, feetToCm, Gender, getAvgWeight, inchesToCm, kgToLb, lbToKg, minToMs, secToMs, type ActivityPerformance, type Player } from "$lib/services/calculator/util";
|
||||||
|
|
||||||
const player: Player = {
|
const player: Player = {
|
||||||
metrics: {
|
metrics: {
|
||||||
@@ -39,7 +39,10 @@ const computedPerformances: ActivityPerformance[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const standards = new Stand();
|
for (let age = 15; age < 80; ++age) {
|
||||||
|
const avgWeightKG = getAvgWeight(Gender.Female, age);
|
||||||
|
console.log(`${age}: ${kgToLb(avgWeightKG).toFixed(1)}`);
|
||||||
|
}
|
||||||
|
|
||||||
// const newStandards: Standards = {
|
// const newStandards: Standards = {
|
||||||
// "Back Squat": [],
|
// "Back Squat": [],
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
import allStandardsRaw from "$lib/data/standards.json" assert { type: "json" };
|
import allStandardsRaw from "$lib/data/standards.json" assert { type: "json" };
|
||||||
import {
|
import {
|
||||||
Activity,
|
Activity,
|
||||||
|
cmToIn,
|
||||||
Gender,
|
Gender,
|
||||||
kgToLb,
|
kgToLb,
|
||||||
lbToKg,
|
lbToKg,
|
||||||
@@ -37,9 +38,10 @@
|
|||||||
const unit = allStandards.byActivity(activity).getMetadata().unit;
|
const unit = allStandards.byActivity(activity).getMetadata().unit;
|
||||||
switch (unit) {
|
switch (unit) {
|
||||||
case "ms":
|
case "ms":
|
||||||
return msToTime(rawValue);
|
return msToTime(rawValue, activity === Activity.ConeDrill);
|
||||||
case "cm":
|
case "cm": {
|
||||||
return String(Math.round(rawValue));
|
return String(Math.round(cmToIn(rawValue) * 10) / 10);
|
||||||
|
}
|
||||||
case "kg":
|
case "kg":
|
||||||
return String(Math.round(kgToLb(rawValue)));
|
return String(Math.round(kgToLb(rawValue)));
|
||||||
default:
|
default:
|
||||||
|
|||||||
Reference in New Issue
Block a user