Skip to content

Commit

Permalink
charts: add weight property to operational points
Browse files Browse the repository at this point in the history
Signed-off-by: Theo Macron <[email protected]>
  • Loading branch information
Akctarus committed Jan 6, 2025
1 parent 5053663 commit 551586a
Show file tree
Hide file tree
Showing 12 changed files with 151 additions and 40 deletions.
22 changes: 22 additions & 0 deletions ui-manchette-with-spacetimechart/src/assets/sampleData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,132 +7,154 @@ export const SAMPLE_WAYPOINTS: Waypoint[] = [
position: 0,
name: 'Brest',
secondaryCode: 'BV',
weight: 100,
},
{
id: '2',
position: 3983000,
name: 'Le Rody',
secondaryCode: '00',
weight: 60,
},
{
id: '3',
position: 7349000,
name: 'Kerhuon',
secondaryCode: 'BV',
weight: 50,
},
{
id: '4',
position: 13882000,
name: 'La Forest',
secondaryCode: '00',
weight: 40,
},
{
id: '5',
position: 18756000,
name: 'Landerneau',
secondaryCode: 'BV',
weight: 30,
},
{
id: '6',
position: 23358000,
name: 'La Roche',
secondaryCode: '00',
weight: 35,
},
{
id: '7',
position: 33219000,
name: 'Landivisiau',
secondaryCode: '00',
weight: 70,
},
{
id: '8',
position: 40612000,
name: 'Guimiliau',
secondaryCode: '00',
weight: 40,
},
{
id: '9',
position: 44960000,
name: 'St-Thégonnec',
secondaryCode: '00',
weight: 50,
},
{
id: '10',
position: 50471000,
name: 'Pleyber-Christ',
secondaryCode: '00',
weight: 45,
},
{
id: '11',
position: 59554000,
name: 'Morlaix',
secondaryCode: 'BV',
weight: 20,
},
{
id: '12',
position: 68903000,
name: 'Plouigneau',
secondaryCode: '00',
weight: 15,
},
{
id: '13',
position: 83194000,
name: 'Plounérin',
secondaryCode: '00',
weight: 10,
},
{
id: '14',
position: 91392000,
name: 'Plouaret',
secondaryCode: '00',
weight: 0,
},
{
id: '15',
position: 102478000,
name: 'Belle-Isle-Bégard',
secondaryCode: '00',
weight: 60,
},
{
id: '16',
position: 117570000,
name: 'Guingamp',
secondaryCode: '00',
weight: 90,
},
{
id: '17',
position: 130463000,
name: 'Châtelaudren-Plouagat',
secondaryCode: 'BV',
weight: 20,
},
{
id: '18',
position: 137315000,
name: 'Plouvara-Plerneuf',
secondaryCode: '00',
weight: 40,
},
{
id: '19',
position: 141157000,
name: 'La Méaugon',
secondaryCode: '00',
weight: 45,
},
{
id: '20',
position: 147745000,
name: 'St-Brieuc',
secondaryCode: 'BV',
weight: 70,
},
{
id: '21',
position: 157361000,
name: 'Yffiniac',
secondaryCode: '00',
weight: 0,
},
{
id: '22',
position: 168056000,
name: 'Lamballe',
secondaryCode: 'BV',
weight: 100,
},
];

