From a92dc6de880dc6644dec2dd1cb4cc621506950f4 Mon Sep 17 00:00:00 2001 From: Philipp Schaad Date: Thu, 31 Oct 2024 10:13:55 +0100 Subject: [PATCH] Various Bugfixes (#179) * Fix exporting to PDF Closes #177 * Fix arrays not showing up in sidebar Closes #156 * Avoid crashing the renderer on bad layouts * Fix SM layouter for some irregular / irreducible CF * Fixes * Allow initialization with different contents container * Fix for Jupyter * Add support for direct struct member accesses on access nodes --- src/layouter/layout.ts | 2 +- src/layouter/state_machine/sm_layouter.ts | 17 +++++++ src/renderer/renderer_elements.ts | 58 +++++++++++++++++++++-- src/sdfv.ts | 5 +- src/sdfv_ui.ts | 37 +++++++++++++-- webpack.config.js | 1 + 6 files changed, 109 insertions(+), 11 deletions(-) diff --git a/src/layouter/layout.ts b/src/layouter/layout.ts index 88c22880..4e100c30 100644 --- a/src/layouter/layout.ts +++ b/src/layouter/layout.ts @@ -1004,7 +1004,7 @@ function layoutControlFlowRegion( // Fall back to dagre for anything that cannot be laid out with // the vertical layout (e.g., irreducible control flow). try { - SMLayouter.layoutDagreCompat(g, sdfg.start_block?.toString()); + SMLayouter.layoutDagreCompat(g, cfg.start_block?.toString()); } catch (_ignored) { dagre.layout(g); } diff --git a/src/layouter/state_machine/sm_layouter.ts b/src/layouter/state_machine/sm_layouter.ts index 97a4a671..67c45df1 100644 --- a/src/layouter/state_machine/sm_layouter.ts +++ b/src/layouter/state_machine/sm_layouter.ts @@ -276,6 +276,23 @@ export class SMLayouter { 'The following edges were not routed:', unrouted ); + // To avoid crashing, simply straight-route these edges. + for (const edge of unrouted) { + const srcNode = this.graph.get((edge as any).src); + const dstNode = this.graph.get((edge as any).dst); + if (!srcNode || !dstNode) + throw Error('Unrouted edge may not be straight-routed.'); + edge.points = [ + { + x: srcNode.x, + y: srcNode.y + (srcNode.height / 2), + }, + { + x: dstNode.x, + y: dstNode.y - (dstNode.height / 2), + }, + ]; + } } } diff --git a/src/renderer/renderer_elements.ts b/src/renderer/renderer_elements.ts index f7669137..67d3de47 100644 --- a/src/renderer/renderer_elements.ts +++ b/src/renderer/renderer_elements.ts @@ -2099,6 +2099,35 @@ export class Connector extends SDFGElement { export class AccessNode extends SDFGNode { + public getDesc(): any { + const name = this.data.node.attributes.data; + const nameParts = name.split('.'); + if (nameParts.length > 1) { + let desc = this.sdfg.attributes._arrays[nameParts[0]]; + let i = 1; + while (i < nameParts.length) { + if (!desc.attributes?.members) + return undefined; + const nextName = nameParts[i]; + let foundDesc = undefined; + for (const mbr of desc.attributes.members) { + if (mbr[0] == nextName) { + foundDesc = mbr[1]; + break; + } + } + if (foundDesc) + desc = foundDesc; + else + return undefined; + i++; + } + return desc; + } else { + return this.sdfg.attributes._arrays[nameParts[0]]; + } + } + public draw( renderer: SDFGRenderer, ctx: CanvasRenderingContext2D, _mousepos?: Point2D @@ -2110,7 +2139,7 @@ export class AccessNode extends SDFGNode { ctx.strokeStyle = this.strokeStyle(renderer); const name = this.data.node.attributes.data; - const nodedesc = this.sdfg.attributes._arrays[name]; + const nodedesc = this.getDesc(); // Streams have dashed edges if (nodedesc && nodedesc.type === 'Stream') ctx.setLineDash([5, 3]); @@ -2217,9 +2246,7 @@ export class AccessNode extends SDFGNode { public tooltip(container: HTMLElement): void { super.tooltip(container); - const nodedesc = this.sdfg.attributes._arrays[ - this.data.node.attributes.data - ]; + const nodedesc = this.getDesc(); if (nodedesc) return; container.classList.add('sdfvtooltip--error'); @@ -3485,7 +3512,28 @@ function drawOctagon( function drawEllipse( ctx: CanvasRenderingContext2D, x: number, y: number, w: number, h: number ): void { - ctx.ellipse(x + w / 2, y + h / 2, w / 2, h / 2, 0, 0, 2 * Math.PI); + if ((ctx as any).pdf) { + // The PDF rendering context does not have an `ellipse` function. As + // such, we revert back to the non-GPU-accelerated method of drawing + // ellipses that we used up to and including commit 2ceba1d. + // Adapted from https://stackoverflow.com/a/2173084/6489142 + const kappa = .5522848 + const ox = (w / 2) * kappa; + const oy = (h / 2) * kappa; + const xe = x + w; + const ye = y + h; + const xm = x + (w / 2); + const ym = y + (h / 2); + ctx.moveTo(x, ym); + ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + } else { + // When drawing on a regular canvas, use the built-in method of drawing + // ellipses to utilize GPU acceleration where available. + ctx.ellipse(x + w / 2, y + h / 2, w / 2, h / 2, 0, 0, 2 * Math.PI); + } } function drawTrapezoid( diff --git a/src/sdfv.ts b/src/sdfv.ts index 38241cd8..0350e76d 100644 --- a/src/sdfv.ts +++ b/src/sdfv.ts @@ -467,10 +467,11 @@ export class WebSDFV extends SDFV { public setSDFG( sdfg: JsonSDFG | null = null, userTransform: DOMMatrix | null = null, - debugDraw: boolean = false + debugDraw: boolean = false, + contents_container_id: string = 'contents' ): void { this.renderer?.destroy(); - const container = document.getElementById('contents'); + const container = document.getElementById(contents_container_id); if (container && sdfg) { const renderer = new SDFGRenderer( sdfg, container, this, null, userTransform, debugDraw, null, diff --git a/src/sdfv_ui.ts b/src/sdfv_ui.ts index c6428463..c40d21c8 100644 --- a/src/sdfv_ui.ts +++ b/src/sdfv_ui.ts @@ -6,6 +6,7 @@ import { Edge, Memlet, NestedSDFG, + SDFG, SDFGElement, } from './renderer/renderer_elements'; import type { DagreGraph, SDFGRenderer } from './renderer/renderer'; @@ -198,9 +199,7 @@ export class SDFVWebUI implements ISDFVUserInterface { // If access node, add array information too if (elem instanceof AccessNode) { - const sdfg_array = elem.sdfg.attributes._arrays[ - elem.attributes().data - ]; + const sdfg_array = elem.getDesc(); contents.append($('
')); contents.append($('

', { text: sdfg_array.type + ' properties:', @@ -235,6 +234,10 @@ export class SDFVWebUI implements ISDFVUserInterface { switch (attr[0]) { case 'layout': case 'sdfg': + case '_arrays': + case 'orig_sdfg': + case 'transformation_hist': + case 'position': continue; default: contents.append($('', { @@ -250,6 +253,34 @@ export class SDFVWebUI implements ISDFVUserInterface { } } } + + // For SDFGs and nested SDFGs, add information about the SDFG's data + // descriptors. + let descriptors = undefined; + if (elem instanceof SDFG) + descriptors = elem.attributes()._arrays; + else if (elem instanceof NestedSDFG) + descriptors = elem.attributes().sdfg.attributes._arrays; + + if (descriptors) { + contents.append($('
')); + contents.append($('', { + html: 'Data containers:  ', + })); + contents.append($('
')); + for (const desc in descriptors) { + contents.append($('', { + html: desc + ':  ', + })); + contents.append($('', { + html: sdfg_property_to_string( + descriptors[desc], renderer.view_settings() + ), + })); + contents.append($('
')); + } + contents.append($('
')); + } } } diff --git a/webpack.config.js b/webpack.config.js index 4b79fc40..a8f57e31 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -116,6 +116,7 @@ const jupyterConfig = { output: { filename: '[name]_jupyter.js', path: path.resolve(__dirname, 'dist'), + publicPath: path.resolve(__dirname, 'dist'), }, plugins: [ new webpack.ProvidePlugin({