diff options
| author | Cliff Click <cliffc@acm.org> | 2026-09-21 22:53:49 -0700 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2026-09-21 22:53:49 -0700 |
| commit | 92e3013a4cab12747af9d5484e7caad6fc454b1e (patch) | |
| tree | a3137dd95038c3487848c355a0c7053f4a518721 /graph/web/render.js | |
| parent | e48d59474230869ba81c1712128c01fd822946f5 (diff) | |
| download | Simple-92e3013a4cab12747af9d5484e7caad6fc454b1e.tar.gz Simple-92e3013a4cab12747af9d5484e7caad6fc454b1e.zip | |
Switch over to the new ELK driver
Interact between compiler & browser now hooks several nbew places.
Diffstat (limited to 'graph/web/render.js')
| -rw-r--r-- | graph/web/render.js | 39 |
1 files changed, 38 insertions, 1 deletions
diff --git a/graph/web/render.js b/graph/web/render.js index b488ed0e..76cf8c06 100644 --- a/graph/web/render.js +++ b/graph/web/render.js @@ -30,7 +30,7 @@ class GraphView { this.resize.observe(host); } - show(snap, scene) { + show(snap, scene, evt) { if (this.comp !== snap.comp) { this.comp = snap.comp; this.pick = 0; @@ -39,6 +39,7 @@ class GraphView { this.links.selectAll("*").remove(); } this.scene = scene; + this.evt = evt; this.links.selectAll("path.edge").data(scene.edges, e => e.id).join("path") .attr("class", e => "edge " + e.role).attr("id", e => e.id) .attr("d", e => e.path).attr("stroke", e => this.colors[e.role]) @@ -78,6 +79,7 @@ class GraphView { .attr("text-anchor", "middle").text((p, j) => n.n.edges[j].idx); }); this.assocs(document.getElementById("assocs").checked); + this.mark(); this.select(this.pick); if (this.auto) this.fit(); } @@ -100,6 +102,41 @@ class GraphView { assocs(show) { this.links.selectAll(".ASSOC").style("display", show ? null : "none"); } + mark() { + const evt = this.evt; + const show = document.getElementById("near").checked; + const near = new Set(show ? evt?.near : []); + this.nodes.selectAll("g.node").classed("near", n => near.has(n.n.id)) + .classed("focus", n => show && (n.n.id === evt?.node || n.n.id === evt?.repl)); + } + + // Save the whole graph at its layout size, independent of pan/zoom. + save(step) { + const svg = this.svg.node().cloneNode(true); + svg.setAttribute("xmlns", "http://www.w3.org/2000/svg"); + svg.setAttribute("width", this.scene.width); + svg.setAttribute("height", this.scene.height); + svg.setAttribute("viewBox", `0 0 ${this.scene.width} ${this.scene.height}`); + svg.style.fontFamily = getComputedStyle(this.host).fontFamily; + svg.querySelector("g").removeAttribute("transform"); + // Keep styles, markers and labels in the file; no viewer assets are needed. + const style = document.createElementNS(svg.namespaceURI, "style"); + style.textContent = document.querySelector("style").textContent; + svg.prepend(style); + const bg = document.createElementNS(svg.namespaceURI, "rect"); + bg.setAttribute("width", "100%"); bg.setAttribute("height", "100%"); + bg.setAttribute("fill", getComputedStyle(this.host.parentElement).backgroundColor); + svg.insertBefore(bg, svg.querySelector("g")); + const blob = new Blob([new XMLSerializer().serializeToString(svg)], {type: "image/svg+xml"}); + const url = URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; link.download = `simple-${step}.svg`; + document.body.append(link); + link.click(); + link.remove(); + setTimeout(() => URL.revokeObjectURL(url), 1000); + } + fit() { if (!this.scene || !this.host.clientWidth || !this.host.clientHeight) return; const w = this.host.clientWidth, h = this.host.clientHeight; |
