Spaces:
Sleeping
Sleeping
Improve RunRate Lab bat rig and swing posture
Browse files
app.py
CHANGED
|
@@ -2200,18 +2200,41 @@ GAME_HTML = r"""
|
|
| 2200 |
ctx.stroke();
|
| 2201 |
}
|
| 2202 |
|
| 2203 |
-
function
|
| 2204 |
-
|
| 2205 |
-
|
| 2206 |
-
|
| 2207 |
-
|
| 2208 |
-
|
| 2209 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2210 |
|
| 2211 |
ctx.save();
|
| 2212 |
ctx.translate(anchor.x, anchor.y);
|
| 2213 |
ctx.rotate(angle);
|
| 2214 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2215 |
ctx.strokeStyle = "#4a3417";
|
| 2216 |
ctx.lineWidth = handleW;
|
| 2217 |
ctx.lineCap = "round";
|
|
@@ -2223,42 +2246,59 @@ GAME_HTML = r"""
|
|
| 2223 |
ctx.strokeStyle = "#f8fafc";
|
| 2224 |
ctx.lineWidth = 2 * scale;
|
| 2225 |
for (let i = 0; i < 3; i += 1) {
|
| 2226 |
-
const gx = (
|
| 2227 |
ctx.beginPath();
|
| 2228 |
ctx.moveTo(gx, -6 * scale);
|
| 2229 |
ctx.lineTo(gx, 6 * scale);
|
| 2230 |
ctx.stroke();
|
| 2231 |
}
|
| 2232 |
|
| 2233 |
-
|
| 2234 |
-
|
| 2235 |
-
ctx.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2236 |
ctx.strokeStyle = "#76551e";
|
| 2237 |
ctx.lineWidth = 3 * scale;
|
| 2238 |
ctx.beginPath();
|
| 2239 |
-
ctx.moveTo(handleLen, -bladeTopW * 0.
|
| 2240 |
-
ctx.quadraticCurveTo(
|
| 2241 |
-
ctx.lineTo(
|
| 2242 |
-
ctx.quadraticCurveTo(
|
| 2243 |
-
ctx.
|
| 2244 |
-
ctx.
|
|
|
|
| 2245 |
ctx.closePath();
|
| 2246 |
-
ctx.fill();
|
| 2247 |
ctx.stroke();
|
| 2248 |
|
| 2249 |
ctx.strokeStyle = "rgba(255,255,255,0.32)";
|
| 2250 |
ctx.lineWidth = 2 * scale;
|
| 2251 |
ctx.beginPath();
|
| 2252 |
-
ctx.moveTo(
|
| 2253 |
-
ctx.lineTo(
|
| 2254 |
ctx.stroke();
|
| 2255 |
|
| 2256 |
-
ctx.strokeStyle = "rgba(118,85,30,0.
|
| 2257 |
ctx.lineWidth = 1.5 * scale;
|
| 2258 |
ctx.beginPath();
|
| 2259 |
-
ctx.moveTo(
|
| 2260 |
-
ctx.lineTo(
|
| 2261 |
ctx.stroke();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2262 |
ctx.restore();
|
| 2263 |
}
|
| 2264 |
|
|
@@ -2269,28 +2309,88 @@ GAME_HTML = r"""
|
|
| 2269 |
const x = base.x + shake;
|
| 2270 |
const y = base.y + 18 * scale;
|
| 2271 |
const progress = clamp((now - state.swingStart) / 420, 0, 1);
|
| 2272 |
-
const
|
| 2273 |
-
let batAngle = -1.
|
| 2274 |
-
let
|
|
|
|
|
|
|
| 2275 |
if (state.mode === "bowling" && state.charging) {
|
| 2276 |
-
|
| 2277 |
-
|
|
|
|
|
|
|
|
|
|
| 2278 |
} else if (state.swingState === "contact") {
|
| 2279 |
-
|
| 2280 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2281 |
} else if (state.swingState === "miss") {
|
| 2282 |
-
batAngle = -1.
|
| 2283 |
-
|
| 2284 |
-
|
| 2285 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2286 |
}
|
| 2287 |
|
| 2288 |
const hipY = y - 82 * scale;
|
| 2289 |
const shoulderY = y - 178 * scale;
|
| 2290 |
const headY = y - 232 * scale;
|
| 2291 |
const footY = y + 18 * scale;
|
| 2292 |
-
const
|
| 2293 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2294 |
|
| 2295 |
ctx.save();
|
| 2296 |
ctx.fillStyle = "rgba(0,0,0,0.26)";
|
|
@@ -2307,41 +2407,37 @@ GAME_HTML = r"""
|
|
| 2307 |
ctx.fill();
|
| 2308 |
|
| 2309 |
ctx.fillStyle = "#f8fafc";
|
| 2310 |
-
roundedRect(x - 54 * scale, shoulderY, 108 * scale, 124 * scale, 26 * scale);
|
| 2311 |
ctx.fill();
|
| 2312 |
ctx.fillStyle = "#2563eb";
|
| 2313 |
-
roundedRect(x - 68 * scale, shoulderY + 16 * scale, 136 * scale, 36 * scale, 15 * scale);
|
| 2314 |
ctx.fill();
|
| 2315 |
ctx.fillStyle = "#0f172a";
|
| 2316 |
-
roundedRect(x - 42 * scale, shoulderY + 40 * scale, 84 * scale, 80 * scale, 13 * scale);
|
| 2317 |
ctx.fill();
|
| 2318 |
|
| 2319 |
-
drawLimb(x
|
| 2320 |
-
drawLimb(x
|
| 2321 |
-
drawLimb(
|
|
|
|
| 2322 |
|
| 2323 |
-
drawBat(
|
| 2324 |
|
| 2325 |
-
|
| 2326 |
-
|
| 2327 |
-
ctx.arc(leftGlove.x, leftGlove.y, 13 * scale, 0, Math.PI * 2);
|
| 2328 |
-
ctx.fill();
|
| 2329 |
-
ctx.beginPath();
|
| 2330 |
-
ctx.arc(rightGlove.x, rightGlove.y, 13 * scale, 0, Math.PI * 2);
|
| 2331 |
-
ctx.fill();
|
| 2332 |
|
| 2333 |
ctx.fillStyle = "#f8fafc";
|
| 2334 |
ctx.beginPath();
|
| 2335 |
-
ctx.arc(x, headY, 32 * scale, 0, Math.PI * 2);
|
| 2336 |
ctx.fill();
|
| 2337 |
ctx.fillStyle = "#0f172a";
|
| 2338 |
ctx.beginPath();
|
| 2339 |
-
ctx.arc(x, headY - 9 * scale, 34 * scale, Math.PI, Math.PI * 2);
|
| 2340 |
ctx.fill();
|
| 2341 |
ctx.fillStyle = "rgba(15,23,42,0.95)";
|
| 2342 |
-
ctx.fillRect(x - 36 * scale, headY - 9 * scale, 72 * scale, 9 * scale);
|
| 2343 |
ctx.fillStyle = "rgba(56,189,248,0.58)";
|
| 2344 |
-
ctx.fillRect(x - 28 * scale, headY + 10 * scale, 56 * scale, 6 * scale);
|
| 2345 |
ctx.restore();
|
| 2346 |
}
|
| 2347 |
|
|
|
|
| 2200 |
ctx.stroke();
|
| 2201 |
}
|
| 2202 |
|
| 2203 |
+
function drawGlove(x, y, angle, scale, flip = 1) {
|
| 2204 |
+
ctx.save();
|
| 2205 |
+
ctx.translate(x, y);
|
| 2206 |
+
ctx.rotate(angle + flip * 0.10);
|
| 2207 |
+
ctx.fillStyle = "#f8fafc";
|
| 2208 |
+
roundedRect(-12 * scale, -8 * scale, 24 * scale, 16 * scale, 5 * scale);
|
| 2209 |
+
ctx.fill();
|
| 2210 |
+
ctx.fillStyle = "#d8ae55";
|
| 2211 |
+
roundedRect(-13 * scale, 3 * scale, 12 * scale, 9 * scale, 4 * scale);
|
| 2212 |
+
ctx.fill();
|
| 2213 |
+
ctx.fillStyle = "rgba(15,23,42,0.18)";
|
| 2214 |
+
ctx.fillRect(-4 * scale, -4 * scale, 12 * scale, 2 * scale);
|
| 2215 |
+
ctx.fillRect(-4 * scale, 0, 12 * scale, 2 * scale);
|
| 2216 |
+
ctx.restore();
|
| 2217 |
+
}
|
| 2218 |
+
|
| 2219 |
+
function drawBat(topHand, bottomHand, angle, scale) {
|
| 2220 |
+
const anchor = topHand;
|
| 2221 |
+
const handSpacing = 16 * scale;
|
| 2222 |
+
const handleLen = 56 * scale;
|
| 2223 |
+
const bladeLen = 126 * scale;
|
| 2224 |
+
const handleW = 6 * scale;
|
| 2225 |
+
const bladeTopW = 18 * scale;
|
| 2226 |
+
const bladeMidW = 28 * scale;
|
| 2227 |
+
const bladeBotW = 40 * scale;
|
| 2228 |
|
| 2229 |
ctx.save();
|
| 2230 |
ctx.translate(anchor.x, anchor.y);
|
| 2231 |
ctx.rotate(angle);
|
| 2232 |
|
| 2233 |
+
ctx.fillStyle = "rgba(0,0,0,0.22)";
|
| 2234 |
+
ctx.beginPath();
|
| 2235 |
+
ctx.ellipse(handleLen + bladeLen * 0.52, 14 * scale, bladeBotW * 0.55, 9 * scale, 0, 0, Math.PI * 2);
|
| 2236 |
+
ctx.fill();
|
| 2237 |
+
|
| 2238 |
ctx.strokeStyle = "#4a3417";
|
| 2239 |
ctx.lineWidth = handleW;
|
| 2240 |
ctx.lineCap = "round";
|
|
|
|
| 2246 |
ctx.strokeStyle = "#f8fafc";
|
| 2247 |
ctx.lineWidth = 2 * scale;
|
| 2248 |
for (let i = 0; i < 3; i += 1) {
|
| 2249 |
+
const gx = (11 + i * 11) * scale;
|
| 2250 |
ctx.beginPath();
|
| 2251 |
ctx.moveTo(gx, -6 * scale);
|
| 2252 |
ctx.lineTo(gx, 6 * scale);
|
| 2253 |
ctx.stroke();
|
| 2254 |
}
|
| 2255 |
|
| 2256 |
+
ctx.fillStyle = "#d9b368";
|
| 2257 |
+
ctx.beginPath();
|
| 2258 |
+
ctx.moveTo(handleLen - 2 * scale, -bladeTopW * 0.50);
|
| 2259 |
+
ctx.quadraticCurveTo(handleLen + 10 * scale, -bladeTopW * 0.78, handleLen + 22 * scale, -bladeMidW * 0.48);
|
| 2260 |
+
ctx.lineTo(handleLen + bladeLen * 0.68, -bladeBotW * 0.44);
|
| 2261 |
+
ctx.quadraticCurveTo(handleLen + bladeLen + 8 * scale, -bladeBotW * 0.12, handleLen + bladeLen, 0);
|
| 2262 |
+
ctx.quadraticCurveTo(handleLen + bladeLen + 8 * scale, bladeBotW * 0.12, handleLen + bladeLen * 0.68, bladeBotW * 0.44);
|
| 2263 |
+
ctx.lineTo(handleLen + 22 * scale, bladeMidW * 0.48);
|
| 2264 |
+
ctx.quadraticCurveTo(handleLen + 10 * scale, bladeTopW * 0.78, handleLen - 2 * scale, bladeTopW * 0.50);
|
| 2265 |
+
ctx.closePath();
|
| 2266 |
+
ctx.fill();
|
| 2267 |
+
|
| 2268 |
ctx.strokeStyle = "#76551e";
|
| 2269 |
ctx.lineWidth = 3 * scale;
|
| 2270 |
ctx.beginPath();
|
| 2271 |
+
ctx.moveTo(handleLen - 2 * scale, -bladeTopW * 0.50);
|
| 2272 |
+
ctx.quadraticCurveTo(handleLen + 10 * scale, -bladeTopW * 0.78, handleLen + 22 * scale, -bladeMidW * 0.48);
|
| 2273 |
+
ctx.lineTo(handleLen + bladeLen * 0.68, -bladeBotW * 0.44);
|
| 2274 |
+
ctx.quadraticCurveTo(handleLen + bladeLen + 8 * scale, -bladeBotW * 0.12, handleLen + bladeLen, 0);
|
| 2275 |
+
ctx.quadraticCurveTo(handleLen + bladeLen + 8 * scale, bladeBotW * 0.12, handleLen + bladeLen * 0.68, bladeBotW * 0.44);
|
| 2276 |
+
ctx.lineTo(handleLen + 22 * scale, bladeMidW * 0.48);
|
| 2277 |
+
ctx.quadraticCurveTo(handleLen + 10 * scale, bladeTopW * 0.78, handleLen - 2 * scale, bladeTopW * 0.50);
|
| 2278 |
ctx.closePath();
|
|
|
|
| 2279 |
ctx.stroke();
|
| 2280 |
|
| 2281 |
ctx.strokeStyle = "rgba(255,255,255,0.32)";
|
| 2282 |
ctx.lineWidth = 2 * scale;
|
| 2283 |
ctx.beginPath();
|
| 2284 |
+
ctx.moveTo(handleLen + 18 * scale, -4 * scale);
|
| 2285 |
+
ctx.lineTo(handleLen + bladeLen * 0.74, -11 * scale);
|
| 2286 |
ctx.stroke();
|
| 2287 |
|
| 2288 |
+
ctx.strokeStyle = "rgba(118,85,30,0.28)";
|
| 2289 |
ctx.lineWidth = 1.5 * scale;
|
| 2290 |
ctx.beginPath();
|
| 2291 |
+
ctx.moveTo(handleLen + 14 * scale, 0);
|
| 2292 |
+
ctx.lineTo(handleLen + bladeLen * 0.76, 0);
|
| 2293 |
ctx.stroke();
|
| 2294 |
+
|
| 2295 |
+
ctx.fillStyle = "#1f1600";
|
| 2296 |
+
ctx.beginPath();
|
| 2297 |
+
ctx.arc(handSpacing * 0.22, 0, 2.2 * scale, 0, Math.PI * 2);
|
| 2298 |
+
ctx.fill();
|
| 2299 |
+
ctx.beginPath();
|
| 2300 |
+
ctx.arc(handSpacing, 0, 2.2 * scale, 0, Math.PI * 2);
|
| 2301 |
+
ctx.fill();
|
| 2302 |
ctx.restore();
|
| 2303 |
}
|
| 2304 |
|
|
|
|
| 2309 |
const x = base.x + shake;
|
| 2310 |
const y = base.y + 18 * scale;
|
| 2311 |
const progress = clamp((now - state.swingStart) / 420, 0, 1);
|
| 2312 |
+
const easeOut = 1 - Math.pow(1 - progress, 3);
|
| 2313 |
+
let batAngle = -1.18;
|
| 2314 |
+
let handReach = 0;
|
| 2315 |
+
let handLift = 0;
|
| 2316 |
+
let torsoLean = 0;
|
| 2317 |
if (state.mode === "bowling" && state.charging) {
|
| 2318 |
+
const charge = clamp(state.power / 100, 0, 1);
|
| 2319 |
+
batAngle = -1.58 - charge * 0.34;
|
| 2320 |
+
handReach = -8 * charge;
|
| 2321 |
+
handLift = -18 * charge;
|
| 2322 |
+
torsoLean = -0.05;
|
| 2323 |
} else if (state.swingState === "contact") {
|
| 2324 |
+
if (state.selectedShot === "Loft") {
|
| 2325 |
+
batAngle = -1.72 + easeOut * 1.46;
|
| 2326 |
+
handReach = 26 * easeOut;
|
| 2327 |
+
handLift = -16 * easeOut;
|
| 2328 |
+
torsoLean = 0.14 * easeOut;
|
| 2329 |
+
} else if (state.selectedShot === "Cut / Pull") {
|
| 2330 |
+
batAngle = -1.66 + easeOut * 1.12;
|
| 2331 |
+
handReach = 22 * easeOut;
|
| 2332 |
+
handLift = -6 * easeOut;
|
| 2333 |
+
torsoLean = 0.08 * easeOut;
|
| 2334 |
+
} else if (state.selectedShot === "Defend") {
|
| 2335 |
+
batAngle = -1.46 + easeOut * 0.62;
|
| 2336 |
+
handReach = 8 * easeOut;
|
| 2337 |
+
handLift = 2 * easeOut;
|
| 2338 |
+
torsoLean = 0.03 * easeOut;
|
| 2339 |
+
} else {
|
| 2340 |
+
batAngle = -1.64 + easeOut * 0.96;
|
| 2341 |
+
handReach = 18 * easeOut;
|
| 2342 |
+
handLift = -2 * easeOut;
|
| 2343 |
+
torsoLean = 0.08 * easeOut;
|
| 2344 |
+
}
|
| 2345 |
} else if (state.swingState === "miss") {
|
| 2346 |
+
batAngle = -1.56 + easeOut * 0.72;
|
| 2347 |
+
handReach = 10 * easeOut;
|
| 2348 |
+
handLift = -4 * easeOut;
|
| 2349 |
+
torsoLean = 0.04 * easeOut;
|
| 2350 |
+
} else if (state.mode === "between" && state.lastInsight) {
|
| 2351 |
+
if (state.lastInsight.shot === "Loft") {
|
| 2352 |
+
batAngle = -0.32;
|
| 2353 |
+
handReach = 24;
|
| 2354 |
+
handLift = -12;
|
| 2355 |
+
torsoLean = 0.10;
|
| 2356 |
+
} else if (state.lastInsight.shot === "Defend") {
|
| 2357 |
+
batAngle = -0.86;
|
| 2358 |
+
handReach = 8;
|
| 2359 |
+
handLift = 2;
|
| 2360 |
+
torsoLean = 0.02;
|
| 2361 |
+
} else if (state.lastInsight.shot === "Cut / Pull") {
|
| 2362 |
+
batAngle = -0.58;
|
| 2363 |
+
handReach = 18;
|
| 2364 |
+
handLift = -2;
|
| 2365 |
+
torsoLean = 0.06;
|
| 2366 |
+
} else {
|
| 2367 |
+
batAngle = -0.70;
|
| 2368 |
+
handReach = 16;
|
| 2369 |
+
handLift = -2;
|
| 2370 |
+
torsoLean = 0.06;
|
| 2371 |
+
}
|
| 2372 |
}
|
| 2373 |
|
| 2374 |
const hipY = y - 82 * scale;
|
| 2375 |
const shoulderY = y - 178 * scale;
|
| 2376 |
const headY = y - 232 * scale;
|
| 2377 |
const footY = y + 18 * scale;
|
| 2378 |
+
const torsoShift = torsoLean * 34 * scale;
|
| 2379 |
+
const leftShoulder = { x: x - 48 * scale + torsoShift, y: shoulderY + 28 * scale };
|
| 2380 |
+
const rightShoulder = { x: x + 44 * scale + torsoShift, y: shoulderY + 28 * scale };
|
| 2381 |
+
const topHand = { x: x + (26 + handReach) * scale, y: shoulderY + (32 + handLift) * scale };
|
| 2382 |
+
const bottomHand = {
|
| 2383 |
+
x: topHand.x + Math.cos(batAngle) * 18 * scale,
|
| 2384 |
+
y: topHand.y + Math.sin(batAngle) * 18 * scale
|
| 2385 |
+
};
|
| 2386 |
+
const leftElbow = {
|
| 2387 |
+
x: (leftShoulder.x + topHand.x) / 2 + 4 * scale,
|
| 2388 |
+
y: (leftShoulder.y + topHand.y) / 2 + 7 * scale
|
| 2389 |
+
};
|
| 2390 |
+
const rightElbow = {
|
| 2391 |
+
x: (rightShoulder.x + bottomHand.x) / 2 + 16 * scale,
|
| 2392 |
+
y: (rightShoulder.y + bottomHand.y) / 2 + 10 * scale
|
| 2393 |
+
};
|
| 2394 |
|
| 2395 |
ctx.save();
|
| 2396 |
ctx.fillStyle = "rgba(0,0,0,0.26)";
|
|
|
|
| 2407 |
ctx.fill();
|
| 2408 |
|
| 2409 |
ctx.fillStyle = "#f8fafc";
|
| 2410 |
+
roundedRect(x - 54 * scale + torsoShift, shoulderY, 108 * scale, 124 * scale, 26 * scale);
|
| 2411 |
ctx.fill();
|
| 2412 |
ctx.fillStyle = "#2563eb";
|
| 2413 |
+
roundedRect(x - 68 * scale + torsoShift, shoulderY + 16 * scale, 136 * scale, 36 * scale, 15 * scale);
|
| 2414 |
ctx.fill();
|
| 2415 |
ctx.fillStyle = "#0f172a";
|
| 2416 |
+
roundedRect(x - 42 * scale + torsoShift, shoulderY + 40 * scale, 84 * scale, 80 * scale, 13 * scale);
|
| 2417 |
ctx.fill();
|
| 2418 |
|
| 2419 |
+
drawLimb(leftShoulder.x, leftShoulder.y, leftElbow.x, leftElbow.y, 15 * scale, "#f8fafc");
|
| 2420 |
+
drawLimb(leftElbow.x, leftElbow.y, topHand.x, topHand.y, 15 * scale, "#f8fafc");
|
| 2421 |
+
drawLimb(rightShoulder.x, rightShoulder.y, rightElbow.x, rightElbow.y, 15 * scale, "#f8fafc");
|
| 2422 |
+
drawLimb(rightElbow.x, rightElbow.y, bottomHand.x, bottomHand.y, 15 * scale, "#f8fafc");
|
| 2423 |
|
| 2424 |
+
drawBat(topHand, bottomHand, batAngle, scale);
|
| 2425 |
|
| 2426 |
+
drawGlove(topHand.x, topHand.y, batAngle - 0.08, scale, -1);
|
| 2427 |
+
drawGlove(bottomHand.x, bottomHand.y, batAngle + 0.02, scale, 1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2428 |
|
| 2429 |
ctx.fillStyle = "#f8fafc";
|
| 2430 |
ctx.beginPath();
|
| 2431 |
+
ctx.arc(x + torsoShift, headY, 32 * scale, 0, Math.PI * 2);
|
| 2432 |
ctx.fill();
|
| 2433 |
ctx.fillStyle = "#0f172a";
|
| 2434 |
ctx.beginPath();
|
| 2435 |
+
ctx.arc(x + torsoShift, headY - 9 * scale, 34 * scale, Math.PI, Math.PI * 2);
|
| 2436 |
ctx.fill();
|
| 2437 |
ctx.fillStyle = "rgba(15,23,42,0.95)";
|
| 2438 |
+
ctx.fillRect(x - 36 * scale + torsoShift, headY - 9 * scale, 72 * scale, 9 * scale);
|
| 2439 |
ctx.fillStyle = "rgba(56,189,248,0.58)";
|
| 2440 |
+
ctx.fillRect(x - 28 * scale + torsoShift, headY + 10 * scale, 56 * scale, 6 * scale);
|
| 2441 |
ctx.restore();
|
| 2442 |
}
|
| 2443 |
|