function draw(graph, state, options) {
let {time, visited_nodes, visited_edges, queue, path_edges} = state;
options = options === undefined ? [] : options.split(",");
let node_svg = ``, edge_svg = ``, queue_svg = ``, label_svg = ``;
for (let [name, v] of Object.entries(graph.nodes)) {
let c = (name in visited_nodes) ? "visited" : "";
node_svg += `<circle class="${c}" id="node-${name}" cx=${v.x} cy=${v.y} r=${nodeRadius} />\n`;
if (options.includes("distances") && name in state.visited_nodes && name != graph.start) {
node_svg += `<text class=node-label x=${v.x} y=${v.y}>${state.visited_nodes[name]}</text>\n`;
}
}
for (let {src,dst,length} of graph.edges) {
let c = (visited_edges.some(([s,d]) => (s == src && d == dst) || (s == dst && d == src)))
? ("visited" + (options.includes("paths") ? " longer" : "")) : "";
let v = graph.nodes[src], u = graph.nodes[dst];
edge_svg += `<line class="${c}" id="edge-${src}-${dst}" x1=${v.x} y1=${v.y} x2=${u.x} y2=${u.y} />\n`;
let lx = (v.x + u.x)/2, ly = (v.y+u.y)/2;
let dx = v.x-u.x, dy = v.y-u.y;
let distance = Math.sqrt(dx*dx+dy*dy);
let ox = labelOffset*dy/distance, oy = -labelOffset*dx/distance;
label_svg += `<text x=${lx+ox} y=${ly+oy}>${length}</text>\n`;
let ok = (([s,d]) => (s == src && d == dst) || (s == dst && d == src));
if (options.includes("paths") && path_edges.some(ok)) {
let [src,dst] = path_edges.find(ok);
let v = graph.nodes[src], u = graph.nodes[dst];
let length = .88*nodeRadius;
let dx = length*(u.x-v.x)/distance, dy = length*(u.y-v.y)/distance;
let o = 1.414*strokeWidth;
let ox= o*(v.y-u.y)/distance, oy = o*(u.x-v.x)/distance;
let l = (length/2 + nodeRadius)/distance;
let lx = u.x + l*(v.x-u.x), ly = u.y + l*(v.y-u.y);
edge_svg += `<g class=shortest>
<line x1=${v.x} y1=${v.y} x2=${u.x} y2=${u.y} />
<polygon stroke-width=${strokeWidth/4}px stroke-linejoin="round" points="${lx+dx} ${ly+dy}
${lx-dx+ox} ${ly-dy+oy}
${lx} ${ly}
${lx-dx-ox} ${ly-dy-oy}" />
</g>
`;
}
}
// QUEUE
for (let {src,dst,src_time,dst_time} of queue) {
// This avoids creating zero-length lines, which can ugly up the diagram.
if (time == src_time) continue;
let progress = (time - src_time)/(dst_time - src_time);
let v = graph.nodes[src], u = graph.nodes[dst];
let dx = u.x-v.x, dy = u.y-v.y; // vector from v to u
let distance = Math.sqrt(dx*dx + dy*dy);
// we offset the start of the blue water by the node radius
let vx = v.x + nodeRadius*dx/distance, vy = v.y + nodeRadius*dy/distance;
let ux = u.x - nodeRadius*dx/distance, uy = u.y - nodeRadius*dy/distance;
//ux = u.x; uy = u.y;
let x2 = vx + progress * (ux-vx), y2 = vy + progress * (uy-vy);
// console.log(src, dst, progress, v.x, x2, y2);
queue_svg += `<line x1=${v.x} y1=${v.y} x2="${x2}" y2="${y2}" />\n`
}
return svg`<svg class=diagram viewBox="0 0 ${columns} ${rows}">
<g class=edges> ${edge_svg} <g class=in-progress> ${queue_svg} </g> </g>
<g class=nodes> ${node_svg} </g>
<g class=edge-label> ${label_svg} </g>
</svg>`
}