aboutsummaryrefslogtreecommitdiff
path: root/web/frontend/climb-attempts-logic.ts
blob: 868545b300043d33f28a0e4b0ce31fd0bbda2c1c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

import type { ClimbAttemptPoint } from "./types.js";

// formatAttemptDuration renders an attempt duration as "mm:ss", or "h:mm:ss" past an hour.
export const formatAttemptDuration = (durationS: number): string => {
  const total = Math.max(0, Math.round(durationS));
  const seconds = total % 60;
  const minutes = Math.floor(total / 60) % 60;
  const hours = Math.floor(total / 3600);
  const two = (value: number): string => String(value).padStart(2, "0");
  return hours > 0 ? `${hours}:${two(minutes)}:${two(seconds)}` : `${minutes}:${two(seconds)}`;
};

// formatPower renders an average power in whole watts.
export const formatPower = (powerW: number): string => `${Math.round(powerW)} W`;

export interface AttemptsDomain {
  maxDurationS: number;
  maxPowerW: number;
}

// attemptsDomain returns zero-based axis bounds padded above the data; empty or degenerate data still yields a drawable unit domain.
export const attemptsDomain = (points: ClimbAttemptPoint[]): AttemptsDomain => {
  let maxDurationS = 0;
  let maxPowerW = 0;
  for (const point of points) {
    maxDurationS = Math.max(maxDurationS, point.durationS);
    maxPowerW = Math.max(maxPowerW, point.averagePowerW);
  }
  return { maxDurationS: maxDurationS * 1.08 || 1, maxPowerW: maxPowerW * 1.08 || 1 };
};