diff --git a/src/i18n/StringManager.ts b/src/i18n/StringManager.ts index 1480f82..743ed62 100644 --- a/src/i18n/StringManager.ts +++ b/src/i18n/StringManager.ts @@ -190,12 +190,21 @@ export class StringManager { simulationStringProperty: ReadOnlyProperty; enableAutoTrackingStringProperty: ReadOnlyProperty; enableAutoTrackingDescriptionStringProperty: ReadOnlyProperty; + showVelocityStringProperty: ReadOnlyProperty; + showVelocityDescriptionStringProperty: ReadOnlyProperty; + showAccelerationStringProperty: ReadOnlyProperty; + showAccelerationDescriptionStringProperty: ReadOnlyProperty; } { return { simulationStringProperty: this.stringProperties.preferences.simulationStringProperty, enableAutoTrackingStringProperty: this.stringProperties.preferences.enableAutoTrackingStringProperty, enableAutoTrackingDescriptionStringProperty: this.stringProperties.preferences.enableAutoTrackingDescriptionStringProperty, + showVelocityStringProperty: this.stringProperties.preferences.showVelocityStringProperty, + showVelocityDescriptionStringProperty: this.stringProperties.preferences.showVelocityDescriptionStringProperty, + showAccelerationStringProperty: this.stringProperties.preferences.showAccelerationStringProperty, + showAccelerationDescriptionStringProperty: + this.stringProperties.preferences.showAccelerationDescriptionStringProperty, }; } diff --git a/src/i18n/strings_en.json b/src/i18n/strings_en.json index 4493df8..386eafc 100644 --- a/src/i18n/strings_en.json +++ b/src/i18n/strings_en.json @@ -52,7 +52,11 @@ "preferences": { "simulation": "Simulation", "enableAutoTracking": "Enable Auto-Tracking", - "enableAutoTrackingDescription": "When enabled, shows the auto-tracking checkbox in the control panel, allowing OpenCV-based automatic object tracking. When disabled, the checkbox is hidden and only manual digitizing is available." + "enableAutoTrackingDescription": "When enabled, shows the auto-tracking checkbox in the control panel, allowing OpenCV-based automatic object tracking. When disabled, the checkbox is hidden and only manual digitizing is available.", + "showVelocity": "Show Velocity", + "showVelocityDescription": "When enabled, velocity components (vx, vy, speed) are available as choices on the kinematics graph axes.", + "showAcceleration": "Show Acceleration", + "showAccelerationDescription": "When enabled, acceleration components (ax, ay, |a|) are available as choices on the kinematics graph axes." }, "autoTracker": { "dragToSelect": "Drag on video to select object to track" diff --git a/src/i18n/strings_fr.json b/src/i18n/strings_fr.json index bc1bd1a..7fa1739 100644 --- a/src/i18n/strings_fr.json +++ b/src/i18n/strings_fr.json @@ -52,7 +52,11 @@ "preferences": { "simulation": "Simulation", "enableAutoTracking": "Activer le suivi automatique", - "enableAutoTrackingDescription": "Lorsque activé, affiche la case à cocher du suivi automatique dans le panneau de contrôle, permettant le suivi automatique d'objets basé sur OpenCV. Lorsque désactivé, la case est cachée et seule la numérisation manuelle est disponible." + "enableAutoTrackingDescription": "Lorsque activé, affiche la case à cocher du suivi automatique dans le panneau de contrôle, permettant le suivi automatique d'objets basé sur OpenCV. Lorsque désactivé, la case est cachée et seule la numérisation manuelle est disponible.", + "showVelocity": "Afficher la vitesse", + "showVelocityDescription": "Lorsque activé, les composantes de vitesse (vx, vy, rapidité) sont disponibles comme choix sur les axes du graphique cinématique.", + "showAcceleration": "Afficher l'accélération", + "showAccelerationDescription": "Lorsque activé, les composantes d'accélération (ax, ay, |a|) sont disponibles comme choix sur les axes du graphique cinématique." }, "autoTracker": { "dragToSelect": "Faire glisser sur la vidéo pour sélectionner l'objet à suivre" diff --git a/src/preferences/TrackLabPreferencesModel.ts b/src/preferences/TrackLabPreferencesModel.ts index ea03bad..1b19961 100644 --- a/src/preferences/TrackLabPreferencesModel.ts +++ b/src/preferences/TrackLabPreferencesModel.ts @@ -2,7 +2,8 @@ * TrackLabPreferencesModel - Model for trackLab simulation preferences. * * Manages user preferences for the trackLab simulation, including whether - * the auto-tracking feature is enabled. + * the auto-tracking feature is enabled and which kinematic quantities appear + * on the graph axes. */ import { BooleanProperty } from "scenerystack/axon"; @@ -15,12 +16,28 @@ export class TrackLabPreferencesModel { */ public readonly enableAutoTrackingProperty: BooleanProperty; + /** + * Whether velocity quantities (vx, vy, speed) appear in the graph axis selectors. + */ + public readonly showVelocityInGraphProperty: BooleanProperty; + + /** + * Whether acceleration quantities (ax, ay, |a|) appear in the graph axis selectors. + */ + public readonly showAccelerationInGraphProperty: BooleanProperty; + public constructor() { // By default, auto-tracking checkbox is hidden this.enableAutoTrackingProperty = new BooleanProperty(false); + + // By default, velocity and acceleration are shown on the graph + this.showVelocityInGraphProperty = new BooleanProperty(true); + this.showAccelerationInGraphProperty = new BooleanProperty(false); } public reset(): void { this.enableAutoTrackingProperty.reset(); + this.showVelocityInGraphProperty.reset(); + this.showAccelerationInGraphProperty.reset(); } } diff --git a/src/preferences/TrackLabPreferencesNode.ts b/src/preferences/TrackLabPreferencesNode.ts index 9bcd4ee..646f819 100644 --- a/src/preferences/TrackLabPreferencesNode.ts +++ b/src/preferences/TrackLabPreferencesNode.ts @@ -2,7 +2,10 @@ * TrackLabPreferencesNode - Custom preferences UI panel for the trackLab simulation. * * Renders the simulation-specific preferences content shown in the Preferences dialog. - * Currently includes a checkbox to enable/disable the auto-tracking feature. + * Includes checkboxes to: + * - Enable/disable the auto-tracking feature + * - Show/hide velocity quantities on the kinematics graph + * - Show/hide acceleration quantities on the kinematics graph */ import { HStrut, Text, VBox } from "scenerystack/scenery"; @@ -46,10 +49,58 @@ export class TrackLabPreferencesNode extends VBox { }, ); + const showVelocityCheckbox = new Checkbox( + preferencesModel.showVelocityInGraphProperty, + new VBox({ + align: "left", + spacing: 2, + children: [ + new Text(prefStrings.showVelocityStringProperty, { + font: new PhetFont(14), + fill: TrackLabColors.preferencesTextProperty, + }), + new Text(prefStrings.showVelocityDescriptionStringProperty, { + font: new PhetFont(11), + fill: TrackLabColors.preferencesTextSecondaryProperty, + maxWidth: 500, + }), + ], + }), + { + checkboxColor: TrackLabColors.checkboxColorProperty, + checkboxColorBackground: TrackLabColors.checkboxColorBackgroundProperty, + spacing: 8, + }, + ); + + const showAccelerationCheckbox = new Checkbox( + preferencesModel.showAccelerationInGraphProperty, + new VBox({ + align: "left", + spacing: 2, + children: [ + new Text(prefStrings.showAccelerationStringProperty, { + font: new PhetFont(14), + fill: TrackLabColors.preferencesTextProperty, + }), + new Text(prefStrings.showAccelerationDescriptionStringProperty, { + font: new PhetFont(11), + fill: TrackLabColors.preferencesTextSecondaryProperty, + maxWidth: 500, + }), + ], + }), + { + checkboxColor: TrackLabColors.checkboxColorProperty, + checkboxColorBackground: TrackLabColors.checkboxColorBackgroundProperty, + spacing: 8, + }, + ); + super({ align: "left", spacing: 12, - children: [header, new HStrut(600), enableAutoTrackingCheckbox], + children: [header, new HStrut(600), enableAutoTrackingCheckbox, showVelocityCheckbox, showAccelerationCheckbox], }); } } diff --git a/src/screen-name/graph/ConfigurableGraph.ts b/src/screen-name/graph/ConfigurableGraph.ts index 18bb755..cb489fa 100644 --- a/src/screen-name/graph/ConfigurableGraph.ts +++ b/src/screen-name/graph/ConfigurableGraph.ts @@ -119,6 +119,7 @@ export default class ConfigurableGraph extends Node { // Module instances private readonly dataManager: GraphDataManager; private readonly interactionHandler: GraphInteractionHandler; + private readonly controlsPanel: GraphControlsPanel; // Title panel with combo boxes (needs to be on top of header bar) private readonly titlePanel: Node; @@ -300,12 +301,13 @@ export default class ConfigurableGraph extends Node { }); // Create controls panel helper - const controlsPanel = new GraphControlsPanel( + this.controlsPanel = new GraphControlsPanel( availableProperties, this.xPropertyProperty, this.yPropertyProperty, this.graphWidth, ); + const controlsPanel = this.controlsPanel; // Create title panel with combo boxes for axis selection // Note: titlePanel is added to 'this' (not graphContentNode) after headerBar @@ -494,6 +496,7 @@ export default class ConfigurableGraph extends Node { this.graphVisibleProperty.unlink(graphVisibleListener); isDraggingProperty.unlink(isDraggingListener); isResizingProperty.unlink(isResizingListener); + this.controlsPanel.dispose(); this.xPropertyProperty.dispose(); this.yPropertyProperty.dispose(); this.graphVisibleProperty.dispose(); @@ -654,6 +657,32 @@ export default class ConfigurableGraph extends Node { return this.graphVisibleProperty; } + /** + * Update the set of quantities available in the axis-selector combo boxes. + * + * If the currently selected X or Y property is no longer in the new list it + * is reset to the first available property before the combo boxes are rebuilt. + * + * @param newProperties - The replacement list of plottable properties. + */ + public setAvailableProperties(newProperties: PlottableProperty[]): void { + if (newProperties.length === 0) { + return; + } + + const first = newProperties[0]!; + + // Reset selections that are no longer available. + if (!newProperties.includes(this.xPropertyProperty.value)) { + this.xPropertyProperty.value = first; + } + if (!newProperties.includes(this.yPropertyProperty.value)) { + this.yPropertyProperty.value = first; + } + + this.controlsPanel.rebuildComboBoxes(newProperties); + } + public override dispose(): void { this.disposeConfigurableGraph(); super.dispose(); diff --git a/src/screen-name/graph/GraphControlsPanel.ts b/src/screen-name/graph/GraphControlsPanel.ts index 17b7ef5..6dd8063 100644 --- a/src/screen-name/graph/GraphControlsPanel.ts +++ b/src/screen-name/graph/GraphControlsPanel.ts @@ -28,11 +28,20 @@ const HEADER_LINE_WIDTH = 2; const HEADER_DARKEN_FACTOR = 0.1; export default class GraphControlsPanel { - private readonly availableProperties: PlottableProperty[]; + private availableProperties: PlottableProperty[]; private readonly xPropertyProperty: Property; private readonly yPropertyProperty: Property; private readonly graphWidth: number; + // Stored references needed to rebuild combo boxes without recreating the whole panel. + private listParent: Node | null = null; + private titleHBox: HBox | null = null; + private xComboBox: ComboBox | null = null; + private yComboBox: ComboBox | null = null; + private leftParenNode: Text | null = null; + private vsTextNode: Text | null = null; + private rightParenNode: Text | null = null; + public constructor( availableProperties: PlottableProperty[], xPropertyProperty: Property, @@ -62,9 +71,9 @@ export default class GraphControlsPanel { } /** - * Create title panel with "(Y vs X)" format where Y and X are combo boxes + * Build ComboBox items for the X-axis selector from the current available properties. */ - public createTitlePanel(listParent: Node): Node { + private buildXComboBox(): ComboBox { const xItems = this.availableProperties.map((prop) => ({ value: prop, createNode: () => @@ -75,7 +84,7 @@ export default class GraphControlsPanel { tandemName: `${this.sanitizeTandemName(prop.name)}Item`, })); - const xComboBox = new ComboBox(this.xPropertyProperty, xItems, listParent, { + return new ComboBox(this.xPropertyProperty, xItems, this.listParent!, { cornerRadius: COMBO_BOX_CORNER_RADIUS, xMargin: COMBO_BOX_X_MARGIN, yMargin: COMBO_BOX_Y_MARGIN, @@ -85,7 +94,12 @@ export default class GraphControlsPanel { listStroke: TrackLabColors.controlPanelStrokeProperty, highlightFill: TrackLabColors.controlPanelStrokeProperty, }); + } + /** + * Build ComboBox items for the Y-axis selector from the current available properties. + */ + private buildYComboBox(): ComboBox { const yItems = this.availableProperties.map((prop) => ({ value: prop, createNode: () => @@ -96,7 +110,7 @@ export default class GraphControlsPanel { tandemName: `${this.sanitizeTandemName(prop.name)}Item`, })); - const yComboBox = new ComboBox(this.yPropertyProperty, yItems, listParent, { + return new ComboBox(this.yPropertyProperty, yItems, this.listParent!, { cornerRadius: COMBO_BOX_CORNER_RADIUS, xMargin: COMBO_BOX_X_MARGIN, yMargin: COMBO_BOX_Y_MARGIN, @@ -106,14 +120,26 @@ export default class GraphControlsPanel { listStroke: TrackLabColors.controlPanelStrokeProperty, highlightFill: TrackLabColors.controlPanelStrokeProperty, }); + } + + /** + * Create title panel with "(Y vs X)" format where Y and X are combo boxes. + * Stores internal references so that `rebuildComboBoxes` can update the panel + * in place when the set of available properties changes. + */ + public createTitlePanel(listParent: Node): Node { + this.listParent = listParent; + + this.xComboBox = this.buildXComboBox(); + this.yComboBox = this.buildYComboBox(); // Create title in format "(Y vs X)" - const leftParen = new Text("(", { + this.leftParenNode = new Text("(", { font: TITLE_FONT, fill: TrackLabColors.textProperty, }); - const vsText = new Text( + this.vsTextNode = new Text( new DerivedProperty([StringManager.getInstance().getControls().graphVsStringProperty], (vs: string) => ` ${vs} `), { font: TITLE_FONT, @@ -121,17 +147,61 @@ export default class GraphControlsPanel { }, ); - const rightParen = new Text(")", { + this.rightParenNode = new Text(")", { font: TITLE_FONT, fill: TrackLabColors.textProperty, }); // Arrange in horizontal layout: (Y vs X) - return new HBox({ + this.titleHBox = new HBox({ spacing: TITLE_SPACING, align: "center", - children: [leftParen, yComboBox, vsText, xComboBox, rightParen], + children: [this.leftParenNode, this.yComboBox, this.vsTextNode, this.xComboBox, this.rightParenNode], }); + + return this.titleHBox; + } + + /** + * Swap out the combo boxes to reflect a new set of available properties. + * The title HBox is updated in place — no parent-level re-parenting needed. + * + * Must be called after `createTitlePanel`. + */ + public rebuildComboBoxes(newProperties: PlottableProperty[]): void { + if (!this.titleHBox || !this.xComboBox || !this.yComboBox) { + return; + } + + this.availableProperties = newProperties; + + const oldX = this.xComboBox; + const oldY = this.yComboBox; + + this.xComboBox = this.buildXComboBox(); + this.yComboBox = this.buildYComboBox(); + + // Update the HBox children with the new combo boxes, keeping the static text nodes. + this.titleHBox.children = [ + this.leftParenNode!, + this.yComboBox, + this.vsTextNode!, + this.xComboBox, + this.rightParenNode!, + ]; + + // Dispose old combo boxes AFTER swapping children to avoid dangling references. + oldX.dispose(); + oldY.dispose(); + } + + /** + * Dispose the combo boxes owned by this panel. + * Should be called when the parent ConfigurableGraph is disposed. + */ + public dispose(): void { + this.xComboBox?.dispose(); + this.yComboBox?.dispose(); } /** diff --git a/src/screen-name/graph/kinematics-plottable-properties.ts b/src/screen-name/graph/kinematics-plottable-properties.ts index 43af43a..901e396 100644 --- a/src/screen-name/graph/kinematics-plottable-properties.ts +++ b/src/screen-name/graph/kinematics-plottable-properties.ts @@ -34,39 +34,59 @@ function createPlottableProperty( } /** - * Build the ordered list of kinematics quantities available for axis selection. - * Called once per `KinematicsGraphNode` instance; the result is passed directly - * to `ConfigurableGraph`. + * Categorised groups of kinematics quantities available for axis selection. + * Each category can be independently included or excluded based on user preferences. + */ +export type KinematicsPlottableGroups = { + /** Time — always available */ + time: PlottableProperty[]; + /** Position components — always available */ + position: PlottableProperty[]; + /** Velocity components — shown when the "show velocity" preference is on */ + velocity: PlottableProperty[]; + /** Acceleration components — shown when the "show acceleration" preference is on */ + acceleration: PlottableProperty[]; +}; + +/** + * Build the categorised groups of kinematics quantities available for axis selection. + * Called once per `KinematicsGraphNode` instance. * * @param model - Provides the reactive unit-string properties so that axis * labels update automatically when the user changes the calibration unit. */ -export function buildKinematicsPlottableProperties(model: SimModel): PlottableProperty[] { - return [ +export function buildKinematicsPlottableGroups(model: SimModel): KinematicsPlottableGroups { + return { // ── Time ────────────────────────────────────────────────────────────── // biome-ignore lint/complexity/useLiteralKeys: TypeScript requires bracket notation - createPlottableProperty("t", "s", (pt) => pt["t"] ?? 0), + time: [createPlottableProperty("t", "s", (pt) => pt["t"] ?? 0)], // ── Position ────────────────────────────────────────────────────────── - // biome-ignore lint/complexity/useLiteralKeys: TypeScript requires bracket notation - createPlottableProperty("x", model.distanceUnitProperty, (pt) => pt["x"] ?? 0), - // biome-ignore lint/complexity/useLiteralKeys: TypeScript requires bracket notation - createPlottableProperty("y", model.distanceUnitProperty, (pt) => pt["y"] ?? 0), + position: [ + // biome-ignore lint/complexity/useLiteralKeys: TypeScript requires bracket notation + createPlottableProperty("x", model.distanceUnitProperty, (pt) => pt["x"] ?? 0), + // biome-ignore lint/complexity/useLiteralKeys: TypeScript requires bracket notation + createPlottableProperty("y", model.distanceUnitProperty, (pt) => pt["y"] ?? 0), + ], // ── Velocity ────────────────────────────────────────────────────────── - // biome-ignore lint/complexity/useLiteralKeys: TypeScript requires bracket notation - createPlottableProperty("vx", model.velocityUnitProperty, (pt) => pt["vx"] ?? 0), - // biome-ignore lint/complexity/useLiteralKeys: TypeScript requires bracket notation - createPlottableProperty("vy", model.velocityUnitProperty, (pt) => pt["vy"] ?? 0), - // biome-ignore lint/complexity/useLiteralKeys: TypeScript requires bracket notation - createPlottableProperty("speed", model.velocityUnitProperty, (pt) => pt["speed"] ?? 0), + velocity: [ + // biome-ignore lint/complexity/useLiteralKeys: TypeScript requires bracket notation + createPlottableProperty("vx", model.velocityUnitProperty, (pt) => pt["vx"] ?? 0), + // biome-ignore lint/complexity/useLiteralKeys: TypeScript requires bracket notation + createPlottableProperty("vy", model.velocityUnitProperty, (pt) => pt["vy"] ?? 0), + // biome-ignore lint/complexity/useLiteralKeys: TypeScript requires bracket notation + createPlottableProperty("speed", model.velocityUnitProperty, (pt) => pt["speed"] ?? 0), + ], // ── Acceleration ────────────────────────────────────────────────────── - // biome-ignore lint/complexity/useLiteralKeys: TypeScript requires bracket notation - createPlottableProperty("ax", model.accelerationUnitProperty, (pt) => pt["ax"] ?? 0), - // biome-ignore lint/complexity/useLiteralKeys: TypeScript requires bracket notation - createPlottableProperty("ay", model.accelerationUnitProperty, (pt) => pt["ay"] ?? 0), - // biome-ignore lint/complexity/useLiteralKeys: TypeScript requires bracket notation - createPlottableProperty("|a|", model.accelerationUnitProperty, (pt) => pt["aMag"] ?? 0), - ]; + acceleration: [ + // biome-ignore lint/complexity/useLiteralKeys: TypeScript requires bracket notation + createPlottableProperty("ax", model.accelerationUnitProperty, (pt) => pt["ax"] ?? 0), + // biome-ignore lint/complexity/useLiteralKeys: TypeScript requires bracket notation + createPlottableProperty("ay", model.accelerationUnitProperty, (pt) => pt["ay"] ?? 0), + // biome-ignore lint/complexity/useLiteralKeys: TypeScript requires bracket notation + createPlottableProperty("|a|", model.accelerationUnitProperty, (pt) => pt["aMag"] ?? 0), + ], + }; } diff --git a/src/screen-name/view/KinematicsGraphNode.ts b/src/screen-name/view/KinematicsGraphNode.ts index 4aff91d..3a97cb0 100644 --- a/src/screen-name/view/KinematicsGraphNode.ts +++ b/src/screen-name/view/KinematicsGraphNode.ts @@ -3,14 +3,17 @@ * * A configurable graph that displays kinematic data from tracks. * Users can select which variables to plot on each axis (t, x, y, vx, vy, speed, ax, ay, |a|). + * The velocity and acceleration groups can be hidden via user preferences. */ import { Property } from "scenerystack/axon"; import { HBox, Node, Text, VBox } from "scenerystack/scenery"; import { PhetFont } from "scenerystack/scenery-phet"; import { ComboBox, type ComboBoxItem } from "scenerystack/sun"; +import type { TrackLabPreferencesModel } from "../../preferences/TrackLabPreferencesModel.js"; import ConfigurableGraph from "../graph/ConfigurableGraph.js"; -import { buildKinematicsPlottableProperties } from "../graph/kinematics-plottable-properties.js"; +import { buildKinematicsPlottableGroups } from "../graph/kinematics-plottable-properties.js"; +import type { PlottableProperty } from "../graph/PlottableProperty.js"; import type { SimModel } from "../model/SimModel.js"; // Graph dimensions @@ -27,7 +30,7 @@ export class KinematicsGraphNode extends VBox { private currentComboBox: ComboBox | null = null; private readonly disposeKinematicsGraph: () => void; - public constructor(model: SimModel, listParent: Node) { + public constructor(model: SimModel, listParent: Node, preferencesModel: TrackLabPreferencesModel) { super({ spacing: 8, align: "left", @@ -37,13 +40,28 @@ export class KinematicsGraphNode extends VBox { this.listParent = listParent; this.selectedTrackProperty = new Property(null); - // Build the registry of plottable quantities from the canonical definition. - // To add a new quantity, edit kinematics-plottable-properties.ts — not here. - const plottableProperties = buildKinematicsPlottableProperties(model); + // Build the categorised groups of plottable quantities. + // Object references are stable — the same PlottableProperty instances are + // reused across filter updates so identity checks in setAvailableProperties work. + const groups = buildKinematicsPlottableGroups(model); + + /** Compute the current filtered list from preference state. */ + const getFilteredProperties = (): PlottableProperty[] => { + const result: PlottableProperty[] = [...groups.time, ...groups.position]; + if (preferencesModel.showVelocityInGraphProperty.value) { + result.push(...groups.velocity); + } + if (preferencesModel.showAccelerationInGraphProperty.value) { + result.push(...groups.acceleration); + } + return result; + }; + + const initialFiltered = getFilteredProperties(); // Default: plot y vs x (trajectory) - const initialXProperty = plottableProperties[1]; - const initialYProperty = plottableProperties[2]; + const initialXProperty = groups.position[0]; + const initialYProperty = groups.position[1]; if (!(initialXProperty && initialYProperty)) { throw new Error("Failed to initialize plottable properties"); @@ -52,7 +70,7 @@ export class KinematicsGraphNode extends VBox { // Create the configurable graph // Pass 'this' (KinematicsGraphNode) as dragTargetNode so dragging moves the whole container this.graph = new ConfigurableGraph( - plottableProperties, + initialFiltered, initialXProperty, initialYProperty, GRAPH_WIDTH, @@ -112,6 +130,17 @@ export class KinematicsGraphNode extends VBox { }; model.distanceUnitProperty.lazyLink(distanceUnitListener); + // When preferences change, rebuild the available properties in the graph selectors. + const velocityPrefListener = () => { + this.graph.setAvailableProperties(getFilteredProperties()); + }; + preferencesModel.showVelocityInGraphProperty.lazyLink(velocityPrefListener); + + const accelerationPrefListener = () => { + this.graph.setAvailableProperties(getFilteredProperties()); + }; + preferencesModel.showAccelerationInGraphProperty.lazyLink(accelerationPrefListener); + // Layout this.children = [this.trackSelectorContainer, this.graph]; @@ -123,6 +152,8 @@ export class KinematicsGraphNode extends VBox { this.graph.getXPropertyProperty().unlink(xPropertyListener); this.graph.getYPropertyProperty().unlink(yPropertyListener); model.distanceUnitProperty.unlink(distanceUnitListener); + preferencesModel.showVelocityInGraphProperty.unlink(velocityPrefListener); + preferencesModel.showAccelerationInGraphProperty.unlink(accelerationPrefListener); if (this.currentComboBox) { this.currentComboBox.dispose(); } diff --git a/src/screen-name/view/SimScreenView.ts b/src/screen-name/view/SimScreenView.ts index 0658ef7..fd8b5f4 100644 --- a/src/screen-name/view/SimScreenView.ts +++ b/src/screen-name/view/SimScreenView.ts @@ -94,7 +94,7 @@ export class SimScreenView extends ScreenView { this.addChild(resetAllButton); // ── Kinematics graph (bottom right, above reset all) ───────────────── - const kinematicsGraph = new KinematicsGraphNode(model, this); + const kinematicsGraph = new KinematicsGraphNode(model, this, trackLabPreferences); this.addChild(kinematicsGraph); kinematicsGraph.right = this.layoutBounds.maxX + 45; kinematicsGraph.bottom = resetAllButton.top - 150;