feat: add density graph and bpm/level sorting

This commit is contained in:
zkldi
2022-06-12 21:17:10 +01:00
parent 4843c6f509
commit b2de507d17
5 changed files with 155 additions and 1 deletions
@@ -0,0 +1,78 @@
import { ResponsiveLine, Serie } from "@nivo/line";
import React from "react";
import { COLOUR_SET } from "tachi-common";
import ChartTooltip from "./ChartTooltip";
export default function ITGDensityChart({
width = "100%",
height = "100%",
mobileHeight = "100%",
mobileWidth = width,
data,
}: {
mobileHeight?: number | string;
mobileWidth?: number | string;
width?: number | string;
height?: number | string;
data: Serie[];
} & ResponsiveLine["props"]) {
const component = (
<ResponsiveLine
data={data}
margin={{ top: 30, bottom: 50, left: 90, right: 50 }}
xScale={{ type: "linear" }}
motionConfig="stiff"
crosshairType="x"
yScale={{ type: "linear" }}
axisLeft={{ format: y => `${npsToBPM(y)}BPM` }}
enablePoints={false}
useMesh={true}
enableGridX={false}
colors={COLOUR_SET.purple}
theme={{
background: "none",
textColor: "#ffffff",
grid: {
line: {
stroke: "#1c1c1c",
strokeWidth: 1,
},
},
}}
curve="linear"
tooltip={d => (
<ChartTooltip
point={d.point}
renderFn={p => (
<div>
Measure {p.data.xFormatted}:{" "}
{npsToBPM(Number(p.data.yFormatted)).toFixed()}BPM
</div>
)}
/>
)}
legends={[]}
enableArea
/>
);
return (
<>
<div className="d-block d-md-none" style={{ height: mobileHeight, width: mobileWidth }}>
{component}
</div>
<div className="d-none d-md-block" style={{ height, width }}>
{component}
</div>
</>
);
}
// Always assume 4/4 time. I know it sucks, but stepmania does the same.
function npsToBPM(nps: number) {
return (nps * 60) / 4;
}
function bpmToNPS(bpm: number) {
return (bpm * 4) / 60;
}
@@ -40,7 +40,8 @@ export default function ITGDifficultyCell({ chart }: { chart: ChartDocument<"itg
>
<div>
<span>
{chart.data.difficultyTag}: {chart.level}
{chart.data.difficultyTag}: {chart.level} [{chart.data.displayBPM.toFixed()}
]
</span>
<br />
<Muted>{chart.data.charter}</Muted>
@@ -2,6 +2,7 @@ import { Game } from "tachi-common";
import { Playtype } from "types/tachi";
import { BMSGraphsComponent } from "./components/BMSScoreDropdownParts";
import { IIDXGraphsComponent } from "./components/IIDXScoreDropdownParts";
import { ITGGraphsComponent } from "./components/ITGScoreDropdownParts";
export function GPTDropdownSettings(game: Game, playtype: Playtype): any {
if (game === "iidx") {
@@ -16,6 +17,11 @@ export function GPTDropdownSettings(game: Game, playtype: Playtype): any {
renderScoreInfo: true,
GraphComponent: BMSGraphsComponent as any,
};
} else if (game === "itg") {
return {
renderScoreInfo: true,
GraphComponent: ITGGraphsComponent as any,
};
}
return {};
@@ -0,0 +1,54 @@
import ITGDensityChart from "components/charts/ITGDensityChart";
import SelectNav from "components/util/SelectNav";
import React, { useState } from "react";
import { Nav } from "react-bootstrap";
import { ChartDocument, PBScoreDocument, ScoreDocument } from "tachi-common";
export function ITGGraphsComponent({
score,
chart,
}: {
score: ScoreDocument<"itg:Stamina"> | PBScoreDocument<"itg:Stamina">;
chart: ChartDocument<"itg:Stamina">;
}) {
const [graph, setGraph] = useState("DENSITY");
return (
<>
<div className="col-12 d-flex justify-content-center">
<Nav variant="pills">
<SelectNav id="DENSITY" value={graph} setValue={setGraph}>
Chart Density
</SelectNav>
<SelectNav id="HISTOGRAM" value={graph} setValue={setGraph} disabled>
Judgement Histogram
</SelectNav>
</Nav>
</div>
<div className="col-12">
{chart.data.breakdown.npsPerMeasure ? (
<ITGDensityChart
height="200px"
mobileHeight="175px"
data={[
{
id: "chart",
data: chart.data.breakdown.npsPerMeasure.map((e, i) => ({
x: i,
y: e,
})),
},
]}
/>
) : (
<div
className="d-flex align-items-center justify-content-center"
style={{ height: "200px" }}
>
<span className="text-muted">No chart graph :(</span>
</div>
)}
</div>
</>
);
}
@@ -29,6 +29,21 @@ export default function ChartHeader<D extends ScoreDataset | FolderDataset | PBD
sortFn = k => CascadingTierlistValue(chartGetter(k), "kt-HC", "kt-NC");
} else if (game === "bms" || game === "pms") {
sortFn = k => CascadingTierlistValue(chartGetter(k), "sgl-HC", "sgl-EC");
} else if (game === "itg") {
return [
"Chart",
"Chart",
(a, b) => {
const ac = chartGetter(a) as ChartDocument<"itg:Stamina">;
const bc = chartGetter(b) as ChartDocument<"itg:Stamina">;
if (ac.levelNum === bc.levelNum) {
return ac.data.displayBPM - bc.data.displayBPM;
}
return ac.levelNum - bc.levelNum;
},
];
}
return ["Chart", "Chart", NumericSOV(sortFn)];