import { debug } from "debug"; import { sortBy } from "underscore"; import { ViewState } from "viewstate"; const log = debug("brick"); interface Placement { row?: number; prev?: number; t0: number; t1: number; onRowChange: () => void; } export class BrickLayout { viewState: ViewState; numRows: number; noteRow: { [uri: string]: Placement }; constructor(viewState: ViewState, numRows: number) { this.viewState = viewState; this.numRows = numRows; this.noteRow = {}; // uristr: row, t0, t1, onRowChange } addNote(n: { uri: { value: string } }, onRowChange: any) { this.noteRow[n.uri.value] = { row: 0, t0: 0, t1: 0, onRowChange }; } setNoteSpan(n: { uri: { value: string } }, t0: any, t1: any) { this.noteRow[n.uri.value].t0 = t0; this.noteRow[n.uri.value].t1 = t1; this._recompute(); } delNote(n: { uri: { value: string } }) { delete this.noteRow[n.uri.value]; this._recompute(); } _recompute() { for (let u in this.noteRow) { const row = this.noteRow[u]; row.prev = row.row; row.row = undefined; } const overlap = (a: Placement, b: Placement) => a.t0 < b.t1 && a.t1 > b.t0; const result = []; for (let u in this.noteRow) { const row = this.noteRow[u]; result.push({ dur: row.t1 - row.t0 + row.t0 * 0.0001, uri: u }); } const notesByWidth = sortBy(result, "dur"); notesByWidth.reverse(); for (let n of Array.from(notesByWidth)) { const blockedRows = new Set(); for (let u in this.noteRow) { const other = this.noteRow[u]; if (other.row !== null) { if (overlap(other, this.noteRow[n.uri])) { blockedRows.add(other.row); } } } for (let r = 0; r < this.numRows; r++) { if (!blockedRows.has(r)) { this.noteRow[n.uri].row = r; break; } } if (this.noteRow[n.uri].row === null) { log(`warning: couldn't place ${n.uri}`); this.noteRow[n.uri].row = 0; } if (this.noteRow[n.uri].row !== this.noteRow[n.uri].prev) { this.noteRow[n.uri].onRowChange(); } } } rowBottom(row: number) { return this.viewState.rowsY() + 20 + 150 * row + 140; } yForVFor(n: { uri: { value: string } }) { const row = this.noteRow[n.uri.value].row; if (row === undefined) { throw new Error(); } const rowBottom = this.rowBottom(row); const rowTop = rowBottom - 140; return (v: number) => rowBottom + (rowTop - rowBottom) * v; } }