mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-12 17:28:31 +03:00
feat: add density graph and bpm/level sorting
This commit is contained in:
@@ -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)];
|
||||
|
||||
Reference in New Issue
Block a user