Files @ e7e03c203c99
Branch filter:

Location: light9/web/timeline/brick_layout.ts

drewp@bigasterisk.com
resize cursor canvas for 400px tall spectros. fix canvas resolution code
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;
  }
}