From 8adb1c45666870eb8477a3126ee1b9df0d35d2af Mon Sep 17 00:00:00 2001 From: Dominic Ferrando Date: Sat, 8 Nov 2025 18:52:48 -0500 Subject: [PATCH] Implement stretching --- bun.lock | 81 +++++--- package.json | 13 +- src/lib/components/StandardsTable.svelte | 238 +++++++++++------------ src/lib/data/analyze.ts | 92 +++++++++ src/lib/services/calculator/main.ts | 106 +++++++++- src/lib/services/calculator/util.ts | 5 +- src/lib/test/scratch.ts | 213 -------------------- src/routes/calculator/+page.svelte | 20 ++ 8 files changed, 395 insertions(+), 373 deletions(-) create mode 100644 src/lib/data/analyze.ts delete mode 100644 src/lib/test/scratch.ts diff --git a/bun.lock b/bun.lock index 518a511..d8ab72a 100644 --- a/bun.lock +++ b/bun.lock @@ -5,21 +5,22 @@ "dependencies": { "@elysiajs/cors": "^1.4.0", "@elysiajs/eden": "^1.4.4", - "@tailwindcss/vite": "^4.1.14", - "daisyui": "^5.3.7", - "elysia": "^1.4.12", + "@tailwindcss/vite": "^4.1.16", + "daisyui": "^5.4.3", + "elysia": "^1.4.15", "memoirist": "^0.4.0", - "tailwindcss": "^4.1.14", + "ml-levenberg-marquardt": "^5.0.0", + "tailwindcss": "^4.1.16", }, "devDependencies": { - "@sveltejs/kit": "^2.47.1", + "@sveltejs/kit": "^2.48.4", "@sveltejs/vite-plugin-svelte": "^6.2.1", - "@types/bun": "^1.3.0", - "svelte": "^5.40.2", + "@types/bun": "^1.3.1", + "svelte": "^5.43.3", "svelte-adapter-bun": "^0.5.2", "svelte-check": "^4.3.3", "typescript": "^5.9.3", - "vite": "^7.1.10", + "vite": "^7.1.12", }, }, }, @@ -150,41 +151,41 @@ "@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@5.0.1", "", { "dependencies": { "debug": "^4.4.1" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^6.0.0-next.0", "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA=="], - "@tailwindcss/node": ["@tailwindcss/node@4.1.16", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.19", "source-map-js": "^1.2.1", "tailwindcss": "4.1.16" } }, "sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw=="], + "@tailwindcss/node": ["@tailwindcss/node@4.1.17", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.1.17" } }, "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg=="], - "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.16", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.16", "@tailwindcss/oxide-darwin-arm64": "4.1.16", "@tailwindcss/oxide-darwin-x64": "4.1.16", "@tailwindcss/oxide-freebsd-x64": "4.1.16", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.16", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.16", "@tailwindcss/oxide-linux-arm64-musl": "4.1.16", "@tailwindcss/oxide-linux-x64-gnu": "4.1.16", "@tailwindcss/oxide-linux-x64-musl": "4.1.16", "@tailwindcss/oxide-wasm32-wasi": "4.1.16", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.16", "@tailwindcss/oxide-win32-x64-msvc": "4.1.16" } }, "sha512-2OSv52FRuhdlgyOQqgtQHuCgXnS8nFSYRp2tJ+4WZXKgTxqPy7SMSls8c3mPT5pkZ17SBToGM5LHEJBO7miEdg=="], + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.17", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.17", "@tailwindcss/oxide-darwin-arm64": "4.1.17", "@tailwindcss/oxide-darwin-x64": "4.1.17", "@tailwindcss/oxide-freebsd-x64": "4.1.17", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17", "@tailwindcss/oxide-linux-arm64-musl": "4.1.17", "@tailwindcss/oxide-linux-x64-gnu": "4.1.17", "@tailwindcss/oxide-linux-x64-musl": "4.1.17", "@tailwindcss/oxide-wasm32-wasi": "4.1.17", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17", "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" } }, "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA=="], - "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.16", "", { "os": "android", "cpu": "arm64" }, "sha512-8+ctzkjHgwDJ5caq9IqRSgsP70xhdhJvm+oueS/yhD5ixLhqTw9fSL1OurzMUhBwE5zK26FXLCz2f/RtkISqHA=="], + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.17", "", { "os": "android", "cpu": "arm64" }, "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ=="], - "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.16", "", { "os": "darwin", "cpu": "arm64" }, "sha512-C3oZy5042v2FOALBZtY0JTDnGNdS6w7DxL/odvSny17ORUnaRKhyTse8xYi3yKGyfnTUOdavRCdmc8QqJYwFKA=="], + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.17", "", { "os": "darwin", "cpu": "arm64" }, "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg=="], - "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.16", "", { "os": "darwin", "cpu": "x64" }, "sha512-vjrl/1Ub9+JwU6BP0emgipGjowzYZMjbWCDqwA2Z4vCa+HBSpP4v6U2ddejcHsolsYxwL5r4bPNoamlV0xDdLg=="], + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.17", "", { "os": "darwin", "cpu": "x64" }, "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog=="], - "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.16", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TSMpPYpQLm+aR1wW5rKuUuEruc/oOX3C7H0BTnPDn7W/eMw8W+MRMpiypKMkXZfwH8wqPIRKppuZoedTtNj2tg=="], + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.17", "", { "os": "freebsd", "cpu": "x64" }, "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g=="], - "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.16", "", { "os": "linux", "cpu": "arm" }, "sha512-p0GGfRg/w0sdsFKBjMYvvKIiKy/LNWLWgV/plR4lUgrsxFAoQBFrXkZ4C0w8IOXfslB9vHK/JGASWD2IefIpvw=="], + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17", "", { "os": "linux", "cpu": "arm" }, "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ=="], - "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-DoixyMmTNO19rwRPdqviTrG1rYzpxgyYJl8RgQvdAQUzxC1ToLRqtNJpU/ATURSKgIg6uerPw2feW0aS8SNr/w=="], + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ=="], - "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-H81UXMa9hJhWhaAUca6bU2wm5RRFpuHImrwXBUvPbYb+3jo32I9VIwpOX6hms0fPmA6f2pGVlybO6qU8pF4fzQ=="], + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg=="], - "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.16", "", { "os": "linux", "cpu": "x64" }, "sha512-ZGHQxDtFC2/ruo7t99Qo2TTIvOERULPl5l0K1g0oK6b5PGqjYMga+FcY1wIUnrUxY56h28FxybtDEla+ICOyew=="], + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.17", "", { "os": "linux", "cpu": "x64" }, "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ=="], - "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.16", "", { "os": "linux", "cpu": "x64" }, "sha512-Oi1tAaa0rcKf1Og9MzKeINZzMLPbhxvm7rno5/zuP1WYmpiG0bEHq4AcRUiG2165/WUzvxkW4XDYCscZWbTLZw=="], + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.17", "", { "os": "linux", "cpu": "x64" }, "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ=="], - "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.16", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.0.7", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-B01u/b8LteGRwucIBmCQ07FVXLzImWESAIMcUU6nvFt/tYsQ6IHz8DmZ5KtvmwxD+iTYBtM1xwoGXswnlu9v0Q=="], + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.17", "", { "dependencies": { "@emnapi/core": "^1.6.0", "@emnapi/runtime": "^1.6.0", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.0.7", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg=="], - "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.16", "", { "os": "win32", "cpu": "arm64" }, "sha512-zX+Q8sSkGj6HKRTMJXuPvOcP8XfYON24zJBRPlszcH1Np7xuHXhWn8qfFjIujVzvH3BHU+16jBXwgpl20i+v9A=="], + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.17", "", { "os": "win32", "cpu": "arm64" }, "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A=="], - "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.16", "", { "os": "win32", "cpu": "x64" }, "sha512-m5dDFJUEejbFqP+UXVstd4W/wnxA4F61q8SoL+mqTypId2T2ZpuxosNSgowiCnLp2+Z+rivdU0AqpfgiD7yCBg=="], + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.17", "", { "os": "win32", "cpu": "x64" }, "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw=="], - "@tailwindcss/vite": ["@tailwindcss/vite@4.1.16", "", { "dependencies": { "@tailwindcss/node": "4.1.16", "@tailwindcss/oxide": "4.1.16", "tailwindcss": "4.1.16" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-bbguNBcDxsRmi9nnlWJxhfDWamY3lmcyACHcdO1crxfzuLpOhHLLtEIN/nCbbAtj5rchUgQD17QVAKi1f7IsKg=="], + "@tailwindcss/vite": ["@tailwindcss/vite@4.1.17", "", { "dependencies": { "@tailwindcss/node": "4.1.17", "@tailwindcss/oxide": "4.1.17", "tailwindcss": "4.1.17" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-4+9w8ZHOiGnpcGI6z1TVVfWaX/koK7fKeSYF3qlYg2xpBtbteP2ddBxiarL+HVgfSJGeK5RIxRQmKm4rTJJAwA=="], "@tokenizer/inflate": ["@tokenizer/inflate@0.2.7", "", { "dependencies": { "debug": "^4.4.0", "fflate": "^0.8.2", "token-types": "^6.0.0" } }, "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg=="], "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="], - "@types/bun": ["@types/bun@1.3.1", "", { "dependencies": { "bun-types": "1.3.1" } }, "sha512-4jNMk2/K9YJtfqwoAa28c8wK+T7nvJFOjxI4h/7sORWcypRNxBpr+TPNaCfVWq70tLCJsqoFwcf0oI0JU/fvMQ=="], + "@types/bun": ["@types/bun@1.3.2", "", { "dependencies": { "bun-types": "1.3.2" } }, "sha512-t15P7k5UIgHKkxwnMNkJbWlh/617rkDGEdSsDbu+qNHTaz9SKf7aC8fiIlUdD5RPpH6GEkP0cK7WlvmrEBRtWg=="], "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="], @@ -200,7 +201,7 @@ "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], - "bun-types": ["bun-types@1.3.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="], + "bun-types": ["bun-types@1.3.2", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-i/Gln4tbzKNuxP70OWhJRZz1MRfvqExowP7U6JKoI8cntFrtxg7RJK3jvz7wQW54UuvNC8tbKHHri5fy74FVqg=="], "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], @@ -210,7 +211,7 @@ "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], - "daisyui": ["daisyui@5.4.3", "", {}, "sha512-dfDCJnN4utErGoWfElgdEE252FtfHV9Mxj5Dq1+JzUq3nVkluWdF3JYykP0Xy/y/yArnPXYztO1tLNCow3kjmg=="], + "daisyui": ["daisyui@5.4.7", "", {}, "sha512-2wYO61vTPCXk7xEBgnzLZAYoE0xS5IRLu/GSq0vORpB+cTrtubdx69NnA0loc0exvCY1s2fYL4lGZtFHe2ohNQ=="], "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], @@ -250,6 +251,8 @@ "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], + "is-any-array": ["is-any-array@2.0.1", "", {}, "sha512-UtilS7hLRu++wb/WBAw9bNuP1Eg04Ivn1vERJck8zJthEvXCBEBpGR/33u/xLKWEQf95803oalHrVDptcAvFdQ=="], + "is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="], "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], @@ -286,6 +289,16 @@ "memoirist": ["memoirist@0.4.0", "", {}, "sha512-zxTgA0mSYELa66DimuNQDvyLq36AwDlTuVRbnQtB+VuTcKWm5Qc4z3WkSpgsFWHNhexqkIooqpv4hdcqrX5Nmg=="], + "ml-array-max": ["ml-array-max@1.2.4", "", { "dependencies": { "is-any-array": "^2.0.0" } }, "sha512-BlEeg80jI0tW6WaPyGxf5Sa4sqvcyY6lbSn5Vcv44lp1I2GR6AWojfUvLnGTNsIXrZ8uqWmo8VcG1WpkI2ONMQ=="], + + "ml-array-min": ["ml-array-min@1.2.3", "", { "dependencies": { "is-any-array": "^2.0.0" } }, "sha512-VcZ5f3VZ1iihtrGvgfh/q0XlMobG6GQ8FsNyQXD3T+IlstDv85g8kfV0xUG1QPRO/t21aukaJowDzMTc7j5V6Q=="], + + "ml-array-rescale": ["ml-array-rescale@1.3.7", "", { "dependencies": { "is-any-array": "^2.0.0", "ml-array-max": "^1.2.4", "ml-array-min": "^1.2.3" } }, "sha512-48NGChTouvEo9KBctDfHC3udWnQKNKEWN0ziELvY3KG25GR5cA8K8wNVzracsqSW1QEkAXjTNx+ycgAv06/1mQ=="], + + "ml-levenberg-marquardt": ["ml-levenberg-marquardt@5.0.0", "", { "dependencies": { "is-any-array": "^2.0.1", "ml-matrix": "^6.12.1" } }, "sha512-vCFoO2DyYKGZFp+KxofN2cAvSc/3gufJHj/oaDHA2nV+NZH509fD7m35zQR2ZXA8fepxpVrwgCAQ0aAIsV2WtA=="], + + "ml-matrix": ["ml-matrix@6.12.1", "", { "dependencies": { "is-any-array": "^2.0.1", "ml-array-rescale": "^1.3.7" } }, "sha512-TJ+8eOFdp+INvzR4zAuwBQJznDUfktMtOB6g/hUcGh3rcyjxbz4Te57Pgri8Q9bhSQ7Zys4IYOGhFdnlgeB6Lw=="], + "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], @@ -316,13 +329,13 @@ "strtok3": ["strtok3@10.3.4", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg=="], - "svelte": ["svelte@5.43.3", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^2.1.0", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-kjkAjCk41mJfvJZG56XcJNOdJSke94JxtcX8zFzzz2vrt47E0LnoBzU6azIZ1aBxJgUep8qegAkguSf1GjxLXQ=="], + "svelte": ["svelte@5.43.5", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^2.1.0", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-HQoZArIewxQVNedseDsgMgnRSC4XOXczxXLF9rOJaPIJkg58INOPUiL8aEtzqZIXNSZJyw8NmqObwg/voajiHQ=="], "svelte-adapter-bun": ["svelte-adapter-bun@0.5.2", "", { "dependencies": { "tiny-glob": "^0.2.9" } }, "sha512-xEtFgaal6UgrCwwkSIcapO9kopoFNUYCYqyKCikdqxX9bz2TDYnrWQZ7qBnkunMxi1HOIERUCvTcebYGiarZLA=="], "svelte-check": ["svelte-check@4.3.3", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-RYP0bEwenDXzfv0P1sKAwjZSlaRyqBn0Fz1TVni58lqyEiqgwztTpmodJrGzP6ZT2aHl4MbTvWP6gbmQ3FOnBg=="], - "tailwindcss": ["tailwindcss@4.1.16", "", {}, "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA=="], + "tailwindcss": ["tailwindcss@4.1.17", "", {}, "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q=="], "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], @@ -340,15 +353,17 @@ "undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], - "vite": ["vite@7.1.12", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug=="], + "vite": ["vite@7.2.2", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ=="], "vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="], "zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="], - "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.5.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="], + "@tailwindcss/node/magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], - "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.5.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.7.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-pJdKGq/1iquWYtv1RRSljZklxHCOCAJFJrImO5ZLKPJVJlVUcs8yFwNQlqS0Lo8xT1VAXXTCZocF9n26FWEKsw=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.7.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], @@ -359,5 +374,9 @@ "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "elysia/cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="], + + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@emnapi/core": ["@emnapi/core@1.5.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="], + + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@emnapi/runtime": ["@emnapi/runtime@1.5.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ=="], } } diff --git a/package.json b/package.json index 60e504c..361c09a 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,12 @@ "devDependencies": { "@sveltejs/kit": "^2.48.4", "@sveltejs/vite-plugin-svelte": "^6.2.1", - "@types/bun": "^1.3.1", - "svelte": "^5.43.3", + "@types/bun": "^1.3.2", + "svelte": "^5.43.5", "svelte-adapter-bun": "^0.5.2", "svelte-check": "^4.3.3", "typescript": "^5.9.3", - "vite": "^7.1.12" + "vite": "^7.2.2" }, "scripts": { "dev": "vite dev", @@ -21,10 +21,11 @@ "dependencies": { "@elysiajs/cors": "^1.4.0", "@elysiajs/eden": "^1.4.4", - "@tailwindcss/vite": "^4.1.16", - "daisyui": "^5.4.3", + "@tailwindcss/vite": "^4.1.17", + "daisyui": "^5.4.7", "elysia": "^1.4.15", "memoirist": "^0.4.0", - "tailwindcss": "^4.1.16" + "ml-levenberg-marquardt": "^5.0.0", + "tailwindcss": "^4.1.17" } } diff --git a/src/lib/components/StandardsTable.svelte b/src/lib/components/StandardsTable.svelte index 09ee9c8..8280e87 100644 --- a/src/lib/components/StandardsTable.svelte +++ b/src/lib/components/StandardsTable.svelte @@ -1,131 +1,131 @@
-
-
-

