From c306004376de94e0cd5f842f0461c6c3aa79f529 Mon Sep 17 00:00:00 2001 From: Robert Knight Date: Wed, 27 Nov 2024 14:32:34 +0000 Subject: [PATCH] Add graph line settings to aid accessibility --- lang/ui.en.json | 30 ++++++++++++++++++- package-lock.json | 12 ++++---- package.json | 2 +- src/components/LiveGraph.tsx | 46 +++++++++++++++++++++++----- src/components/RecordingGraph.tsx | 9 ++++-- src/components/SettingsDialog.tsx | 41 ++++++++++++++++++++++++- src/hooks/use-graph-line-styles.ts | 33 ++++++++++++++++++++ src/messages/ui.en.json | 48 +++++++++++++++++++++++++++++- src/recording-graph.ts | 21 +++++++++++-- src/settings.tsx | 17 +++++++++++ 10 files changed, 236 insertions(+), 23 deletions(-) create mode 100644 src/hooks/use-graph-line-styles.ts diff --git a/lang/ui.en.json b/lang/ui.en.json index 392860796..b819630f2 100644 --- a/lang/ui.en.json +++ b/lang/ui.en.json @@ -731,6 +731,34 @@ "defaultMessage": "Default (red, blue, green)", "description": "Graph colour scheme option" }, + "graph-line-scheme": { + "defaultMessage": "Graph line style", + "description": "Graph line scheme setting label" + }, + "graph-line-scheme-accessible": { + "defaultMessage": "Accessible lines (solid, dashed, dots)", + "description": "Graph line scheme option" + }, + "graph-line-scheme-solid": { + "defaultMessage": "Solid lines", + "description": "Graph line scheme option" + }, + "graph-line-weight": { + "defaultMessage": "Graph line thickness", + "description": "Graph line weight setting label" + }, + "graph-line-weight-normal": { + "defaultMessage": "Normal", + "description": "Graph line weight option" + }, + "graph-line-weight-thick": { + "defaultMessage": "Thick", + "description": "Graph line weight option" + }, + "graph-line-weight-thin": { + "defaultMessage": "Thin", + "description": "Graph line weight option" + }, "help-label": { "defaultMessage": "Help", "description": "Help icon aria label" @@ -1659,4 +1687,4 @@ "defaultMessage": "unplug and replug the USB cable", "description": "WebUSB error dialog" } -} +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 923553a76..cf632a6cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@microbit/makecode-embed": "^0.0.0-alpha.7", "@microbit/microbit-connection": "^0.0.0-alpha.29", "@microbit/ml-header-generator": "^0.4.3", + "@microbit/smoothie": "^1.37.0-microbit.1", "@tensorflow/tfjs": "^4.20.0", "@types/w3c-web-serial": "^1.0.6", "@types/w3c-web-usb": "^1.0.6", @@ -34,7 +35,6 @@ "react-intl": "^6.6.8", "react-router": "^6.24.0", "react-router-dom": "^6.24.0", - "smoothie": "^1.36.1", "zustand": "^4.5.5" }, "devDependencies": { @@ -4387,6 +4387,11 @@ "resolved": "https://registry.npmjs.org/@microbit/ml-header-generator/-/ml-header-generator-0.4.3.tgz", "integrity": "sha512-aMdo074VvHr4Ol1ctx8zvvaqX/FjOBD7bv2I+CLyV051OeZ24PdYB6FMf5nH6ULJVyUgKGNjIadAxRbieaPauA==" }, + "node_modules/@microbit/smoothie": { + "version": "1.37.0-microbit.1", + "resolved": "https://registry.npmjs.org/@microbit/smoothie/-/smoothie-1.37.0-microbit.1.tgz", + "integrity": "sha512-wuYQf6/sgMvo3IfVxbnC9MZmPLosH0Emj6ICgIpF7RQSV8prr42w54z64siaX3vET9SWwN9KPhC6KP2wwakEcA==" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -12502,11 +12507,6 @@ "integrity": "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==", "dev": true }, - "node_modules/smoothie": { - "version": "1.36.1", - "resolved": "https://registry.npmjs.org/smoothie/-/smoothie-1.36.1.tgz", - "integrity": "sha512-499Vr2od6TicP8s7ykcyTfddh/6n11BB41G9RE7gqQRyfoPIAYotUTzwAxQpAfOdVOb+BvcG2qla+hjyqwe+PA==" - }, "node_modules/snake-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", diff --git a/package.json b/package.json index b559344cb..c42465445 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "@microbit/makecode-embed": "^0.0.0-alpha.7", "@microbit/microbit-connection": "^0.0.0-alpha.29", "@microbit/ml-header-generator": "^0.4.3", + "@microbit/smoothie": "^1.37.0-microbit.1", "@tensorflow/tfjs": "^4.20.0", "@types/w3c-web-serial": "^1.0.6", "@types/w3c-web-usb": "^1.0.6", @@ -81,7 +82,6 @@ "react-intl": "^6.6.8", "react-router": "^6.24.0", "react-router-dom": "^6.24.0", - "smoothie": "^1.36.1", "zustand": "^4.5.5" } } diff --git a/src/components/LiveGraph.tsx b/src/components/LiveGraph.tsx index 9e7d9aea8..eaa4fb9bc 100644 --- a/src/components/LiveGraph.tsx +++ b/src/components/LiveGraph.tsx @@ -2,7 +2,7 @@ import { HStack, usePrevious } from "@chakra-ui/react"; import { useSize } from "@chakra-ui/react-use-size"; import { AccelerometerDataEvent } from "@microbit/microbit-connection"; import { useEffect, useMemo, useRef, useState } from "react"; -import { SmoothieChart, TimeSeries } from "smoothie"; +import { SmoothieChart, TimeSeries } from "@microbit/smoothie"; import { useConnectActions } from "../connect-actions-hooks"; import { ConnectionStatus } from "../connect-status-hooks"; import { useConnectionStage } from "../connection-stage-hooks"; @@ -10,6 +10,10 @@ import { useGraphColors } from "../hooks/use-graph-colors"; import { maxAccelerationScaleForGraphs } from "../mlConfig"; import { useSettings, useStore } from "../store"; import LiveGraphLabels from "./LiveGraphLabels"; +import { + graphLineStyleStringToArray, + useGraphLineStyles, +} from "../hooks/use-graph-line-styles"; export const smoothenDataPoint = (curr: number, next: number) => { // TODO: Factor out so that recording graph can do the same @@ -20,13 +24,16 @@ export const smoothenDataPoint = (curr: number, next: number) => { const LiveGraph = () => { const { isConnected, status } = useConnectionStage(); const connectActions = useConnectActions(); - const [{ graphColorScheme }] = useSettings(); + const [{ graphColorScheme, graphLineScheme, graphLineWeight }] = + useSettings(); const colors = useGraphColors(graphColorScheme); + const lineStyles = useGraphLineStyles(graphLineScheme); const canvasRef = useRef(null); const [chart, setChart] = useState(undefined); - const lineWidth = 2; + const lineWidth = + graphLineWeight === "normal" ? 2 : graphLineWeight === "thin" ? 1 : 3; const liveGraphContainerRef = useRef(null); const { width, height } = useSize(liveGraphContainerRef) ?? { @@ -58,9 +65,21 @@ const LiveGraph = () => { enableDpiScaling: false, }); - smoothieChart.addTimeSeries(lineX, { lineWidth, strokeStyle: colors.x }); - smoothieChart.addTimeSeries(lineY, { lineWidth, strokeStyle: colors.y }); - smoothieChart.addTimeSeries(lineZ, { lineWidth, strokeStyle: colors.z }); + smoothieChart.addTimeSeries(lineX, { + lineWidth, + strokeStyle: colors.x, + lineDash: graphLineStyleStringToArray(lineStyles.x), + }); + smoothieChart.addTimeSeries(lineY, { + lineWidth, + strokeStyle: colors.y, + lineDash: graphLineStyleStringToArray(lineStyles.y), + }); + smoothieChart.addTimeSeries(lineZ, { + lineWidth, + strokeStyle: colors.z, + lineDash: graphLineStyleStringToArray(lineStyles.z), + }); smoothieChart.addTimeSeries(recordLines, { lineWidth: 3, @@ -73,7 +92,19 @@ const LiveGraph = () => { return () => { smoothieChart.stop(); }; - }, [colors.x, colors.y, colors.z, lineX, lineY, lineZ, recordLines]); + }, [ + colors.x, + colors.y, + colors.z, + lineStyles.x, + lineStyles.y, + lineStyles.z, + lineWidth, + lineX, + lineY, + lineZ, + recordLines, + ]); useEffect(() => { if (isConnected || status === ConnectionStatus.ReconnectingAutomatically) { @@ -134,6 +165,7 @@ const LiveGraph = () => { overflow="hidden" > { - const [{ graphColorScheme }] = useSettings(); + const [{ graphColorScheme, graphLineScheme, graphLineWeight }] = + useSettings(); const canvasRef = useRef(null); const colors = useGraphColors(graphColorScheme); + const lineStyles = useGraphLineStyles(graphLineScheme); useEffect(() => { Chart.unregister(...registerables); Chart.register([LinearScale, LineController, PointElement, LineElement]); const chart = new Chart( canvasRef.current?.getContext("2d") ?? new HTMLCanvasElement(), - getRecordingChartConfig(data, colors) + getRecordingChartConfig(data, colors, lineStyles, graphLineWeight) ); return () => { chart.destroy(); }; - }, [colors, data]); + }, [colors, data, graphLineWeight, lineStyles]); return ( + + setSettings({ + ...settings, + graphLineScheme, + }) + } + /> + + setSettings({ + ...settings, + graphLineWeight, + }) + } + />