utils.ts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import { IBlock } from '../training/types'
  2. import { calculateDuration } from '../training/utils'
  3. import { IExerciseItem } from './types'
  4. /**
  5. * Find the right exercise given a certain time.
  6. * @param exerciseList
  7. * @param time
  8. */
  9. export function getPosition(exerciseList: IExerciseItem[], time: number) {
  10. const index = exerciseList.findIndex(
  11. exercise => time < exercise.offset + exercise.duration
  12. )
  13. const previousExercise = index >= 1 ? exerciseList[index - 1] : null
  14. const currentExercise = index >= 0 ? exerciseList[index] : null
  15. const nextExercise =
  16. index >= 0 && index < exerciseList.length - 1
  17. ? exerciseList[index + 1]
  18. : null
  19. const values = {
  20. currentExercise,
  21. nextExercise,
  22. previousExercise,
  23. exerciseTime: 0
  24. }
  25. if (currentExercise !== null) {
  26. values.exerciseTime = Math.max(time - currentExercise.offset, 0)
  27. }
  28. return values
  29. }
  30. /**
  31. * Takes a (nested) block of exercises and returns a flat array of exercises.
  32. * @param blocks
  33. * @param initialOffset - used for recursive application
  34. */
  35. export function getExerciseList(
  36. blocks: IBlock[],
  37. initialOffset = 0,
  38. toplevelBlock: undefined | string = undefined
  39. ): IExerciseItem[] {
  40. let offset = initialOffset
  41. return blocks
  42. .map(block => {
  43. if (block.blocks) {
  44. const blockArray = []
  45. for (let i = 0; i < (block.repetitions || 1); i++) {
  46. const subBlocks = getExerciseList(
  47. block.blocks,
  48. offset,
  49. toplevelBlock || block.title
  50. )
  51. const lastItem = subBlocks[subBlocks.length - 1]
  52. offset = lastItem.offset + lastItem.duration
  53. if (block.rest) {
  54. if (lastItem.exercise === 'Rest') {
  55. lastItem.duration += block.rest
  56. offset += block.rest
  57. } else {
  58. subBlocks.push({
  59. exercise: 'Rest',
  60. toplevelBlock: toplevelBlock || block.title,
  61. duration: block.rest,
  62. offset
  63. })
  64. offset += block.rest
  65. }
  66. }
  67. blockArray.push(subBlocks)
  68. }
  69. return blockArray.flat()
  70. } else if (block.exercises) {
  71. const blockArray: IExerciseItem[] = []
  72. const newItem = {
  73. exercise: block.exercises
  74. .map(exercise =>
  75. exercise.repetitions > 1
  76. ? `${exercise.repetitions}x ${exercise.name}`
  77. : exercise.name
  78. )
  79. .join(' - '),
  80. duration: calculateDuration(block),
  81. video: block.video,
  82. description: block.description,
  83. toplevelBlock: toplevelBlock || block.title,
  84. offset
  85. }
  86. for (let i = 0; i < (block.repetitions || 1); i++) {
  87. blockArray.push({ ...newItem, offset })
  88. offset += newItem.duration
  89. }
  90. return blockArray.flat()
  91. }
  92. })
  93. .flat()
  94. }
  95. export function getTrainingTime(exercises: IExerciseItem[]) {
  96. return exercises.reduce(
  97. (accumulator, exercise) => accumulator + exercise.duration,
  98. 0
  99. )
  100. }
  101. export function polarToCartesian(
  102. centerX: number,
  103. centerY: number,
  104. radius: number,
  105. angleInDegrees: number
  106. ) {
  107. const angleInRadians = ((angleInDegrees - 90) * Math.PI) / 180.0
  108. return {
  109. x: centerX + radius * Math.cos(angleInRadians),
  110. y: centerY + radius * Math.sin(angleInRadians)
  111. }
  112. }
  113. export function describeArc(
  114. x: number,
  115. y: number,
  116. radius: number,
  117. startAngle: number,
  118. endAngle: number
  119. ) {
  120. const start = polarToCartesian(x, y, radius, endAngle)
  121. const end = polarToCartesian(x, y, radius, startAngle)
  122. const largeArcFlag = endAngle - startAngle <= 180 ? '0' : '1'
  123. const arcString = `M ${start.x} ${start.y} A ${radius} ${radius} 0 ${largeArcFlag} 0 ${end.x} ${end.y}`
  124. return arcString
  125. }
  126. export function limit(value: number, min: number, max: number) {
  127. return Math.min(Math.max(min, value), max)
  128. }