/** * Returns a compact layout of a list of intervals * * Second options parameter * - `tight` places an interval with same ending as previous next to each other. */ export function layoutIntervals(intervals, { tight } = {}) { tight ??= true const canPlaceInterval = tight ? ({ start }, place) => place <= start : ({ start }, place) => place < start if (intervals.length === 0) { return [] } // sort intervals by ".start" intervals.sort((a, b) => a.start - b.start) const stack = [{ lastEnd: -Infinity, intervals: [] }] for (const interval of intervals) { const s = stack.find(level => canPlaceInterval(interval, level.lastEnd)) if (s) { s.intervals.push(interval) s.lastEnd = Math.max(s.lastEnd, interval.end) } else { stack.push({ lastEnd: interval.end, intervals: [interval] }) } } return stack.map(({ intervals }) => intervals) } function layoutBlockEvents(events) { events.sort((a, b) => a.start - b.start) let result = [] for (const event of events) { let viableIndex = 0 while ( result.filter( e => e.index === viableIndex && e.start < event.end && event.start < e.end ).length !== 0 ) { viableIndex += 1 } result.push({ ...event, index: viableIndex }) } return result } export function layoutEvents(events) { const overlap = (event, block) => event.start < block.end && block.start < event.end events.sort((a, b) => a.start - b.start) let layout = [] for (const event of events) { const blocks = layout.filter(block => overlap(event, block)) if (blocks.length > 0) { layout = layout.filter(block => !overlap(event, block)) layout.push({ start: Math.min(event.start, ...blocks.map(block => block.start)), end: Math.max(event.end, ...blocks.map(block => block.end)), events: blocks.flatMap(block => block.events).concat([event]), }) } else { layout.push({ start: event.start, end: event.end, events: [event] }) } } return layout.map(block => { const events = layoutBlockEvents(block.events) return { ...block, layers: Math.max(...events.map(event => event.index)) + 1, events: events, } }) } // // // // Testing... // // // console.dir( // layoutIntervals([ // { start: 0, end: 2 }, // { start: 2, end: 4 }, // { start: 1, end: 3 }, // { start: 4, end: 6 }, // { start: 3, end: 5 }, // { start: 2, end: 4 }, // ]), // { depth: null } // )