perf(tracks): bound playback rendering work
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import type { TrackPlaybackResponse } from '../../api/types';
|
||||
import { formatDuration, sampledEventIndex, trackCsv } from './track';
|
||||
import { buildTrackRailSelection, formatDuration, sampledEventIndex, trackCsv, trackPlaybackInterval } from './track';
|
||||
|
||||
describe('track domain', () => {
|
||||
it('formats durations and maps original event indices to sampled points', () => {
|
||||
@@ -9,6 +9,26 @@ describe('track domain', () => {
|
||||
expect(sampledEventIndex({ index: 50, sampledIndex: 7 } as never, 11, 101)).toBe(7);
|
||||
});
|
||||
|
||||
it('precomputes sparse rail highlights and bounds the playback cadence', () => {
|
||||
const playback = {
|
||||
points: Array.from({ length: 8 }, (_, index) => ({ longitude: index, latitude: index })),
|
||||
stops: [{ sampledIndex: 3 }],
|
||||
events: [{ index: 50 }, { index: 75, sampledIndex: 6 }],
|
||||
summary: { pointCount: 101 }
|
||||
} as TrackPlaybackResponse;
|
||||
const selection = buildTrackRailSelection(playback);
|
||||
|
||||
expect(selection.stopIndexesByPoint[1]).toBeUndefined();
|
||||
expect(selection.stopIndexesByPoint[2]).toEqual([0]);
|
||||
expect(selection.stopIndexesByPoint[3]).toEqual([0]);
|
||||
expect(selection.stopIndexesByPoint[4]).toEqual([0]);
|
||||
expect(selection.eventIndexesByPoint[4]).toEqual([0]);
|
||||
expect(selection.eventIndexesByPoint[6]).toEqual([1]);
|
||||
expect(trackPlaybackInterval(1)).toBe(300);
|
||||
expect(trackPlaybackInterval(4)).toBe(75);
|
||||
expect(trackPlaybackInterval(100)).toBe(55);
|
||||
});
|
||||
|
||||
it('exports the current result with UTF-8 BOM and escaped values', () => {
|
||||
const track = {
|
||||
plate: '粤A,001', vin: 'VIN', summary: { startTime: '2026-07-03 10:00:00' },
|
||||
|
||||
@@ -16,6 +16,30 @@ export function sampledEventIndex(event: TrackPlaybackEvent, sampledCount: numbe
|
||||
return Math.max(0, Math.min(sampledCount - 1, Math.round(event.index * (sampledCount - 1) / (originalCount - 1))));
|
||||
}
|
||||
|
||||
export function trackPlaybackInterval(speed: number) {
|
||||
return Math.max(55, 300 / Math.max(0.5, speed));
|
||||
}
|
||||
|
||||
export function buildTrackRailSelection(track?: TrackPlaybackResponse) {
|
||||
const pointCount = track?.points.length ?? 0;
|
||||
const stopIndexesByPoint: Array<number[] | undefined> = new Array(pointCount);
|
||||
const eventIndexesByPoint: Array<number[] | undefined> = new Array(pointCount);
|
||||
if (!track || pointCount === 0) return { stopIndexesByPoint, eventIndexesByPoint };
|
||||
|
||||
track.stops.forEach((stop, stopIndex) => {
|
||||
for (let offset = -1; offset <= 1; offset += 1) {
|
||||
const pointIndex = stop.sampledIndex + offset;
|
||||
if (pointIndex < 0 || pointIndex >= pointCount) continue;
|
||||
(stopIndexesByPoint[pointIndex] ??= []).push(stopIndex);
|
||||
}
|
||||
});
|
||||
track.events.forEach((event, eventIndex) => {
|
||||
const pointIndex = sampledEventIndex(event, pointCount, track.summary.pointCount);
|
||||
(eventIndexesByPoint[pointIndex] ??= []).push(eventIndex);
|
||||
});
|
||||
return { stopIndexesByPoint, eventIndexesByPoint };
|
||||
}
|
||||
|
||||
function csvCell(value: unknown) {
|
||||
const text = String(value ?? '');
|
||||
return /[",\n]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
|
||||
|
||||
Reference in New Issue
Block a user