diff options
Diffstat (limited to 'graph')
| -rw-r--r-- | graph/README.md | 20 | ||||
| -rw-r--r-- | graph/web/layout.js | 34 |
2 files changed, 44 insertions, 10 deletions
diff --git a/graph/README.md b/graph/README.md index f0e32d9d..f7037548 100644 --- a/graph/README.md +++ b/graph/README.md @@ -156,11 +156,21 @@ This keeps both graphs side by side in the chapter README. Chapter 10a's [load-after-store example](../chapter10a/README.md#local-memory-optimizations) uses this format to isolate a single rewrite. -The compact memory-dependency schematics in 10b retain `.gv` sources to -preserve their deliberate horizontal layouts. They use the viewer palette and -node shapes; definitions are left of uses horizontally and above uses vertically, -with arrows from use to definition. Type lattices use their own domain colors -and top-to-bottom lattice order, rather than IR edge conventions. +The memory diagrams in 10a, 10b, and 11 also use viewer snapshots, with definitions +above uses (apart from loop backedges) and arrows from use to definition. +They omit control-flow context, including Regions and their Phi bindings, +while retaining data and memory dependencies. Small, detached True/False +projections show branch gates for Loads and Stores. Chapter 10 shows schematic +source gates; Chapter 11 shows actual bindings at each scheduling stage, with +entry-block and Loop bindings omitted. Its infinite-loop figures retain the +CFG because that is their subject. Empty control slots in focused snapshots +can denote omitted context rather than null inputs in the compiler. +Type lattices retain DOT sources, their own +domain colors, and top-to-bottom lattice order, rather than IR edge conventions. + +Routed edges keep their horizontal and vertical segments, with rounded corners +in both the live viewer and SVG exports. The bend radius shrinks on short +segments so adjacent corners do not overlap. The launcher prints the viewer URL before opening the browser. If the desktop only raises an existing browser window, paste that URL into a new tab. Use diff --git a/graph/web/layout.js b/graph/web/layout.js index 880e7662..04d6f338 100644 --- a/graph/web/layout.js +++ b/graph/web/layout.js @@ -24,7 +24,9 @@ class GraphLayout { const hidden = (n, e) => ["START", "UNIT"].includes(n.kind) && raw.get(e.def)?.kind === "STOP"; const nodes = snap.nodes.map(n => { const label = `${this.clip(n.label)} #${n.id}${n.proj ? "/" + n.proj.idx : ""}`, type = this.clip(n.type); - const compact = ["START", "UNIT", "STOP"].includes(n.kind); + // Detached control projections need only a small, single-line anchor. + const compact = ["START", "UNIT", "STOP"].includes(n.kind) || + (n.kind === "CTRL" && n.proj && !raw.has(n.proj.par) && !n.type); let w = Math.ceil(Math.max(120, this.ctx.measureText(label).width + 24 + (n.folding ? 60 : 0), this.ctx.measureText(type).width + 24, (n.edges.length + 1) * 18)); const scope = n.kind === "SCOPE"; @@ -335,9 +337,9 @@ class GraphLayout { e.path = `M${x},${y} C${x},${y + 24} ${u},${v - 24} ${u},${v}`; } else { const route = routes.get(e.id), origin = boxes.get(route.e.container) || route.box; - e.path = route.e.sections.map(s => + e.path = route.e.sections.map(s => this.roundedPath( [s.startPoint, ...(s.bendPoints || []), s.endPoint] - .map((p, i) => `${i ? "L" : "M"}${p.x + origin.x},${p.y + origin.y}`).join(" ")).join(" "); + .map(p => ({x: p.x + origin.x, y: p.y + origin.y})))).join(" "); } if (e.wrap) { const box = boxes.get("g" + e.wrap), i = lanes.get(e.wrap) || 0; @@ -352,7 +354,7 @@ class GraphLayout { const right = box.x + box.width - 12 - i * 10; const head = this.toRight(from, right, obstacles), tail = this.toRight(to, right, obstacles); const pts = [start, ...head, ...tail.reverse(), end]; - e.path = pts.map((p, i) => `${i ? "L" : "M"}${p.x},${p.y}`).join(" "); + e.path = this.roundedPath(pts); e.lane = right; } } @@ -361,7 +363,9 @@ class GraphLayout { j.x += box.x; j.y += box.y; const p = use.ports.find(p => p.id === use.id + "i" + j.slot); const x = use.x + p.x + 3, y = use.y + p.y + 3; - j.path = `M${j.x},${j.y + j.height / 2}H${j.x - 12}V${box.y - 18}H${x}V${y}`; + j.path = this.roundedPath([{x: j.x, y: j.y + j.height / 2}, + {x: j.x - 12, y: j.y + j.height / 2}, {x: j.x - 12, y: box.y - 18}, + {x, y: box.y - 18}, {x, y}]); } return {width: graph.width, height: graph.height, nodes, edges, jumps, raw: view.raw, cover: view.cover, groups: view.open.map(g => ({...g, ...boxes.get("g" + g.id), gid: g.id, n: view.raw.get(g.id)})), @@ -369,6 +373,26 @@ class GraphLayout { held: new Set(held.map(n => n.n.id))}; } + // Keep the orthogonal route, easing each bend without moving its endpoints. + // Half-segment limits prevent neighboring corners from overlapping on short + // routes. The radius fits inside the routing gutters' eight-pixel clearance. + roundedPath(points, radius = 8) { + const ps = points.filter((p, i) => !i || p.x !== points[i-1].x || p.y !== points[i-1].y); + if (!ps.length) return ""; + let path = `M${ps[0].x},${ps[0].y}`; + for (let i = 1; i < ps.length - 1; i++) { + const a = ps[i-1], b = ps[i], c = ps[i+1]; + const ux = b.x-a.x, uy = b.y-a.y, vx = c.x-b.x, vy = c.y-b.y; + if (ux*vy === uy*vx) { path += ` L${b.x},${b.y}`; continue; } + const before = Math.hypot(ux,uy), after = Math.hypot(vx,vy); + const r = Math.min(radius, before/2, after/2); + path += ` L${b.x-ux*r/before},${b.y-uy*r/before}` + + ` Q${b.x},${b.y} ${b.x+vx*r/after},${b.y+vy*r/after}`; + } + if (ps.length > 1) path += ` L${ps.at(-1).x},${ps.at(-1).y}`; + return path; + } + // An orthogonal lead to a right-side gutter. Search only the channels // between boxes; this is routing geometry, not a second node layout. toRight(start, right, boxes) { |
