Feature: tank distribution scale on Tankopedia stats

This commit is contained in:
Mark Greene committed 2026-10-20 07:18:01 +13:00
1 parent 2e10052732
commit a0c54b50fc
2 files changed
+97 -8

No files matched your search

@@ -1,5 +1,5 @@
import { createDefaultSkills } from "@blitzkit/core";
import { Flex, Progress, Text } from "@radix-ui/themes";
import { Box, Flex, Progress, Text } from "@radix-ui/themes";
import { clamp } from "lodash-es";
import { memo, useMemo, type ComponentProps, type ReactNode } from "react";
import { api } from "../../../../../../core/blitzkit/api";
@@ -16,6 +16,7 @@ import {
TankopediaRelativeAgainst,
} from "../../../../../../stores/tankopedia";
import { Info, type InfoProps } from "./Info";
import { StatScale } from "./StatScale";
type InfoWithDeltaProps = Omit<InfoProps, "name"> & {
stats: TankCharacteristics;
@@ -122,6 +123,15 @@ export const InfoWithDelta = memo<InfoWithDeltaProps>(
return othersValue !== undefined;
}) as TankCharacteristics[];
}, [relativeAgainst, shellIndex, equipmentMatrix, equalize]);
const values = useMemo(
() =>
others.map((tank) =>
typeof props.value === "function"
? props.value(tank)!
: (tank[props.value] as number),
),
[others],
);
const betterTanks = others.filter((tank) => {
const othersValue =
typeof props.value === "function"
@@ -161,13 +171,21 @@ export const InfoWithDelta = memo<InfoWithDeltaProps>(
{!noRanking && (
<Flex pl={indent ? "2" : "0"} align="center" gap="2">
<Progress
variant="soft"
size="1"
value={goodness * 100}
color={color}
style={{ height: "0.125rem", opacity: 0.5 }}
/>
<Box position="relative" flexGrow="1">
<StatScale
values={values}
value={uhWhatDoICallThisVariable}
lowerIsBetter={deltaType === "lowerIsBetter"}
/>
<Progress
variant="soft"
size="1"
value={goodness * 100}
color={color}
style={{ height: "0.125rem", opacity: 0.5 }}
/>
</Box>
<Text color="gray" size="1">
{clamp(betterTanks.length + 1, 1, others.length)} /{" "}
@@ -0,0 +1,71 @@
import { memo, useMemo } from "react";
const RESOLUTION = 1000;
const HEIGHT = 9;
const DENSE_TICKS = 100;
interface StatScaleProps {
values: number[];
value: number;
lowerIsBetter?: boolean;
}
export const StatScale = memo<StatScaleProps>(
({ values, value, lowerIsBetter }) => {
const ticks = useMemo(() => {
const min = Math.min(value, ...values);
const max = Math.max(value, ...values);
const range = max - min;
const counts = new Map<number, number>();
for (const other of values) {
const raw = range === 0 ? 0.5 : (other - min) / range;
const x = Math.round((lowerIsBetter ? 1 - raw : raw) * RESOLUTION);
if (x === 0 || x === RESOLUTION) continue;
counts.set(x, (counts.get(x) ?? 0) + 1);
}
return Array.from(counts, ([x, count]) => ({
x,
color:
count === 1
? "var(--gray-a5)"
: count <= 3
? "var(--gray-a6)"
: "var(--gray-a7)",
}));
}, [values, value, lowerIsBetter]);
return (
<div
style={{
position: "absolute",
top: 0,
bottom: 0,
left: 0,
width: "100%",
height: HEIGHT,
margin: "auto 0",
opacity: Math.min(1, Math.sqrt(DENSE_TICKS / ticks.length)),
pointerEvents: "none",
}}
>
{ticks.map(({ x, color }) => (
<div
key={x}
style={{
position: "absolute",
top: 0,
left: `${(x / RESOLUTION) * 100}%`,
width: 1,
height: "100%",
backgroundColor: color,
}}
/>
))}
</div>
);
},
);