diff --git a/src/TrackLabConstants.ts b/src/TrackLabConstants.ts index 55c6a65..9c689b7 100644 --- a/src/TrackLabConstants.ts +++ b/src/TrackLabConstants.ts @@ -52,8 +52,3 @@ export const MIN_CALIB_DISTANCE = 1e-9; // minimum real-world calibration distan // ── Webcam panel ────────────────────────────────────────────────────────────── export const WEBCAM_PREVIEW_WIDTH = 480; // width of the preview and review video elements export const WEBCAM_PREVIEW_HEIGHT = 270; // height of the preview and review video elements - -// ── Graph decimation ───────────────────────────────────────────────────────── -// Controls how many sub-step data points are skipped when plotting to reduce -// rendering overhead while maintaining visual fidelity. -export const SUB_STEP_DECIMATION = 1; // keep every Nth point (1 = no decimation) diff --git a/src/screen-name/graph/ConfigurableGraph.ts b/src/screen-name/graph/ConfigurableGraph.ts index 2e52864..f8e5ed5 100644 --- a/src/screen-name/graph/ConfigurableGraph.ts +++ b/src/screen-name/graph/ConfigurableGraph.ts @@ -28,15 +28,11 @@ import { } from "scenerystack/scenery"; import { PhetFont } from "scenerystack/scenery-phet"; import TrackLabColors from "../../TrackLabColors.js"; -import { SUB_STEP_DECIMATION } from "../../TrackLabConstants.js"; import trackLab from "../../TrackLabNamespace.js"; import GraphControlsPanel from "./GraphControlsPanel.js"; import GraphDataManager from "./GraphDataManager.js"; import GraphInteractionHandler from "./GraphInteractionHandler.js"; -import type { - PlottableProperty, - SubStepDataPoint, -} from "./PlottableProperty.js"; +import type { PlottableProperty } from "./PlottableProperty.js"; // Grid line styling const GRID_LINE_WIDTH = 0.5; @@ -101,9 +97,6 @@ export default class ConfigurableGraph extends Node { // Title panel with combo boxes (needs to be on top of header bar) private readonly titlePanel: Node; - // Sub-step decimation counter for high-resolution data - private decimationCounter: number = 0; - // Drag and resize state properties (kept for dispose) private readonly isDraggingProperty: BooleanProperty; private readonly isResizingProperty: BooleanProperty; @@ -623,60 +616,28 @@ export default class ConfigurableGraph extends Node { this.interactionHandler.updateResizeHandlePositions(); } - /** - * Add a new data point based on current property values. - * @deprecated Not currently called anywhere — superseded by addDataPointsFromSubSteps. - */ - public addDataPoint(): void { - const xValue = this.xPropertyProperty.value.property?.value; - const yValue = this.yPropertyProperty.value.property?.value; - if (xValue === undefined || yValue === undefined) return; - - this.dataManager.addDataPoint(xValue, yValue); - } - /** * Clear all data points */ public clearData(): void { this.dataManager.clearData(); - this.decimationCounter = 0; } /** - * Add data points from sub-step data collected during ODE integration. - * Maps the sub-step data to the currently selected x and y axes. - * Uses decimation to prevent memory overflow while maintaining smooth curves. - * @param subStepData - Array of sub-step data points from the model + * Add data points from a record array, mapping each record to the selected axes. */ - public addDataPointsFromSubSteps(subStepData: SubStepDataPoint[]): void { - if (subStepData.length === 0) return; + public addDataPoints(dataPoints: Array>): void { + if (dataPoints.length === 0) return; const xProperty = this.xPropertyProperty.value; const yProperty = this.yPropertyProperty.value; - // Map sub-step data to x/y values with decimation const mappedPoints: Array<{ x: number; y: number }> = []; - const decimation = SUB_STEP_DECIMATION; - - for (const point of subStepData) { - this.decimationCounter++; - - // Only keep every Nth point - if (this.decimationCounter >= decimation) { - this.decimationCounter = 0; - - const x = this.getValueForAxis(xProperty, point); - const y = this.getValueForAxis(yProperty, point); - - if ( - x !== null && - y !== null && - Number.isFinite(x) && - Number.isFinite(y) - ) { - mappedPoints.push({ x, y }); - } + for (const point of dataPoints) { + const x = this.getValueForAxis(xProperty, point); + const y = this.getValueForAxis(yProperty, point); + if (x !== null && y !== null && Number.isFinite(x) && Number.isFinite(y)) { + mappedPoints.push({ x, y }); } } @@ -686,19 +647,16 @@ export default class ConfigurableGraph extends Node { } /** - * Get the value for a specific axis from a sub-step data point. - * Uses the type-safe subStepAccessor when available, otherwise falls back - * to the current property value for derived quantities (energy, RMS, etc.). + * Get the value for a specific axis from a data point record. + * Uses the accessor when available, otherwise falls back to property.value. */ private getValueForAxis( axisProperty: PlottableProperty, - point: SubStepDataPoint, + point: Record, ): number | null { - if (axisProperty.subStepAccessor) { - return axisProperty.subStepAccessor(point); + if (axisProperty.accessor) { + return axisProperty.accessor(point); } - // For properties without sub-step data, fall back to current property value. - // This handles derived properties like energy, RMS values, etc. return axisProperty.property?.value ?? null; } diff --git a/src/screen-name/graph/GraphDataManager.ts b/src/screen-name/graph/GraphDataManager.ts index 93da76e..adc9fa9 100644 --- a/src/screen-name/graph/GraphDataManager.ts +++ b/src/screen-name/graph/GraphDataManager.ts @@ -84,9 +84,9 @@ export default class GraphDataManager { } /** - * Add multiple data points at once (for sub-step data). + * Add multiple data points at once. * More efficient than calling addDataPoint repeatedly. - * @param points - Array of [x, y] value pairs + * @param points - Array of x/y value pairs */ public addDataPoints(points: Array<{ x: number; y: number }>): void { if (points.length === 0) return; diff --git a/src/screen-name/graph/PlottableProperty.ts b/src/screen-name/graph/PlottableProperty.ts index 2c4c906..60875fe 100644 --- a/src/screen-name/graph/PlottableProperty.ts +++ b/src/screen-name/graph/PlottableProperty.ts @@ -5,28 +5,19 @@ import type { TReadOnlyProperty } from "scenerystack/axon"; -/** - * A data point from sub-step simulation data. - * This is a flexible type that can hold any numeric data keyed by string names. - */ -export type SubStepDataPoint = Record; - export type PlottableProperty = { // The name to display in the selector (can be a string or a localized string property) name: string | TReadOnlyProperty; // The property to read values from. - // Required when subStepAccessor is absent; may be omitted when subStepAccessor - // covers all usage paths (e.g. kinematic variables that are always pushed via - // addDataPointsFromSubSteps rather than polled with addDataPoint). + // Required when accessor is absent. property?: TReadOnlyProperty; // Optional unit string for axis label (e.g., "m", "m/s", "J") // Can be a static string or a dynamic property for units that depend on calibration unit?: string | TReadOnlyProperty; - // Optional accessor for extracting this property's value from sub-step data. - // When provided, high-resolution sub-step data is used for smooth phase-space plots. - // When absent, falls back to the current property value. - subStepAccessor?: (point: SubStepDataPoint) => number; + // Optional accessor for extracting this property's value from a data point record. + // When provided, data is extracted from the record; when absent, falls back to property.value. + accessor?: (point: Record) => number; }; diff --git a/src/screen-name/view/KinematicsGraphNode.ts b/src/screen-name/view/KinematicsGraphNode.ts index 33bf616..be52367 100644 --- a/src/screen-name/view/KinematicsGraphNode.ts +++ b/src/screen-name/view/KinematicsGraphNode.ts @@ -10,10 +10,7 @@ import { HBox, Node, Text, VBox } from "scenerystack/scenery"; import { PhetFont } from "scenerystack/scenery-phet"; import { ComboBox, type ComboBoxItem } from "scenerystack/sun"; import ConfigurableGraph from "../graph/ConfigurableGraph.js"; -import type { - PlottableProperty, - SubStepDataPoint, -} from "../graph/PlottableProperty.js"; +import type { PlottableProperty } from "../graph/PlottableProperty.js"; import type { SimModel } from "../model/SimModel.js"; // Graph dimensions @@ -21,21 +18,12 @@ const GRAPH_WIDTH = 300; const GRAPH_HEIGHT = 200; const MAX_DATA_POINTS = 5000; -/** - * Creates a PlottableProperty for a kinematic variable driven entirely by - * subStepAccessor. No backing Property is needed because KinematicsGraphNode - * always feeds data via addDataPointsFromSubSteps rather than addDataPoint. - */ function createPlottableProperty( name: string, unit: string | TReadOnlyProperty, - accessor: (point: SubStepDataPoint) => number, + accessor: (point: Record) => number, ): PlottableProperty { - return { - name, - unit, - subStepAccessor: accessor, - }; + return { name, unit, accessor }; } export class KinematicsGraphNode extends VBox { @@ -282,8 +270,7 @@ export class KinematicsGraphNode extends VBox { if (!trackData || trackData.points.length === 0) return; - // Convert kinematic points to SubStepDataPoint format for the graph - const subStepData: SubStepDataPoint[] = trackData.points.map((pt) => ({ + const dataPoints = trackData.points.map((pt) => ({ t: pt.time, x: pt.x, y: pt.y, @@ -295,8 +282,8 @@ export class KinematicsGraphNode extends VBox { aMag: pt.accelerationMagnitude ?? Number.NaN, })); - if (subStepData.length > 0) { - this.graph.addDataPointsFromSubSteps(subStepData); + if (dataPoints.length > 0) { + this.graph.addDataPoints(dataPoints); } } }