1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2024-06-01 20:41:36 +00:00
8bitworkshop/src/waveform.ts
2018-09-12 11:50:21 -04:00

232 lines
6.0 KiB
TypeScript

declare var VirtualList;
declare var Mousetrap;
export interface WaveformMeta {
name : string;
len : number;
}
export interface WaveformProvider {
getSignalMetadata() : WaveformMeta[];
getSignalData(index:number, start:number, len:number) : number[];
}
export class WaveformView {
parent : HTMLElement;
wfp : WaveformProvider;
wavelist;
meta : WaveformMeta[];
lines : HTMLCanvasElement[] = [];
zoom : number = 8;
t0 : number = 0;
tsel : number = -1;
pageWidth : number;
clocksPerPage : number;
clockMax : number;
constructor(parent:HTMLElement, wfp:WaveformProvider) {
this.parent = parent;
this.wfp = wfp;
this.recreate();
}
wtimer;
recreate() {
clearTimeout(this.wtimer);
this.wtimer = setTimeout(() => {
this.destroy();
// create new thing
this._recreate();
}, 0);
}
destroy() {
// remove old thing
if (this.wavelist) {
$(this.parent).empty();
}
}
_recreate() {
this.meta = this.wfp.getSignalMetadata();
if (!this.meta) return;
var width = this.pageWidth = $(this.parent).width();
var rowHeight = 40; // TODO
this.clocksPerPage = Math.floor(this.pageWidth/this.zoom) - 1;
this.clockMax = 0;
this.wavelist = new VirtualList({
w: width,
h: $(this.parent).height(),
itemHeight: rowHeight,
totalRows: this.meta.length,
generatorFn: (row : number) => {
var s = this.meta[row].name;
var linediv = document.createElement("div");
var canvas = document.createElement("canvas");
canvas.width = width - 4;
canvas.height = rowHeight;
linediv.appendChild(canvas); //document.createTextNode(s));
linediv.classList.add('waverow');
this.lines[row] = canvas;
this.refreshRow(row);
return linediv;
}
});
var wlc = this.wavelist.container;
wlc.tabIndex = -1; // make it focusable
//wlc.style = "overflow-x: hidden"; // TODO?
$(this.parent).append(wlc);
var down = false;
var selfn = (e) => {
this.setSelTime(e.offsetX / this.zoom + this.t0);
};
$(wlc).mousedown( (e) => {
down = true;
selfn(e);
//if (e['pointerId']) e.target.setPointerCapture(e['pointerId']);
});
$(wlc).mousemove( (e) => {
if (down) selfn(e);
});
$(wlc).mouseup( (e) => {
down = false;
//if (e['pointerId']) e.target.releasePointerCapture(e['pointerId']);
});
Mousetrap(wlc).bind('+', (e,combo) => {
this.setZoom(this.zoom * 2);
});
Mousetrap(wlc).bind('-', (e,combo) => {
this.setZoom(this.zoom / 2);
});
Mousetrap(wlc).bind('left', (e,combo) => {
this.setSelTime(this.tsel - 1);
});
Mousetrap(wlc).bind('right', (e,combo) => {
this.setSelTime(this.tsel + 1);
});
Mousetrap(wlc).bind('ctrl+left', (e,combo) => {
this.setSelTime(this.tsel - this.clocksPerPage/4);
});
Mousetrap(wlc).bind('ctrl+right', (e,combo) => {
this.setSelTime(this.tsel + this.clocksPerPage/4);
});
$(window).resize(() => {
this.recreate();
}); // TODO: remove?
// assign buttons
$("#scope_go_start").click(() => {
this.setOrgTime(0);
});
$("#scope_go_end").click(() => {
// TODO
});
$("#scope_go_fwd").click(() => {
this.setOrgTime(this.t0 + this.clocksPerPage/4);
});
$("#scope_go_back").click(() => {
this.setOrgTime(this.t0 - this.clocksPerPage/4);
});
}
roundT(t : number) {
t = Math.round(t);
t = Math.max(0, t); // make sure >= 0
t = Math.min(this.clockMax + this.clocksPerPage/2, t); // make sure <= end
return t;
}
setOrgTime(t : number) {
this.t0 = this.roundT(t);
this.refresh();
}
setEndTime(t : number) {
this.setOrgTime(t - this.clocksPerPage);
}
setSelTime(t : number) {
t = this.roundT(t);
if (t > this.t0 + this.clocksPerPage)
this.t0 += this.clocksPerPage / 4;
if (t < this.t0)
this.t0 -= this.clocksPerPage / 4;
this.tsel = t;
this.setOrgTime(this.t0);
}
setZoom(zoom : number) {
this.zoom = Math.max(1, zoom);
this.clocksPerPage = Math.ceil(this.pageWidth/this.zoom); // TODO: refactor into other one
this.refresh();
}
refresh() {
if (!this.meta) this.recreate();
if (!this.meta) return;
for (var i=0; i<this.meta.length; i++) {
this.refreshRow(i);
}
}
refreshRow(row : number) {
var canvas = this.lines[row];
var meta = this.meta[row];
if (!canvas || !meta) return;
var isclk = (meta.name == 'clk');
var w = canvas.width;
var h = canvas.height;
var ctx = canvas.getContext("2d");
ctx.font = "14px Andale Mono, Lucida Console, monospace";
// clear to black
ctx.clearRect(0, 0, canvas.width, canvas.height);
// draw waveform
ctx.strokeStyle = "#66ff66";
var b = 4;
var h2 = h-16-b;
var yrange = ((1<<meta.len)-1) || 0;
var data = this.wfp.getSignalData(row, this.t0, Math.ceil(w/this.zoom));
this.clockMax = Math.max(this.clockMax, this.t0 + data.length);
ctx.beginPath();
var x = 0;
var y = 0;
for (var i=0; i<data.length; i++) {
if (i>0)
ctx.lineTo(x,y);
y = b + (1.0 - data[i]/yrange) * h2;
if (!isclk) x += this.zoom*(1/8);
if (i==0)
ctx.moveTo(x,y);
else
ctx.lineTo(x,y);
if (isclk)
x += this.zoom;
else
x += this.zoom*(7/8);
}
ctx.stroke();
// draw selection thingie
if (this.tsel >= this.t0) {
ctx.strokeStyle = ctx.fillStyle = "#ff66ff";
ctx.beginPath();
x = (this.tsel - this.t0)*this.zoom + this.zoom/2;
ctx.moveTo(x, 0);
ctx.lineTo(x, h);
ctx.stroke();
// print value
var val = data[this.tsel - this.t0];
ctx.textAlign = 'right';
if (val !== undefined) {
ctx.fillText(val.toString(), w-b*2, h);
}
}
// draw labels
ctx.fillStyle = "white";
ctx.textAlign = "left";
var name = meta.name;
name = name.replace(/__DOT__/g, "."); // make nicer name
ctx.fillText(name, 5, h-b);
}
}