Skip to content

Commit 7aabdbe

Browse files
committed
Add tracker graph to tracker settings page
1 parent f2b4d46 commit 7aabdbe

File tree

6 files changed

+222
-0
lines changed

6 files changed

+222
-0
lines changed

gui/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@
2626
"@tweenjs/tween.js": "^25.0.0",
2727
"@twemoji/svg": "^15.0.0",
2828
"browser-fs-access": "^0.35.0",
29+
"chart.js": "^4.5.0",
2930
"classnames": "^2.5.1",
3031
"flatbuffers": "22.10.26",
3132
"intl-pluralrules": "^2.0.1",
3233
"ip-num": "^1.5.1",
3334
"jotai": "^2.12.2",
3435
"prompts": "^2.4.2",
3536
"react": "^18.3.1",
37+
"react-chartjs-2": "^5.3.0",
3638
"react-dom": "^18.3.1",
3739
"react-error-boundary": "^4.0.13",
3840
"react-helmet": "^6.1.0",

gui/public/i18n/en/translation.ftl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,10 @@ tracker-settings-update-blocked = Update not available. No other releases availa
390390
tracker-settings-update-available = { $versionName } is now available
391391
tracker-settings-update = Update now
392392
tracker-settings-update-title = Firmware version
393+
tracker-settings-graph-acceleration-title = Tracker Acceleration
394+
tracker-settings-graph-position-title = Tracker Position
395+
tracker-settings-graph-show-title = Show Tracker Graph
396+
tracker-settings-graph-hide-title = Hide Tracker Graph
393397
394398
## Tracker part card info
395399
tracker-part_card-no_name = No name

gui/src/App.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,16 @@ import { FirmwareUpdate } from './components/firmware-update/FirmwareUpdate';
6161
import { ConnectionLost } from './components/onboarding/pages/ConnectionLost';
6262
import { VRCWarningsPage } from './components/vrc/VRCWarningsPage';
6363
import { StayAlignedSetup } from './components/onboarding/pages/stay-aligned/StayAlignedSetup';
64+
import {
65+
Chart as ChartJS,
66+
Title,
67+
Tooltip,
68+
Legend,
69+
CategoryScale,
70+
LinearScale,
71+
PointElement,
72+
LineElement,
73+
} from 'chart.js';
6474