Expand Down
48 changes: 26 additions & 22 deletions ui-manchette-with-spacetimechart/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,37 +24,41 @@ export const calcWaypointsToDisplay = (

// For proportional display, we only display waypoints that do not overlap with
// the last displayed point:
const result: InteractiveWaypoint[] = [{ ...waypoints[0], display: true }];
const totalDistance = calcTotalDistance(waypoints);
const heightWithoutFinalWaypoint = getHeightWithoutLastWaypoint(height);
let lastDisplayedWaypoint = result[0];
const minSpace = BASE_WAYPOINT_HEIGHT / yZoom;

// We iterate through all points, and only add them if they don't collide
// with the last visible point:
for (let i = 1; i < waypoints.length; i++) {
const waypoint = waypoints[i];
const diff = waypoint.position - lastDisplayedWaypoint.position;
const display =
(diff / totalDistance) * heightWithoutFinalWaypoint * yZoom >= BASE_WAYPOINT_HEIGHT;

if (display) {
result.push({ ...waypoint, display });
lastDisplayedWaypoint = waypoint;
}
}
const sortedWaypointsByWeight = waypoints
.slice()
.sort((a, b) => (b.weight ?? 0) - (a.weight ?? 0));

const displayedWaypoints: { waypoint: Waypoint; posY: number }[] = [];

// In the end, to make sure the last point is visible, if it's not, we swap
// it with the last visible item:
const lastItem = result[result.length - 1];
if (!lastItem.display) {
const lastVisibleItem = result.findLast((waypoint) => waypoint.display);
if (lastVisibleItem) {
lastVisibleItem.display = false;
lastItem.display = true;
for (const waypoint of sortedWaypointsByWeight) {
// Calculation of proportional Y position for waypoint
const posY = (waypoint.position / totalDistance) * heightWithoutFinalWaypoint;

// Check if the waypoint can be displayed (sufficient space)
const hasSpace = !displayedWaypoints.some(
(displayedWaypoint) => Math.abs(posY - displayedWaypoint.posY) < minSpace
);

if (hasSpace) {
displayedWaypoints.push({ waypoint, posY });
}
}

return result;
// Reorder displayed waypoints in original position order
const displayedWaypointsSortedByPosition = displayedWaypoints
.sort((a, b) => a.waypoint.position - b.waypoint.position)
.map(({ waypoint }) => ({
...waypoint,
display: true,
}));

return displayedWaypointsSortedByPosition;
};

export const calcWaypointsHeight = (
Expand Down
4 changes: 4 additions & 0 deletions ui-manchette/src/stories/assets/sampleData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,27 @@ export const SAMPLE_WAYPOINTS: Waypoint[] = [
name: 'South_West_station',
secondaryCode: 'BV',
position: 0,
weight: 100,
},
{
id: 'Mid_West_station',
name: 'Mid_West_station',
secondaryCode: 'BV',
position: 13000000,
weight: 30,
},
{
id: 'Mid_East_station',
name: 'Mid_East_station',
secondaryCode: 'BV',
position: 27550000,
weight: 50,
},
{
id: 'North_East_station',
name: 'North_East_station',
secondaryCode: 'BV',
position: 47050000,
weight: 100,
},
];
1 change: 1 addition & 0 deletions ui-manchette/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export type Waypoint = {
position: number; // in mm
name?: string;
secondaryCode?: string;
weight?: number;
};

export type InteractiveWaypoint = Waypoint & {
Expand Down
2 changes: 1 addition & 1 deletion ui-speedspacechart/src/__tests__/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ describe('getSnappedStop', () => {
])('should find the correct snapped stop', (stopPositions, cursorX, expectedPos) => {
const stops = stopPositions.map((pos) => ({
position: { start: pos },
value: 'MyTestStop',
value: { name: 'MyTestStop' },
}));
const storeWithStops: Store = {
...store,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export const drawCursor = ({ ctx, width, height, store }: DrawFunctionParams) =>
snapToStop = true;
cursorPosition = snappedStop.position.start;
reticleX = positionToPosX(cursorPosition, maxPosition, width, ratioX, leftOffset);
stopText = snappedStop.value;
stopText = snappedStop.value.name;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export const drawSteps = ({ ctx, width, height, store }: DrawFunctionParams) =>
ctx.textAlign = 'left';

// Draw the text at the origin, since the context is already transformed
ctx.fillText(value!, 0, 0);
ctx.fillText(value!.name, 0, 0);
ctx.restore();
});

Expand Down
40 changes: 27 additions & 13 deletions ui-speedspacechart/src/components/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
MARGINS,
type LAYERS_SELECTION,
} from './const';
import type { LayerData, Store } from '../types/chartTypes';
import type { LayerData, OperationalPoints, Store } from '../types/chartTypes';

type SlopesValues = {
minGradient: number;
Expand Down Expand Up @@ -326,26 +326,40 @@ export const interpolate = (x1: number, y1: number, x2: number, y2: number, x: n
export const clamp = (value: number, min: number, max: number) =>
Math.min(Math.max(value, min), max);

/** Filter stops to avoid overlapping draw */
/** Filter stops to avoid overlapping, showing the most important stops first */
export const filterStops = (
stops: LayerData<string>[],
stops: LayerData<OperationalPoints>[],
ratioX: number,
width: number,
maxPosition: number,
minSpace = 8
) => {
let lastDisplayedPosX: number | null = null;
// Sort stops by decreasing weight to prioritize the heaviest ones
const sortedStopsByWeight = stops
.slice()
.sort((a, b) => (b.value.weight ?? 0) - (a.value.weight ?? 0));

const filteredStops = stops.filter(({ position }) => {
const posX = positionToPosX(position.start, maxPosition, width, ratioX);
if (lastDisplayedPosX !== null && Math.abs(posX - lastDisplayedPosX) < minSpace) {
return false;
const displayedStops: { stop: LayerData<OperationalPoints>; posX: number }[] = [];

for (const stop of sortedStopsByWeight) {
const posX = positionToPosX(stop.position.start, maxPosition, width, ratioX);

// Check if the stop can be displayed (enough space with other stops displayed)
const hasSpace = !displayedStops.some(
(displayedStop) => Math.abs(posX - displayedStop.posX) < minSpace
);

if (hasSpace) {
displayedStops.push({ stop, posX }); // Combine stop and posX into one structure
}
lastDisplayedPosX = posX;
return true;
});
}

// Reorder stops in initial position order for final display
const displayedStopsSortedByPosition = displayedStops
.sort((a, b) => a.stop.position.start - b.stop.position.start)
.map((waypoints) => waypoints.stop); // Extract only the stops for the final output

return filteredStops;
return displayedStopsSortedByPosition;
};

/** Compute the cursor position on the x-axis given its position on the canva */
Expand Down Expand Up @@ -374,7 +388,7 @@ export const getSnappedStop = (cursorX: number, width: number, store: Store) =>
const index = binarySearch(
filteredStops,
cursorPosition,
(element: LayerData<string>) => element.position.start
(element: LayerData<OperationalPoints>) => element.position.start
);

const nextIndex = Math.min(index + 1, filteredStops.length - 1);
Expand Down
Loading

0 comments on commit 551586a

Please sign in to comment.