mirror of
https://github.com/SlimeVR/SlimeVR-Server.git
synced 2026-04-06 02:01:58 +02:00
Compare commits
3 Commits
sapphire/d
...
tracker-gr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61065eba7f | ||
|
|
0e750fd1de | ||
|
|
7aabdbe724 |
@@ -26,6 +26,7 @@
|
||||
"@tweenjs/tween.js": "^25.0.0",
|
||||
"@twemoji/svg": "^15.0.0",
|
||||
"browser-fs-access": "^0.35.0",
|
||||
"chart.js": "^4.5.0",
|
||||
"classnames": "^2.5.1",
|
||||
"flatbuffers": "22.10.26",
|
||||
"intl-pluralrules": "^2.0.1",
|
||||
@@ -33,6 +34,7 @@
|
||||
"jotai": "^2.12.2",
|
||||
"prompts": "^2.4.2",
|
||||
"react": "^18.3.1",
|
||||
"react-chartjs-2": "^5.3.0",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-error-boundary": "^4.0.13",
|
||||
"react-helmet": "^6.1.0",
|
||||
|
||||
@@ -390,6 +390,10 @@ tracker-settings-update-blocked = Update not available. No other releases availa
|
||||
tracker-settings-update-available = { $versionName } is now available
|
||||
tracker-settings-update = Update now
|
||||
tracker-settings-update-title = Firmware version
|
||||
tracker-settings-graph-acceleration-title = Tracker Acceleration
|
||||
tracker-settings-graph-position-title = Tracker Position
|
||||
tracker-settings-graph-show-title = Show Tracker Graph
|
||||
tracker-settings-graph-hide-title = Hide Tracker Graph
|
||||
|
||||
## Tracker part card info
|
||||
tracker-part_card-no_name = No name
|
||||
|
||||
@@ -61,6 +61,16 @@ import { FirmwareUpdate } from './components/firmware-update/FirmwareUpdate';
|
||||
import { ConnectionLost } from './components/onboarding/pages/ConnectionLost';
|
||||
import { VRCWarningsPage } from './components/vrc/VRCWarningsPage';
|
||||
import { StayAlignedSetup } from './components/onboarding/pages/stay-aligned/StayAlignedSetup';
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
Title,
|
||||
Tooltip,
|
||||
Legend,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
PointElement,
|
||||
LineElement,
|
||||
} from 'chart.js';
|
||||
|
||||
export const GH_REPO = 'SlimeVR/SlimeVR-Server';
|
||||
export const VersionContext = createContext('');
|
||||
@@ -73,6 +83,16 @@ function Layout() {
|
||||
const { isMobile } = useBreakpoint('mobile');
|
||||
useDiscordPresence();
|
||||
|
||||
ChartJS.register(
|
||||
Title,
|
||||
Tooltip,
|
||||
Legend,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
PointElement,
|
||||
LineElement
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SerialDetectionModal></SerialDetectionModal>
|
||||
|
||||
177
gui/src/components/tracker/TrackerGraph.tsx
Normal file
177
gui/src/components/tracker/TrackerGraph.tsx
Normal file
@@ -0,0 +1,177 @@
|
||||
import { useLocalization } from '@fluent/react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Line } from 'react-chartjs-2';
|
||||
import { TrackerDataT } from 'solarxr-protocol';
|
||||
import { Button } from '@/components/commons/Button';
|
||||
import { useConfig } from '@/hooks/config';
|
||||
|
||||
export function TrackerGraph({ tracker }: { tracker: TrackerDataT }) {
|
||||
const { l10n } = useLocalization();
|
||||
const { config } = useConfig();
|
||||
|
||||
type AxisData = {
|
||||
x: number;
|
||||
y: number;
|
||||
time: number;
|
||||
};
|
||||
|
||||
type ChartData = {
|
||||
x: AxisData[];
|
||||
y: AxisData[];
|
||||
z: AxisData[];
|
||||
};
|
||||
|
||||
const [chartData, setChartData] = useState<ChartData>({
|
||||
x: [],
|
||||
y: [],
|
||||
z: [],
|
||||
});
|
||||
|
||||
const [showTrackerGraph, setShowTrackerGraph] = useState(false);
|
||||
|
||||
const secondDuration = 60;
|
||||
|
||||
useEffect(() => {
|
||||
if (!showTrackerGraph) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newValue = tracker.info?.isImu
|
||||
? tracker.linearAcceleration
|
||||
: tracker.position;
|
||||
if (!newValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentTime = new Date().getTime() / 1000;
|
||||
const startTime = currentTime - secondDuration;
|
||||
|
||||
const updateData = (data: AxisData[], newSample: number) => {
|
||||
const remapped = data
|
||||
.filter((value) => value.time >= startTime)
|
||||
.map((value) => ({ ...value, x: value.time - startTime }));
|
||||
remapped.push({
|
||||
time: currentTime,
|
||||
x: secondDuration,
|
||||
y: newSample,
|
||||
});
|
||||
return remapped;
|
||||
};
|
||||
|
||||
const newData = {
|
||||
x: updateData(chartData.x, newValue.x),
|
||||
y: updateData(chartData.y, newValue.y),
|
||||
z: updateData(chartData.z, newValue.z),
|
||||
};
|
||||
setChartData(newData);
|
||||
}, [tracker]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!showTrackerGraph) {
|
||||
setChartData({ x: [], y: [], z: [] });
|
||||
}
|
||||
}, [showTrackerGraph]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
variant="tertiary"
|
||||
className="self-start"
|
||||
onClick={() => setShowTrackerGraph(!showTrackerGraph)}
|
||||
>
|
||||
{l10n.getString(
|
||||
showTrackerGraph
|
||||
? 'tracker-settings-graph-hide-title'
|
||||
: 'tracker-settings-graph-show-title'
|
||||
)}
|
||||
</Button>
|
||||
{showTrackerGraph && (
|
||||
<div className="h-96">
|
||||
<Line
|
||||
options={{
|
||||
responsive: true,
|
||||
animation: false,
|
||||
font: {
|
||||
family: config?.fonts.map((font) => `"${font}"`).join(','),
|
||||
size: config?.textSize,
|
||||
},
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: l10n.getString(
|
||||
tracker?.info?.isImu
|
||||
? 'tracker-settings-graph-acceleration-title'
|
||||
: 'tracker-settings-graph-position-title'
|
||||
),
|
||||
color: 'white',
|
||||
},
|
||||
tooltip: {
|
||||
mode: 'index',
|
||||
intersect: false,
|
||||
animation: false,
|
||||
callbacks: {
|
||||
title: () => '',
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
labels: {
|
||||
color: 'white',
|
||||
},
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
type: 'linear',
|
||||
min: 0,
|
||||
max: secondDuration,
|
||||
ticks: {
|
||||
color: 'white',
|
||||
},
|
||||
},
|
||||
y: {
|
||||
min: -4,
|
||||
max: 4,
|
||||
ticks: {
|
||||
color: 'white',
|
||||
},
|
||||
},
|
||||
},
|
||||
elements: {
|
||||
point: {
|
||||
radius: 0,
|
||||
},
|
||||
},
|
||||
parsing: false,
|
||||
normalized: true,
|
||||
maintainAspectRatio: false,
|
||||
}}
|
||||
data={{
|
||||
labels: ['X', 'Y', 'Z'],
|
||||
datasets: [
|
||||
{
|
||||
label: 'X',
|
||||
data: chartData.x,
|
||||
borderColor: 'rgb(200, 50, 50)',
|
||||
backgroundColor: 'rgb(200, 100, 100)',
|
||||
},
|
||||
{
|
||||
label: 'Y',
|
||||
data: chartData.y,
|
||||
borderColor: 'rgb(50, 200, 50)',
|
||||
backgroundColor: 'rgb(100, 200, 100)',
|
||||
},
|
||||
{
|
||||
label: 'Z',
|
||||
data: chartData.z,
|
||||
borderColor: 'rgb(50, 50, 200)',
|
||||
backgroundColor: 'rgb(100, 100, 200)',
|
||||
},
|
||||
],
|
||||
}}
|
||||
id="tracker-graph"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -40,6 +40,7 @@ import semver from 'semver';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { ignoredTrackersAtom } from '@/store/app-store';
|
||||
import { checkForUpdate } from '@/hooks/firmware-update';
|
||||
import { TrackerGraph } from './TrackerGraph';
|
||||
|
||||
const rotationsLabels: [Quaternion, string][] = [
|
||||
[rotationToQuatMap.BACK, 'tracker-rotation-back'],
|
||||
@@ -505,6 +506,7 @@ export function TrackerSettingsPage() {
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{tracker && <TrackerGraph tracker={tracker.tracker} />}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
31
pnpm-lock.yaml
generated
31
pnpm-lock.yaml
generated
@@ -83,6 +83,9 @@ importers:
|
||||
browser-fs-access:
|
||||
specifier: ^0.35.0
|
||||
version: 0.35.0
|
||||
chart.js:
|
||||
specifier: ^4.5.0
|
||||
version: 4.5.0
|
||||
classnames:
|
||||
specifier: ^2.5.1
|
||||
version: 2.5.1
|
||||
@@ -104,6 +107,9 @@ importers:
|
||||
react:
|
||||
specifier: ^18.3.1
|
||||
version: 18.3.1
|
||||
react-chartjs-2:
|
||||
specifier: ^5.3.0
|
||||
version: 5.3.0(chart.js@4.5.0)(react@18.3.1)
|
||||
react-dom:
|
||||
specifier: ^18.3.1
|
||||
version: 18.3.1(react@18.3.1)
|
||||
@@ -720,6 +726,9 @@ packages:
|
||||
'@jridgewell/trace-mapping@0.3.25':
|
||||
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
|
||||
|
||||
'@kurkle/color@0.3.4':
|
||||
resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==}
|
||||
|
||||
'@mediapipe/tasks-vision@0.10.8':
|
||||
resolution: {integrity: sha512-Rp7ll8BHrKB3wXaRFKhrltwZl1CiXGdibPxuWXvqGnKTnv8fqa/nvftYNuSbf+pbJWKYCXdBtYTITdAUTGGh0Q==}
|
||||
|
||||
@@ -1780,6 +1789,10 @@ packages:
|
||||
character-reference-invalid@2.0.1:
|
||||
resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==}
|
||||
|
||||
chart.js@4.5.0:
|
||||
resolution: {integrity: sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==}
|
||||
engines: {pnpm: '>=8'}
|
||||
|
||||
chokidar@3.6.0:
|
||||
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
||||
engines: {node: '>= 8.10.0'}
|
||||
@@ -2470,6 +2483,7 @@ packages:
|
||||
got-fetch@5.1.10:
|
||||
resolution: {integrity: sha512-Gwj/A2htjvLEcY07PKDItv0WCPEs3dV2vWeZ+9TVBSKSTuWEZ4oXaMD0ZAOsajwx2orahQWN4HI0MfRyWSZsbg==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
deprecated: please use built-in fetch in nodejs
|
||||
peerDependencies:
|
||||
got: ^12.0.0
|
||||
|
||||
@@ -3542,6 +3556,12 @@ packages:
|
||||
resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
react-chartjs-2@5.3.0:
|
||||
resolution: {integrity: sha512-UfZZFnDsERI3c3CZGxzvNJd02SHjaSJ8kgW1djn65H1KK8rehwTjyrRKOG3VTMG8wtHZ5rgAO5oTHtHi9GCCmw==}
|
||||
peerDependencies:
|
||||
chart.js: ^4.1.1
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
react-composer@5.0.3:
|
||||
resolution: {integrity: sha512-1uWd07EME6XZvMfapwZmc7NgCZqDemcvicRi3wMJzXsQLvZ3L7fTHVyPy1bZdnWXM4iPjYuNE+uJ41MLKeTtnA==}
|
||||
peerDependencies:
|
||||
@@ -4959,6 +4979,8 @@ snapshots:
|
||||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.4.15
|
||||
|
||||
'@kurkle/color@0.3.4': {}
|
||||
|
||||
'@mediapipe/tasks-vision@0.10.8': {}
|
||||
|
||||
'@mgit-at/typescript-flatbuffers-codegen@0.1.3':
|
||||
@@ -6072,6 +6094,10 @@ snapshots:
|
||||
|
||||
character-reference-invalid@2.0.1: {}
|
||||
|
||||
chart.js@4.5.0:
|
||||
dependencies:
|
||||
'@kurkle/color': 0.3.4
|
||||
|
||||
chokidar@3.6.0:
|
||||
dependencies:
|
||||
anymatch: 3.1.3
|
||||
@@ -8159,6 +8185,11 @@ snapshots:
|
||||
|
||||
quick-lru@5.1.1: {}
|
||||
|
||||
react-chartjs-2@5.3.0(chart.js@4.5.0)(react@18.3.1):
|
||||
dependencies:
|
||||
chart.js: 4.5.0
|
||||
react: 18.3.1
|
||||
|
||||
react-composer@5.0.3(react@18.3.1):
|
||||
dependencies:
|
||||
prop-types: 15.8.1
|
||||
|
||||
Reference in New Issue
Block a user