6575
export const GH_REPO = 'SlimeVR/SlimeVR-Server';
6676
export const VersionContext = createContext('');
@@ -73,6 +83,16 @@ function Layout() {
7383
const { isMobile } = useBreakpoint('mobile');
7484
useDiscordPresence();
7585

86+
ChartJS.register(
87+
Title,
88+
Tooltip,
89+
Legend,
90+
CategoryScale,
91+
LinearScale,
92+
PointElement,
93+
LineElement
94+
);
95+
7696
return (
7797
<>
7898
<SerialDetectionModal></SerialDetectionModal>
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import { useLocalization } from '@fluent/react';
2+
import { useEffect, useState } from 'react';
3+
import { Line } from 'react-chartjs-2';
4+
import { TrackerDataT } from 'solarxr-protocol';
5+
import { Button } from '@/components/commons/Button';
6+
7+
export function TrackerGraph({ tracker }: { tracker: TrackerDataT }) {
8+
const { l10n } = useLocalization();
9+
10+
type AxisData = {
11+
x: number;
12+
y: number;
13+
time: number;
14+
};
15+
16+
type ChartData = {
17+
x: AxisData[];
18+
y: AxisData[];
19+
z: AxisData[];
20+
};
21+
22+
const [chartData, setChartData] = useState<ChartData>({
23+
x: [],
24+
y: [],
25+
z: [],
26+
});
27+
28+
const [showTrackerGraph, setShowTrackerGraph] = useState(false);
29+
30+
const secondDuration = 60;
31+
32+
useEffect(() => {
33+
if (!showTrackerGraph) {
34+
return;
35+
}
36+
37+
const newValue = tracker.info?.isImu
38+
? tracker.linearAcceleration
39+
: tracker.position;
40+
if (!newValue) {
41+
return;
42+
}
43+
44+
const currentTime = new Date().getTime() / 1000;
45+
const startTime = currentTime - secondDuration;
46+
47+
const updateData = (data: AxisData[], newSample: number) => {
48+
const remapped = data
49+
.filter((value) => value.time >= startTime)
50+
.map((value) => ({ ...value, x: value.time - startTime }));
51+
remapped.push({
52+
time: currentTime,
53+
x: secondDuration,
54+
y: newSample,
55+
});
56+
return remapped;
57+
};
58+
59+
const newData = {
60+
x: updateData(chartData.x, newValue.x),
61+
y: updateData(chartData.y, newValue.y),
62+
z: updateData(chartData.z, newValue.z),
63+
};
64+
setChartData(newData);
65+
}, [tracker]);
66+
67+
useEffect(() => {
68+
if (!showTrackerGraph) {
69+
setChartData({ x: [], y: [], z: [] });
70+
}
71+
}, [showTrackerGraph]);
72+
73+
const options = {
74+
responsive: true,
75+
animation: false,
76+
plugins: {
77+
title: {
78+
display: true,
79+
text: l10n.getString(
80+
tracker?.info?.isImu
81+
? 'tracker-settings-graph-acceleration-title'
82+
: 'tracker-settings-graph-position-title'
83+
),
84+
},
85+
tooltip: {
86+
mode: 'index',
87+
intersect: false,
88+
animation: false,
89+
callbacks: {
90+
title: () => '',
91+
},
92+
},
93+
},
94+
scales: {
95+
x: {
96+
type: 'linear',
97+
min: 0,
98+
max: secondDuration,
99+
},
100+
y: {
101+
min: -4,
102+
max: 4,
103+
},
104+
},
105+
elements: {
106+
point: {
107+
radius: 0,
108+
},
109+
},
110+
parsing: false,
111+
normalized: true,
112+
} as const;
113+
114+
return (
115+
<>
116+
<Button
117+
variant="tertiary"
118+
className="self-start"
119+
onClick={() => setShowTrackerGraph(!showTrackerGraph)}
120+
>
121+
{l10n.getString(
122+
showTrackerGraph
123+
? 'tracker-settings-graph-hide-title'
124+
: 'tracker-settings-graph-show-title'
125+
)}
126+
</Button>
127+
{showTrackerGraph && (
128+
<Line
129+
options={options}
130+
data={{
131+
labels: ['X', 'Y', 'Z'],
132+
datasets: [
133+
{
134+
label: 'X',
135+
data: chartData.x,
136+
borderColor: 'rgb(200, 50, 50)',
137+
backgroundColor: 'rgb(200, 100, 100)',
138+
},
139+
{
140+
label: 'Y',
141+
data: chartData.y,
142+
borderColor: 'rgb(50, 200, 50)',
143+
backgroundColor: 'rgb(100, 200, 100)',
144+
},
145+
{
146+
label: 'Z',
147+
data: chartData.z,
148+
borderColor: 'rgb(50, 50, 200)',
149+
backgroundColor: 'rgb(100, 100, 200)',
150+
},
151+
],
152+
}}
153+
id="tracker-graph"
154+
/>
155+
)}
156+
</>
157+
);
158+
}

gui/src/components/tracker/TrackerSettings.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ import semver from 'semver';
4040
import { useSetAtom } from 'jotai';
4141
import { ignoredTrackersAtom } from '@/store/app-store';
4242
import { checkForUpdate } from '@/hooks/firmware-update';
43+
import { TrackerGraph } from './TrackerGraph';
44+
import { useConfig } from '@/hooks/config';
4345

4446
const rotationsLabels: [Quaternion, string][] = [
4547
[rotationToQuatMap.BACK, 'tracker-rotation-back'],
@@ -75,6 +77,8 @@ export function TrackerSettingsPage() {
7577

7678
const tracker = useTrackerFromId(trackernum, deviceid);
7779

80+
const { config } = useConfig();
81+
7882
const onDirectionSelected = (mountingOrientationDegrees: Quaternion) => {
7983
if (!tracker) return;
8084

@@ -505,6 +509,9 @@ export function TrackerSettingsPage() {
505509
</Button>
506510
</div>
507511
)}
512+
{config?.debug && tracker && (
513+
<TrackerGraph tracker={tracker.tracker} />
514+
)}
508515
</div>
509516
</div>
510517
</form>

pnpm-lock.yaml

Lines changed: 31 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)