diff --git a/ui-manchette-with-spacetimechart/src/consts.ts b/ui-manchette-with-spacetimechart/src/consts.ts index 12a9a993f..5a7fbf617 100644 --- a/ui-manchette-with-spacetimechart/src/consts.ts +++ b/ui-manchette-with-spacetimechart/src/consts.ts @@ -2,9 +2,11 @@ export const BASE_WAYPOINT_HEIGHT = 32; export const INITIAL_WAYPOINT_LIST_HEIGHT = 521; export const INITIAL_SPACE_TIME_CHART_HEIGHT = INITIAL_WAYPOINT_LIST_HEIGHT + 40; -export const MIN_ZOOM_X = 1; -export const MAX_ZOOM_X = 10; -export const ZOOM_X_DELTA = 0.5; +export const MIN_ZOOM_MS_PER_PX = 600000; +export const MAX_ZOOM_MS_PER_PX = 625; +export const DEFAULT_ZOOM_MS_PER_PX = 7500; +export const MIN_ZOOM_X = 0; +export const MAX_ZOOM_X = 100; export const MIN_ZOOM_Y = 1; export const MAX_ZOOM_Y = 10.5; diff --git a/ui-manchette-with-spacetimechart/src/helpers.ts b/ui-manchette-with-spacetimechart/src/helpers.ts index 6ff6111dc..85763698e 100644 --- a/ui-manchette-with-spacetimechart/src/helpers.ts +++ b/ui-manchette-with-spacetimechart/src/helpers.ts @@ -3,12 +3,17 @@ import type { ProjectPathTrainResult, Waypoint, } from '@osrd-project/ui-manchette/dist/types'; -import type { - OperationalPoint, - SpaceTimeChartProps, -} from '@osrd-project/ui-spacetimechart/dist/lib/types'; - -import { BASE_WAYPOINT_HEIGHT, MAX_TIME_WINDOW, MAX_ZOOM_X, MIN_ZOOM_X } from './consts'; +import type { OperationalPoint } from '@osrd-project/ui-spacetimechart/dist/lib/types'; +import { clamp } from 'lodash'; + +import { + BASE_WAYPOINT_HEIGHT, + MAX_TIME_WINDOW, + MAX_ZOOM_MS_PER_PX, + MAX_ZOOM_X, + MIN_ZOOM_MS_PER_PX, + MIN_ZOOM_X, +} from './consts'; import { calcTotalDistance, getHeightWithoutLastWaypoint, msToS } from './utils'; type WaypointsOptions = { isProportional: boolean; yZoom: number; height: number }; @@ -154,17 +159,32 @@ export const getScales = ( ]; }; +export const zoomValueToTimeScale = (slider: number) => + MIN_ZOOM_MS_PER_PX * Math.pow(MAX_ZOOM_MS_PER_PX / MIN_ZOOM_MS_PER_PX, slider / 100); + +export const timeScaleToZoomValue = (timeScale: number) => + (100 * Math.log(timeScale / MIN_ZOOM_MS_PER_PX)) / + Math.log(MAX_ZOOM_MS_PER_PX / MIN_ZOOM_MS_PER_PX); + /** Zoom on X axis and center on the mouse position */ export const zoomX = ( currentXZoom: number, currentXOffset: number, - { delta, position: { x } }: Parameters>[0] + newXZoom: number, + position: number ) => { - const xZoom = Math.min(Math.max(currentXZoom * (1 + delta / 10), MIN_ZOOM_X), MAX_ZOOM_X); + const boundedXZoom = clamp(newXZoom, MIN_ZOOM_X, MAX_ZOOM_X); + const oldTimeScale = zoomValueToTimeScale(currentXZoom); + const newTimeScale = zoomValueToTimeScale(boundedXZoom); + // by default zooming expands the graph around timeOrigin + // changing the offset to timeOriginShift alone keeps the left border in place. + // subtracting centerShift keeps the center in place + // (xOffset = how many px the timeOrigin is shifted to the right) + const timeOriginShift = (currentXOffset * oldTimeScale) / newTimeScale; + const centerShift = (position * oldTimeScale - position * newTimeScale) / newTimeScale; + const newOffset = timeOriginShift - centerShift; return { - xZoom, - // Adjust zoom level relatively to the input delta value: - // These lines are here to center the zoom on the mouse position: - xOffset: x - ((x - currentXOffset) / currentXZoom) * xZoom, + xZoom: boundedXZoom, + xOffset: newOffset, }; }; diff --git a/ui-manchette-with-spacetimechart/src/hooks/useManchetteWithSpaceTimeChart.ts b/ui-manchette-with-spacetimechart/src/hooks/useManchetteWithSpaceTimeChart.ts index 2e09ebc33..899b91b4b 100644 --- a/ui-manchette-with-spacetimechart/src/hooks/useManchetteWithSpaceTimeChart.ts +++ b/ui-manchette-with-spacetimechart/src/hooks/useManchetteWithSpaceTimeChart.ts @@ -7,7 +7,13 @@ import type { } from '@osrd-project/ui-spacetimechart/dist/lib/types'; import usePaths from './usePaths'; -import { MAX_ZOOM_Y, MIN_ZOOM_Y, ZOOM_Y_DELTA, INITIAL_SPACE_TIME_CHART_HEIGHT } from '../consts'; +import { + MAX_ZOOM_Y, + MIN_ZOOM_Y, + ZOOM_Y_DELTA, + INITIAL_SPACE_TIME_CHART_HEIGHT, + DEFAULT_ZOOM_MS_PER_PX, +} from '../consts'; import { calcWaypointsToDisplay, computeTimeWindow, @@ -15,6 +21,8 @@ import { getScales, calcWaypointsHeight, zoomX, + zoomValueToTimeScale, + timeScaleToZoomValue, } from '../helpers'; import { getDiff } from '../utils/point'; @@ -39,7 +47,7 @@ const useManchettesWithSpaceTimeChart = ( ) => { const [isShiftPressed, setIsShiftPressed] = useState(false); const [state, setState] = useState({ - xZoom: 1, + xZoom: timeScaleToZoomValue(DEFAULT_ZOOM_MS_PER_PX), yZoom: 1, xOffset: 0, yOffset: 0, @@ -130,7 +138,6 @@ const useManchettesWithSpaceTimeChart = ( [operationalPointsWithPosition, height, isProportional, yZoom] ); - // Memoize manchetteProps separately const manchetteProps = useMemo( () => ({ waypoints: waypointWithHeight, @@ -144,21 +151,30 @@ const useManchettesWithSpaceTimeChart = ( [waypointWithHeight, zoomYIn, zoomYOut, resetZoom, toggleMode, yZoom, isProportional] ); - // Memoize spaceTimeChartProps separately + const handleXZoom = useCallback( + ( + newXZoom: number, + xPosition = (manchetteWithSpaceTimeChartContainer.current?.offsetWidth || 0) / 2 + ) => { + setState((prev) => ({ + ...prev, + ...zoomX(prev.xZoom, prev.xOffset, newXZoom, xPosition), + })); + }, + [manchetteWithSpaceTimeChartContainer] + ); + const spaceTimeChartProps = useMemo( () => ({ operationalPoints: operationalPointsWithPosition, spaceScales: computedScales, - timeScale: timeWindow / xZoom, + timeScale: zoomValueToTimeScale(xZoom), paths, xOffset, yOffset: -scrollPosition + 14, - onZoom: (payload: Parameters>[0]) => { + onZoom: ({ delta, position }: Parameters>[0]) => { if (isShiftPressed) { - setState((prev) => ({ - ...prev, - ...zoomX(prev.xZoom, prev.xOffset, payload), - })); + handleXZoom(xZoom + delta, position.x); } }, onPan: (payload: { @@ -195,7 +211,6 @@ const useManchettesWithSpaceTimeChart = ( [ operationalPointsWithPosition, computedScales, - timeWindow, xZoom, paths, xOffset, @@ -205,6 +220,7 @@ const useManchettesWithSpaceTimeChart = ( panning, yOffset, manchetteWithSpaceTimeChartContainer, + handleXZoom, ] ); @@ -213,8 +229,9 @@ const useManchettesWithSpaceTimeChart = ( manchetteProps, spaceTimeChartProps, handleScroll, + handleXZoom, }), - [manchetteProps, spaceTimeChartProps, handleScroll] + [manchetteProps, spaceTimeChartProps, handleScroll, handleXZoom] ); };