chords = {
document.head.innerHTML+=styling;
const width=512;
const height=512;
var canvas=DOM.canvas(width, height);
var ctx=canvas.getContext('2d');
let frame;
var ecvxys=[];
for (var i=0;i<ecvs.length;i++) {
const ecv=ecvs[i];
const a=(2.0*Math.PI*(1+i))/(ecvs.length+1);
const dx= 0.4*width*Math.sin(a);
const dy= -0.4*height*Math.cos(a);
const x=width*0.5+dx;
const y=height*0.5+dy;
ecvxys.push(
[x,y]
);
}
var ecvnames=[];
for (var i=0;i<ecvs.length;i++) {
ecvnames.push(ecvs[i].name.replaceAll("\n"," "));
}
var ecvindices={};
for (var i=0;i<ecvs.length;i++) {
ecvindices[ecvnames[i]]=i;
}
// Precompute enumerated links
var ecvlinks=[];
for (var i=0;i<ecvs.length;i++) {
var row=[];
console.log(ecvs[i].links);
for (var j=0;j<ecvs[i].links.length;j++) {
row.push(ecvindices[ecvs[i].links[j]]);
}
ecvlinks.push(row);
}
console.log(ecvlinks);
var nearestecv= -2; // -1 means near centre
var ecvstar=
function tick() {
ctx.fillStyle = "white";
ctx.fillRect(0, 0, width, height); // Draw a filled rectangle
//console.log('Tick: nearest is '+(ecvs[nearestecv].name));
for (var i=0;i<ecvs.length;i++) {
const ecv=ecvs[i];
const active=(i==nearestecv);
for (var j=0;j<ecvlinks[i].length;j++) {
const t=ecvlinks[i][j];
const x0=width*0.5+0.8*(ecvxys[i][0]-0.5*width);
const y0=height*0.5+0.8*(ecvxys[i][1]-0.5*height)
const x1=width*0.5+0.8*(ecvxys[t][0]-0.5*width);
const y1=height*0.5+0.8*(ecvxys[t][1]-0.5*height);
if (active || nearestecv==-1) {
var gradient = ctx.createLinearGradient(x0,y0,x1,y1);
gradient.addColorStop(0,groupcolours[ecvs[i].group]);
gradient.addColorStop(1,groupcolours[ecvs[t].group]);
ctx.strokeStyle=gradient;
ctx.lineWidth=2;
} else {
ctx.strokeStyle='#eeeeee44'; // A bit of transparency to make it really faint
ctx.lineWidth=1;
}
ctx.beginPath();
ctx.moveTo(x0,y0);
ctx.quadraticCurveTo(width*0.5,height*0.5,x1,y1);
ctx.stroke();
}
if (active) {
ctx.font='16px notesstyle-boldtfregular';
} else {
ctx.font='14px notesstyle-boldtfregular';
}
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle='#444444'; // Text colour
const lines=ecv.name.split('\n');
if (lines.length==1) {
ctx.fillText(ecv.name,ecvxys[i][0],ecvxys[i][1]);
} else {
const lineHeight=ctx.measureText('M').width*1.25 // Doesn't measure height!
for (var line=0;line<lines.length;line++) {
ctx.fillText(lines[line],ecvxys[i][0],ecvxys[i][1]-lineHeight*(0.5*lines.length-line));
}
}
}
frame = requestAnimationFrame(tick);
}
tick();
function sqr(x) {return x*x;}
canvas.onmousemove = function(e) {
const rect = canvas.getBoundingClientRect();
const ex = e.clientX - rect.left;
const ey = e.clientY - rect.top;
nearestecv= -1;
var nearestd=4.0*Math.sqrt(sqr(ex-0.5*width)+sqr(ey-0.5*height));
for (var i=0;i<ecvs.length;i++) {
const x=ecvxys[i][0];
const y=ecvxys[i][1];
const d=Math.sqrt(sqr(x-ex)+sqr(y-ey));
if (d<nearestd) {
nearestecv=i;
nearestd=d;
}
//console.log('Nearest is '+(ecvs[nearestecv].name));
}
}
invalidation.then(
() => cancelAnimationFrame(frame)
);
return canvas;
}