From 43f233aae79aa4f22e71ad64d2f822bd0db893d6 Mon Sep 17 00:00:00 2001 From: David Harrigan Date: Thu, 11 Aug 2022 21:47:30 -0400 Subject: [PATCH] wip: get some lines goin --- .../_experimental/CornerAnalysis.tsx | 99 +++++++++++++++++++ src/components/_experimental/TrackMap.tsx | 34 ++----- src/libs/types.ts | 17 ++++ src/pages/experiments/trackmap.tsx | 14 ++- 4 files changed, 138 insertions(+), 26 deletions(-) create mode 100644 src/components/_experimental/CornerAnalysis.tsx create mode 100644 src/libs/types.ts diff --git a/src/components/_experimental/CornerAnalysis.tsx b/src/components/_experimental/CornerAnalysis.tsx new file mode 100644 index 0000000..b5f31c1 --- /dev/null +++ b/src/components/_experimental/CornerAnalysis.tsx @@ -0,0 +1,99 @@ +import * as d3 from "d3"; +import React, { useRef, useEffect, useMemo } from "react"; +import { useRefDimensions } from "libs/react/dimensions"; +import type { DriverData, Telemetry } from "libs/types"; + +interface CornerAnalysisProps { + turnNumber: number; + driverData: DriverData[]; + distanceFrom: number; + distanceTo: number; +} + +export const CornerAnalysis = (props: CornerAnalysisProps) => { + const ref = useRef(null); + const dimensions = useRefDimensions(ref); + const svgRef = useRef(null); + const allDriverData: DriverTelemetry[] = props.driverData + .map((d) => { + return d.data.map((tel) => { + return { + ...tel, + Code: d.driverCode, + Color: d.driverColor, + }; + }); + }) + .flat(); + + const [mx, my] = [14, 14]; + + // x scale bounds + const distanceScale = useMemo(() => { + const [xMin, xMax] = [props.distanceFrom, props.distanceTo]; + return d3 + .scaleLinear() + .domain([xMin ?? 0, xMax ?? 0]) + .range([mx, dimensions.width - mx]); + }, [allDriverData, dimensions.width, mx]); + + // y scale bounds + const speedScale = useMemo(() => { + const [yMin, yMax] = d3.extent(allDriverData, (d) => d.Speed); + return d3 + .scaleLinear() + .domain([yMin ?? 0, yMax ?? 0]) + .range([dimensions.height - my, my]); + }, [allDriverData, dimensions.height, my]); + + // draw + useEffect(() => { + if (!distanceScale || !speedScale) { + return; + } + + d3.select(svgRef.current).selectAll("*").remove(); + drawSpeedGraph(allDriverData); + }, [props, distanceScale, speedScale]); + + const drawSpeedGraph = () => { + const color = d3 + .scaleOrdinal() + .domain(props.driverData.map((d) => d.driverCode)) + .range(props.driverData.map((d) => d.driverColor)); + + const g = d3 + .select(svgRef.current) + .selectAll(`.speed-graph`) + .data(props.driverData) + .enter() + .append("g") + .attr("class", "speed-graph"); + + g.append("path") + .attr("d", (d) => { + return d3 + .line() + .x((d) => { + return distanceScale(d.Distance); + }) + .y((d) => speedScale(d.Speed))(d.data); + }) + .attr("fill", "none") + .attr("stroke-width", 2) + .attr("stroke", (d) => color(d.driverCode)); + }; + + return ( +
+
+

+ Corner Analysis - Turn {props.turnNumber} 🏎️ 🏎️ 🏎️ +

+
+
+ +
+
+ ); +}; diff --git a/src/components/_experimental/TrackMap.tsx b/src/components/_experimental/TrackMap.tsx index 8a2f074..177efb5 100644 --- a/src/components/_experimental/TrackMap.tsx +++ b/src/components/_experimental/TrackMap.tsx @@ -1,25 +1,10 @@ import * as d3 from "d3"; import React, { useRef, useEffect, useState, useMemo } from "react"; -import { Dimensions, useRefDimensions } from "libs/react/dimensions"; -import { svg } from "d3"; +import { useRefDimensions } from "libs/react/dimensions"; +import type { TrackData, DriverData, Telemetry } from "libs/types"; -interface DriverData { - driverCode: string; - driverColor: string; - data: Telemetry[]; -} - -type DriverTelemetry = Partial & - TrackMap & { - Code?: string; - }; - -type Telemetry = TrackMap & { - Speed: number; - Distance: number; -}; - -type TrackMap = { +type DriverTelemetry = Partial & { + Code?: string; X: number; Y: number; }; @@ -93,7 +78,7 @@ export const TrackMap = (props: TrackMapProps) => { .scaleLinear() .domain([xMin ?? 0, xMax ?? 0]) .range([mx, dimensions.width - mx]); - }, [map, dimensions.width]); + }, [map, dimensions.width, mx]); // y scale bounds const yScale = useMemo(() => { @@ -102,14 +87,13 @@ export const TrackMap = (props: TrackMapProps) => { .scaleLinear() .domain([yMin ?? 0, yMax ?? 0]) .range([dimensions.height - my, my]); - }, [map, dimensions.height]); + }, [map, dimensions.height, my]); // draw when scales have changed useEffect(() => { if (!xScale || !yScale) { return; } - console.log("draw!"); d3.select(svgRef.current).selectAll("*").remove(); drawMap(map); @@ -120,11 +104,11 @@ export const TrackMap = (props: TrackMapProps) => { }, [props, xScale, yScale]); // draw track map - const drawMap = (map: TrackMap[]) => { + const drawMap = (map: TrackData[]) => { const g = d3 .select(svgRef.current) .selectAll(".track-map") - .data([map]) + .data([map]) .enter() .append("g") .attr("class", "track-map"); @@ -133,7 +117,7 @@ export const TrackMap = (props: TrackMapProps) => { .attr( "d", d3 - .line() + .line() .x((d) => xScale(d.X)) .y((d) => yScale(d.Y)) ) diff --git a/src/libs/types.ts b/src/libs/types.ts new file mode 100644 index 0000000..38e5aad --- /dev/null +++ b/src/libs/types.ts @@ -0,0 +1,17 @@ +export interface DriverData { + driverCode: string; + driverColor: string; + data: Telemetry[]; +} + +export type Telemetry = TrackData & { + Speed: number; + Distance: number; + Brake: boolean; + Throttle: number; +}; + +export type TrackData = { + X: number; + Y: number; +}; diff --git a/src/pages/experiments/trackmap.tsx b/src/pages/experiments/trackmap.tsx index 41e4daf..10ccfcf 100644 --- a/src/pages/experiments/trackmap.tsx +++ b/src/pages/experiments/trackmap.tsx @@ -1,9 +1,10 @@ import { TrackMap } from "components/_experimental/TrackMap"; +import { CornerAnalysis } from "components/_experimental/CornerAnalysis"; import { NorrisData, RicciardoData } from "libs/data/mclaren"; export default function TrackMapExperiment() { return ( -
+
+
+ +
); }