Press n or j to go to the next uncovered block, b, p or k for the previous block.
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | interface Dimension { readonly min: number; readonly max: number; } export interface LaneLayoutEntry<T> { readonly data: T; readonly dimension: Dimension; readonly lane: { startLane: number; endLane: number; maxLane: number }; } export interface LaneLayout<T> { readonly entries: readonly T[]; get maxLanes(): number; } export function computeLaneLayout<T extends LaneLayoutEntry<unknown>>( layoutEntries: T[], ): LaneLayout<T> { let lanes: LaneLayoutEntry<unknown>[][] = []; let lastEnding: number | null = null; const rv: T[] = layoutEntries.sort((e1, e2) => { const rv = e1.dimension.min - e2.dimension.min; if (rv !== 0) { return rv; } return e1.dimension.max - e2.dimension.max; }); rv.forEach(e => { if (lastEnding !== null && e.dimension.min >= lastEnding) { computeLanes(lanes); lanes = []; lastEnding = null; } let placed = false; for (const col of lanes) { if (!intersects(col[col.length - 1].dimension, e.dimension)) { col.push(e); placed = true; break; } } if (!placed) { lanes.push([e]); } if (lastEnding === null || e.dimension.max > lastEnding) { lastEnding = e.dimension.max; } }); if (lanes.length > 0) { computeLanes(lanes); } return { get maxLanes() { return rv .map(e => e.lane.maxLane) .reduce((a, b) => Math.max(a, b), -Infinity); }, entries: rv, }; } function computeLanes(lanes: LaneLayoutEntry<unknown>[][]) { lanes.forEach((lane, index) => { lane.forEach(layoutEntry => { const colSpan = computeLaneSpan(layoutEntry, index, lanes); layoutEntry.lane.startLane = index; layoutEntry.lane.endLane = index + colSpan; layoutEntry.lane.maxLane = lanes.length; }); }); } function computeLaneSpan( layoutEntry: LaneLayoutEntry<unknown>, laneIndex: number, columns: LaneLayoutEntry<unknown>[][], ) { let colSpan = 1; for (let i = laneIndex + 1; i < columns.length; i++) { const col = columns[i]; for (const e of col) { if (intersects(layoutEntry.dimension, e.dimension)) { return colSpan; } } colSpan++; } return colSpan; } function intersects(a: Dimension, b: Dimension) { return a.max > b.min && a.min < b.max; } |