diff --git a/src/detectRepeats.test.ts b/src/detectRepeats.test.ts index 3f7659a..2c20e86 100644 --- a/src/detectRepeats.test.ts +++ b/src/detectRepeats.test.ts @@ -143,4 +143,74 @@ describe("detectRepeats()", () => { ]; expect(detectRepeats(intervals)).toEqual(intervals); }); + + it("gathers comments together", () => { + const intervals: Interval[] = [ + { + type: "Interval", + duration: new Seconds(100), + intensity: { from: 1, to: 1 }, + comments: [ + { offset: new Seconds(0), text: "Let's start" }, + { offset: new Seconds(20), text: "Stay strong!" }, + { offset: new Seconds(90), text: "Finish it!" }, + ], + }, + { + type: "Rest", + duration: new Seconds(100), + intensity: { from: 0.5, to: 0.5 }, + comments: [ + { offset: new Seconds(0), text: "Huh... have a rest" }, + { offset: new Seconds(80), text: "Ready for next?" }, + ], + }, + { + type: "Interval", + duration: new Seconds(100), + intensity: { from: 1, to: 1 }, + comments: [ + { offset: new Seconds(0), text: "Bring it on again!" }, + { offset: new Seconds(50), text: "Half way" }, + { offset: new Seconds(90), text: "Almost there!" }, + ], + }, + { + type: "Rest", + duration: new Seconds(100), + intensity: { from: 0.5, to: 0.5 }, + comments: [ + { offset: new Seconds(30), text: "Wow... you did it!" }, + { offset: new Seconds(40), text: "Nice job." }, + { offset: new Seconds(50), text: "Until next time..." }, + ], + }, + ]; + expect(detectRepeats(intervals)).toEqual([ + { + type: "repeat", + times: 2, + intervals: [ + { type: "Interval", duration: new Seconds(100), intensity: { from: 1, to: 1 }, comments: [] }, + { type: "Rest", duration: new Seconds(100), intensity: { from: 0.5, to: 0.5 }, comments: [] }, + ], + comments: [ + { offset: new Seconds(0), text: "Let's start" }, + { offset: new Seconds(20), text: "Stay strong!" }, + { offset: new Seconds(90), text: "Finish it!" }, + + { offset: new Seconds(100), text: "Huh... have a rest" }, + { offset: new Seconds(180), text: "Ready for next?" }, + + { offset: new Seconds(200), text: "Bring it on again!" }, + { offset: new Seconds(250), text: "Half way" }, + { offset: new Seconds(290), text: "Almost there!" }, + + { offset: new Seconds(330), text: "Wow... you did it!" }, + { offset: new Seconds(340), text: "Nice job." }, + { offset: new Seconds(350), text: "Until next time..." }, + ], + }, + ]); + }); }); diff --git a/src/detectRepeats.ts b/src/detectRepeats.ts index b32ff60..2e047cf 100644 --- a/src/detectRepeats.ts +++ b/src/detectRepeats.ts @@ -1,5 +1,6 @@ -import { equals } from "ramda"; +import { eqProps, flatten, zip } from "ramda"; import { Interval, Comment } from "./ast"; +import { Seconds } from "./types"; export type RepeatedInterval = { type: "repeat"; @@ -8,6 +9,13 @@ export type RepeatedInterval = { comments: Comment[]; }; +// All fields besides comments must equal +const equalIntervals = (a: Interval, b: Interval): boolean => + eqProps("type", a, b) && eqProps("duration", a, b) && eqProps("intensity", a, b) && eqProps("cadence", a, b); + +const equalIntervalArrays = (as: Interval[], bs: Interval[]): boolean => + zip(as, bs).every(([a, b]) => equalIntervals(a, b)); + const windowSize = 2; const countRepetitions = (reference: Interval[], intervals: Interval[], startIndex: number): number => { @@ -15,7 +23,7 @@ const countRepetitions = (reference: Interval[], intervals: Interval[], startInd while (startIndex + repeats * windowSize < intervals.length) { const from = startIndex + repeats * windowSize; const possibleRepeat = intervals.slice(from, from + windowSize); - if (equals(reference, possibleRepeat)) { + if (equalIntervalArrays(reference, possibleRepeat)) { repeats++; } else { return repeats; @@ -24,6 +32,28 @@ const countRepetitions = (reference: Interval[], intervals: Interval[], startInd return repeats; }; +const offsetComments = (interval: Interval, baseOffset: Seconds): Comment[] => { + return interval.comments.map(({ offset, ...rest }) => ({ + offset: new Seconds(baseOffset.value + offset.value), + ...rest, + })); +}; + +const collectComments = (intervals: Interval[]): Comment[] => { + let previousIntervalsDuration = new Seconds(0); + return flatten( + intervals.map((interval) => { + const comments = offsetComments(interval, previousIntervalsDuration); + previousIntervalsDuration = new Seconds(previousIntervalsDuration.value + interval.duration.value); + return comments; + }), + ); +}; + +const stripComments = (intervals: Interval[]): Interval[] => { + return intervals.map(({ comments, ...rest }) => ({ comments: [], ...rest })); +}; + const isRangeInterval = (interval: Interval): boolean => interval.intensity.from !== interval.intensity.to; export const detectRepeats = (intervals: Interval[]): (Interval | RepeatedInterval)[] => { @@ -47,8 +77,8 @@ export const detectRepeats = (intervals: Interval[]): (Interval | RepeatedInterv processed.push({ type: "repeat", times: repeats, - intervals: reference, - comments: [], + intervals: stripComments(reference), + comments: collectComments(intervals.slice(i, i + windowSize * repeats)), }); i += repeats * windowSize; } else {