- {#if selected.cfg.activity[selected.activity].enableGeneration && allStandards - .byActivity(selected.activity) - .getMetadata().generators.length} - (Generated data: {allStandards - .byActivity(selected.activity) - .getMetadata() - .generators.map((g) => g.metric) - .join(", ")}) - {:else} - (Generated data: NONE) - {/if} -

-
-
+
+
+

+ {#if selected.cfg.activity[selected.activity].enableGeneration && allStandards + .byActivity(selected.activity) + .getMetadata().generators.length} + (Generated data: {allStandards + .byActivity(selected.activity) + .getMetadata() + .generators.map((g) => g.metric) + .join(", ")}) + {:else} + (Generated data: NONE) + {/if} +

+
+
- {#if !selected.metrics.age} - {#each allStandards.agesFor(selected.activity, selected.metrics.gender) as age} - {@render standardsTable(selected.activity, { - ...selected.metrics, - age, - })} - {/each} - {:else} - {@render standardsTable(selected.activity, selected.metrics)} - {/if} + {#if !selected.metrics.age} + {#each allStandards.agesFor(selected.activity, selected.metrics.gender) as age} + {@render standardsTable(selected.activity, { + ...selected.metrics, + age, + })} + {/each} + {:else} + {@render standardsTable(selected.activity, selected.metrics)} + {/if}
{#snippet standardsTable(activity: Activity, metrics: Metrics)} - {#snippet standardsTableRow(standard: Standard)} - - - {standard.metrics.weight - ? parseFloat(kgToLb(standard.metrics.weight).toFixed(2)) - : "None"} - {#each range(selected.cfg.global.maxLevel) as lvl} - {getLvlValue(activity, standard, lvl)} - {/each} - - {/snippet} -
-

{metrics.age}

-
- - - - - {#each range(selected.cfg.global.maxLevel) as lvl} - - {/each} - - - - {#if metrics.weight} - {@const standard = allStandards - .byActivity(activity) - .byGender(metrics.gender) - .byAge(metrics.age) - .byWeight(metrics.weight) - .getOneInterpolated()} - {@render standardsTableRow(standard)} - {:else} - {@const standards = allStandards - .byActivity(activity) - .byGender(metrics.gender) - .byAge(metrics.age) - .getAllInterpolated({ normalizeForLb: true })} - {#each standards as standard} - {@render standardsTableRow(standard)} - {/each} - {/if} - -
Body weight{lvl}
-
-
+ {#snippet standardsTableRow(standard: Standard)} + + + {standard.metrics.weight + ? parseFloat(kgToLb(standard.metrics.weight).toFixed(2)) + : "None"} + {#each range(selected.cfg.global.maxLevel).map((i) => ++i) as lvl} + {getLvlValue(activity, standard, lvl)} + {/each} + + {/snippet} +
+

{metrics.age}

+
+ + + + + {#each range(selected.cfg.global.maxLevel).map((i) => ++i) as lvl} + + {/each} + + + + {#if metrics.weight} + {@const standard = allStandards + .byActivity(activity) + .byGender(metrics.gender) + .byAge(metrics.age) + .byWeight(metrics.weight) + .getOneInterpolated()} + {@render standardsTableRow(standard)} + {:else} + {@const standards = allStandards + .byActivity(activity) + .byGender(metrics.gender) + .byAge(metrics.age) + .getAllInterpolated({ normalizeForLb: true })} + {#each standards as standard} + {@render standardsTableRow(standard)} + {/each} + {/if} + +
Body weight{lvl}
+
+
{/snippet} diff --git a/src/lib/data/analyze.ts b/src/lib/data/analyze.ts new file mode 100644 index 0000000..ae7a1af --- /dev/null +++ b/src/lib/data/analyze.ts @@ -0,0 +1,92 @@ +import { + type ActivityStandards, + type Levels, +} from "$lib/services/calculator/main"; +import { Activity, clamp, Gender, range } from "$lib/services/calculator/util"; +import allStandards from "./standards.json"; +import { levenbergMarquardt as LM } from "ml-levenberg-marquardt"; + +const activityStandards = allStandards as ActivityStandards; + +for (const activity of Object.values(Activity)) { + const DATASET_MAX_LEVEL = 5; + const NEW_LEVEL_COUNT = 2; + const RATIO_CLAMP = 1.4; + + function expDecayModel([A, B, C]: number[]) { + return (i: number) => A * Math.exp(-B * i) + C; + } + + for (const gender of Object.values(Gender)) { + const standardsByGender = activityStandards[activity].standards.filter( + (s) => s["metrics"].gender === gender, + ); + const ages = [...new Set(standardsByGender.map((s) => s.metrics.age))]; + for (const age of ages) { + const standards = standardsByGender.filter( + (s) => s.metrics.age === age, + ); + + const data = { + x: [] as number[], + y: [] as number[], + }; + + const isIncreasing = + standards[0].levels["2"] > standards[0].levels["1"]; + + for (const i of range(DATASET_MAX_LEVEL).slice(0, -1)) { + const level = i + 1; + const progressionRatios: number[] = []; + for (const standard of standards) { + const curr = standard.levels[level]; + const next = standard.levels[level]; + progressionRatios.push( + isIncreasing ? next / curr : curr / next, + ); + } + + const sum = progressionRatios.reduce((p, c) => p + c, 0); + const progressionRatioAvg = sum / progressionRatios.length; + + data.x.push(i); + data.y.push(progressionRatioAvg); + } + + const fittedParams = LM(data, expDecayModel, { + initialValues: [0.4, 0.5, 1.1], + minValues: [0.0, 0.0, 1.02], + maxValues: [1.0, 2.0, 1.25], + maxIterations: 200, + }); + const getDecayRatio = (i: number) => + clamp( + expDecayModel(fittedParams.parameterValues)(i), + 1, + RATIO_CLAMP, + ); + + // UPDATE THE DATA + for (const standard of standards) { + const newLevels: Levels = {}; + + let prev = standard.levels["1"]; + for (const i of range(NEW_LEVEL_COUNT)) { + const level = i + 1; + const ratio = getDecayRatio(level - NEW_LEVEL_COUNT); + prev = isIncreasing ? prev / ratio : prev * ratio; + newLevels[level] = Math.round(prev); + } + + const oldLevels = standard.levels as Levels; + for (const i of range(DATASET_MAX_LEVEL)) { + const level = i + 1; + newLevels[level + NEW_LEVEL_COUNT] = oldLevels[level]; + } + standard.levels = newLevels; + console.log(newLevels); + } + } + } +} +// Bun.write("./newStandards.json", JSON.stringify(allStandards, null, 2)); diff --git a/src/lib/services/calculator/main.ts b/src/lib/services/calculator/main.ts index dbb6673..5f09ef0 100644 --- a/src/lib/services/calculator/main.ts +++ b/src/lib/services/calculator/main.ts @@ -1,14 +1,17 @@ import { Activity, Attribute, + clamp, Gender, getAvgWeight, kgToLb, lbToKg, + range, type ActivityPerformance, type Player, type StandardUnit, } from "./util"; +import { levenbergMarquardt as LM } from "ml-levenberg-marquardt"; // SOURCES // Squat, Bench, Dead Lift: @@ -217,6 +220,7 @@ export type StandardsConfig = { enableGeneration: boolean; difficultyModifier: number; peakAge: number; + stretch: number; } >; }; @@ -242,6 +246,7 @@ export class Standards { difficultyModifier: activity === Activity.BroadJump ? 0.05 : 0, peakAge: 27, + stretch: 0, }, ]), ) as Record; @@ -255,9 +260,104 @@ export class Standards { // prepare data this.activityStandards = structuredClone(activityStandards); - for (const activity of Object.keys( - this.activityStandards, - ) as Activity[]) { + + // STRETCH + for (const activity of Object.values(Activity)) { + const BASE_MAX_LEVEL = 5; + const newLevelCount = Math.max( + Math.round(this.cfg.activity[activity].stretch), + 0, + ); + + function expDecayModel([A, B, C]: number[]) { + return (i: number) => A * Math.exp(-B * i) + C; + } + + for (const gender of Object.values(Gender)) { + const standardsByGender = this.activityStandards[ + activity + ].standards.filter((s) => s["metrics"].gender === gender); + const ages = [ + ...new Set(standardsByGender.map((s) => s.metrics.age)), + ]; + for (const age of ages) { + const standards = standardsByGender.filter( + (s) => s.metrics.age === age, + ); + + const data = { + x: [] as number[], + y: [] as number[], + }; + + const isIncreasing = + standards[0].levels["2"] > standards[0].levels["1"]; + + for (const i of range(BASE_MAX_LEVEL).slice(0, -1)) { + const level = i + 1; + const progressionRatios: number[] = []; + for (const standard of standards) { + const curr = standard.levels[level]; + const next = standard.levels[level + 1]; + progressionRatios.push( + isIncreasing ? next / curr : curr / next, + ); + } + + const sum = progressionRatios.reduce( + (p, c) => p + c, + 0, + ); + const progressionRatioAvg = + sum / progressionRatios.length; + + data.x.push(i); + data.y.push(progressionRatioAvg); + } + + const fittedParams = LM(data, expDecayModel, { + initialValues: [0.4, 0.5, 1.1], + minValues: [0.0, 0.0, 1.02], + maxValues: [1.0, 2.0, 1.2], + maxIterations: 200, + }); + const getDecayRatio = (i: number) => + expDecayModel(fittedParams.parameterValues)(i); + + // UPDATE THE DATA + for (const standard of standards) { + const newLevels: Levels = {}; + + let prev = standard.levels["1"]; + for (const i of range(newLevelCount).reverse()) { + const level = i + 1; + const ratio = getDecayRatio(-i); + prev = isIncreasing ? prev / ratio : prev * ratio; + newLevels[level] = Math.round(prev); + } + + const oldLevels = standard.levels as Levels; + for (const i of range(BASE_MAX_LEVEL)) { + const level = i + 1; + newLevels[level + newLevelCount] = oldLevels[level]; + } + + prev = standard.levels[BASE_MAX_LEVEL]; + for (const i of range(newLevelCount)) { + const level = + BASE_MAX_LEVEL + newLevelCount + (i + 1); + const ratio = getDecayRatio(level - 1); + prev = isIncreasing ? prev * ratio : prev / ratio; + newLevels[level] = Math.round(prev); + } + standard.levels = newLevels; + } + } + } + } + + // EXPAND, COMPRESS, SKEW + for (const activity of Object.values(Activity)) { for (const standard of this.activityStandards[activity].standards) { // expand let i = 1; diff --git a/src/lib/services/calculator/util.ts b/src/lib/services/calculator/util.ts index 72f00f8..1828b7c 100644 --- a/src/lib/services/calculator/util.ts +++ b/src/lib/services/calculator/util.ts @@ -66,7 +66,10 @@ export const msToTime = (ms: number, includeMs = false): string => { }; export const range = (length: number) => - Array.from({ length: length }, (_, i) => i + 1); + Array.from({ length: length }, (_, i) => i); + +export const clamp = (x: number, lo: number, hi: number) => + Math.max(lo, Math.min(hi, x)); export const getAvgWeight = (gender: Gender, age: number) => { type AvgWeightData = { diff --git a/src/lib/test/scratch.ts b/src/lib/test/scratch.ts deleted file mode 100644 index e072977..0000000 --- a/src/lib/test/scratch.ts +++ /dev/null @@ -1,213 +0,0 @@ -import { - Activity, - ftToCm, - Gender, - getAvgWeight, - inToCm, - kgToLb, - lbToKg, - minToMs, - secToMs, - type ActivityPerformance, - type Player, -} from "$lib/services/calculator/util"; -import PrintfulService from "$lib/services/commerce/printful"; -import SyncService from "$lib/services/commerce/sync"; -import { FetchError, type DeepPartial } from "$lib/services/commerce/util/misc"; -import type { Printful } from "$lib/services/commerce/util/types"; -import WebflowService from "$lib/services/commerce/webflow"; - -const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); - -const player: Player = { - metrics: { - age: 25, - weight: lbToKg(190), - gender: Gender.Male, - }, -}; - -const computedPerformances: ActivityPerformance[] = [ - // STRENGTH - { - activity: Activity.BenchPress, - performance: lbToKg(225), - }, - { - activity: Activity.Deadlift, - performance: lbToKg(270), - }, - { - activity: Activity.BackSquat, - performance: lbToKg(190), - }, - // POWER - { - activity: Activity.BroadJump, - performance: ftToCm(105) + inToCm(5), - }, - // ENDURANCE - { - activity: Activity.Run, - performance: minToMs(7) + secToMs(15), - }, - // AGILITY - { - activity: Activity.ConeDrill, - performance: secToMs(9), - }, -]; - -const pOrders = await PrintfulService.Orders.getAll(); -console.log(pOrders); - -// wProduct.product.fieldData.name = "Rogue Title Hoodie [White] / XS"; -// -// WebflowService.Products.update("68e66dc4633e577c91eda727",); - -// const printfulProducts = await PrintfulService.Products.getAll(); -// const webflowProducts = await WebflowService.Products.getAll(); -// -// for (const printfulProduct of printfulProducts) { -// if (!printfulProduct.name.includes("T-Shirt") || printfulProduct.name.includes("[White]") || printfulProduct.name.includes("[Black]")) { -// continue; -// } -// -// console.log(printfulProduct.name); -// -// const fullPrintfulProduct = await PrintfulService.Products.get(printfulProduct.id); -// if (!fullPrintfulProduct) { -// console.log("Could not get full product, skipped"); -// continue; -// } -// -// const existingWebflowProduct = webflowProducts.find(p => fullPrintfulProduct.sync_product.name.includes(p.product.fieldData.name)) -// if (!existingWebflowProduct) { -// console.log("Could not get associated webflow product, skipped"); -// continue; -// } -// -// const newPrintfulVariants: DeepPartial[] = []; -// for (const printfulVariant of fullPrintfulProduct.sync_variants) { -// console.log(printfulVariant.name); -// const associatedWebflowSku = existingWebflowProduct.skus -// .find(sku => sku.fieldData["sku-values"]?.["color"] === SyncService.findColorInProductName(printfulVariant.name) && -// sku.fieldData["sku-values"]?.["size"] === printfulVariant.size); -// if (associatedWebflowSku) { -// console.log('Found associated webflow sku: ' + associatedWebflowSku.fieldData.name); -// newPrintfulVariants.push({ -// "id": printfulVariant.id, // printful variant id -// "external_id": String(associatedWebflowSku.id), // webflow variant id -// }); -// } -// else { -// console.log("Could not get associated webflow sku"); -// } -// } -// -// if (!newPrintfulVariants.length) { -// console.log("No new printful variants, skipped"); -// continue; -// } -// -// await sleep(3000); -// try { -// await PrintfulService.Products.update(printfulProduct.id, { -// "sync_product": { -// "id": printfulProduct.id, -// "external_id": existingWebflowProduct.product.id + "-" + SyncService.findColorInProductName(printfulProduct.name) -// }, -// "sync_variants": newPrintfulVariants -// }); -// } catch (err) { -// if (err instanceof FetchError) { -// console.error(err.message, err.payload); -// } -// } -// } -// console.log("DONE"); - -// await WebflowService.Products.update(wProduct?.product.id!, wProduct!); -// -// const sku = { -// "fieldData": { -// "name": "Test", -// "slug": "test-test", -// "sku-values": { -// "color": "black", -// "size": "L", -// }, -// "price": { -// "value": +0 * 100, -// "unit": "USD", -// }, -// "main-image": "https://cdn.prod.website-files.com/675a3fbe966e58ea9aa710e4/68d09d3d8e1e8bad26a9cf07_knight-title-tank-white-10.png" -// } -// } -// -// -// -// const skuId = "68d080a5d90e8263e52242e9"; -// -// const colors = ["Black", "White", "Red"]; - -// const sku = product.skus[0]; -// -// sku.fieldData["more-images"] = [ -// { -// "url": "https://website-media.bladeandbrawn.com/product-images/henry-iv-flag-black-10.png", -// }, -// { -// "url": "https://website-media.bladeandbrawn.com/product-images/henry-iv-flag-black-20.png", -// }, -// ]; - -// console.log("Fetching..."); -// const res = await fetch(`${Webflow.API_SITES_URL}/products/${productId}/skus/${skuId}`, { -// method: "PATCH", -// headers: { -// "Content-Type": "application/json", -// "Authorization": `bearer ${Bun.env.WEBFLOW_AUTH}` -// }, -// body: JSON.stringify({ -// "sku": sku -// }) -// }); -// console.log(res); -// -// const productAfter = (await Webflow.Products.getAll())[0]; -// -// console.log(productAfter.skus[0].fieldData["more-images"]); - -// const newStandards: Standards = { -// "Back Squat": [], -// "Deadlift": [], -// "Bench Press": [], -// "Run": [], -// "Dash": [], -// "Treadmill Dash": [], -// "Broad Jump": [], -// "Cone Drill": [], -// }; -// -// for (const item of standards) { -// if (!newStandards[item.activityType]) -// continue; -// -// newStandards[item.activityType].push({ -// metrics: { -// age: item.age, -// weight: item.bodyWeight, -// gender: item.gender -// }, -// levels: { -// physicallyActive: item.physicallyActive, -// beginner: item.beginner, -// intermediate: item.intermediate, -// advanced: item.advanced, -// elite: item.elite -// } -// }); -// } -// -// Bun.write("./newStandards.json", JSON.stringify(newStandards, null, 2)); diff --git a/src/routes/calculator/+page.svelte b/src/routes/calculator/+page.svelte index 7fbd538..4828642 100644 --- a/src/routes/calculator/+page.svelte +++ b/src/routes/calculator/+page.svelte @@ -50,6 +50,9 @@ peakAge: String( defaultStandards.cfg.activity[activity].peakAge, ), + stretch: String( + defaultStandards.cfg.activity[activity].stretch, + ), }, ]; }), @@ -80,6 +83,7 @@ difficultyModifier: +input.cfg.activity[activity].difficultyModifier, peakAge: +input.cfg.activity[activity].peakAge, + stretch: +input.cfg.activity[activity].stretch, }, ]), ), @@ -143,6 +147,22 @@ bind:value={input.cfg.global.maxLevel} /> + +