From d9dbab167baa5040c2703cdafe1d01dd74868426 Mon Sep 17 00:00:00 2001 From: Philipp Schaad Date: Fri, 19 Apr 2024 15:53:20 +0200 Subject: [PATCH] Fixes to cutouts and CFG lists --- src/renderer/canvas_manager.ts | 4 +- src/renderer/renderer.ts | 117 +++++++++++++++++++-------------- src/utils/sdfg/sdfg_utils.ts | 18 +++-- src/utils/sdfg/traversal.ts | 3 +- 4 files changed, 86 insertions(+), 56 deletions(-) diff --git a/src/renderer/canvas_manager.ts b/src/renderer/canvas_manager.ts index 6ef6d6db..6042f3a9 100644 --- a/src/renderer/canvas_manager.ts +++ b/src/renderer/canvas_manager.ts @@ -656,7 +656,7 @@ export class CanvasManager { // Also update destination point of edge if (edge.dst_connector !== null) { const e = parent_element?.data?.state?.edges[edge.id]; - const dst_el = parent_graph.node(e?.dst); + const dst_el = parent_graph?.node(e?.dst); if (dst_el) { for (let i = 0; i < dst_el.in_connectors.length; i++) { const dst_name = dst_el.in_connectors[i].data.name; @@ -693,7 +693,7 @@ export class CanvasManager { // Also update source point of edge if (edge.src_connector !== null) { const e = parent_element?.data?.state?.edges[edge.id]; - const src_el = parent_graph.node(e?.src); + const src_el = parent_graph?.node(e?.src); if (src_el) { for (let i = 0; i < src_el.out_connectors.length; i++) { const out_name = src_el.out_connectors[i].data.name; diff --git a/src/renderer/renderer.ts b/src/renderer/renderer.ts index f26c63e8..62c84566 100644 --- a/src/renderer/renderer.ts +++ b/src/renderer/renderer.ts @@ -40,7 +40,7 @@ import { memletTreeComplete } from '../utils/sdfg/memlet_trees'; import { check_and_redirect_edge, deletePositioningInfo, deleteSDFGNodes, deleteCFGBlocks, findExitForEntry, findGraphElementByUUID, - getPositioningInfo, get_uuid_graph_element, findRootCFG + getPositioningInfo, getGraphElementUUID, findRootCFG } from '../utils/sdfg/sdfg_utils'; import { traverseSDFGScopes @@ -112,7 +112,7 @@ type JsonSDFGElemFunction = ( export type CFGListType = { [id: string]: { jsonObj: JsonSDFGControlFlowRegion, - graph: DagreGraph, + graph: DagreGraph | null, } }; @@ -902,6 +902,7 @@ export class SDFGRenderer extends EventEmitter { // Create the initial SDFG layout // Loading animation already started in the file_read_complete function in sdfv.ts // to also include the JSON parsing step. + this.updateCFGList(); this.relayout(); // Set mouse event handlers @@ -1016,42 +1017,57 @@ export class SDFGRenderer extends EventEmitter { this.canvas_manager?.draw_async(); } - public setSDFG(new_sdfg: JsonSDFG, layout: boolean = true): void { - this.sdfg = new_sdfg; - - if (layout) { - this.add_loading_animation(); - setTimeout(() => { - this.relayout(); - }, 10); - - this.draw_async(); - } - - // Update info box - if (this.selected_elements.length === 1) { - const uuid = get_uuid_graph_element(this.selected_elements[0]); - if (this.graph) - this.sdfv_instance.fill_info( - findGraphElementByUUID(this.cfgList, uuid) - ); - } - + public updateCFGList() { // Update SDFG metadata this.cfgTree = {}; + this.cfgList = {}; + this.cfgList[0] = { + jsonObj: this.sdfg, + graph: null, + }; + this.doForAllSDFGElements( (_oGroup, oInfo, obj) => { const cfgId = (obj as JsonSDFGControlFlowRegion).cfg_list_id; if (obj.type === SDFGElementType.NestedSDFG && - obj.attributes.sdfg) + obj.attributes.sdfg) { this.cfgTree[obj.attributes.sdfg.cfg_list_id] = oInfo.sdfg.cfg_list_id; - else if (cfgId !== undefined && cfgId >= 0) + } else if (cfgId !== undefined && cfgId >= 0) { this.cfgTree[cfgId] = oInfo.cfgId; + this.cfgList[cfgId] = { + jsonObj: obj as JsonSDFGControlFlowRegion, + graph: null, + }; + } } ); } + public setSDFG(new_sdfg: JsonSDFG, layout: boolean = true): void { + this.sdfg = new_sdfg; + + // Update info box + if (this.selected_elements.length === 1) { + const uuid = getGraphElementUUID(this.selected_elements[0]); + if (this.graph) + this.sdfv_instance.fill_info( + findGraphElementByUUID(this.cfgList, uuid) + ); + } + + if (layout) { + this.updateCFGList(); + + this.add_loading_animation(); + setTimeout(() => { + this.relayout(); + }, 10); + + this.draw_async(); + } + } + // Set mouse events (e.g., click, drag, zoom) public set_mouse_handlers(): void { const canvas = this.canvas; @@ -1136,7 +1152,8 @@ export class SDFGRenderer extends EventEmitter { if (!this.ctx) throw new Error('No context found while performing layouting'); - this.cfgList = {}; + for (const cfgId in this.cfgList) + this.cfgList[cfgId].graph = null; this.graph = relayoutStateMachine( this.ctx, this.sdfg, this.sdfg, this.cfgList, this.state_parent_list, !SDFVSettings.showAccessNodes, undefined @@ -2708,7 +2725,7 @@ export class SDFGRenderer extends EventEmitter { // Do not move element individually if it is // moved together with its parent state const state_parent = - this.cfgList[list_id].graph.node( + this.cfgList[list_id].graph?.node( el.parent_id!.toString() ); if (state_parent && @@ -3028,7 +3045,7 @@ export class SDFGRenderer extends EventEmitter { if (obj instanceof AccessNode) { if (obj.hovered && hover_changed) { traverseSDFGScopes( - this.cfgList[obj.sdfg.cfg_list_id].graph, + this.cfgList[obj.sdfg.cfg_list_id].graph!, (node: any) => { // If node is a state, then visit sub-scope if (node instanceof State) @@ -3044,7 +3061,7 @@ export class SDFGRenderer extends EventEmitter { } else if (!obj.hovered && hover_changed) { traverseSDFGScopes( - this.cfgList[obj.sdfg.cfg_list_id].graph, + this.cfgList[obj.sdfg.cfg_list_id].graph!, (node: any) => { // If node is a state, then visit sub-scope if (node instanceof State) @@ -3269,11 +3286,11 @@ export class SDFGRenderer extends EventEmitter { this.emit( 'add_element', this.add_type, - get_uuid_graph_element( + getGraphElementUUID( foreground_elem ), undefined, - get_uuid_graph_element(start), + getGraphElementUUID(start), this.add_edge_start_conn ? this.add_edge_start_conn.data.name : undefined, @@ -3292,7 +3309,7 @@ export class SDFGRenderer extends EventEmitter { this.emit( 'add_element', this.add_type, - get_uuid_graph_element( + getGraphElementUUID( foreground_elem ), this.add_mode_lib || undefined @@ -3303,7 +3320,7 @@ export class SDFGRenderer extends EventEmitter { this.emit( 'add_element', this.add_type, - get_uuid_graph_element( + getGraphElementUUID( foreground_elem ) ); @@ -3707,19 +3724,25 @@ export class SDFGRenderer extends EventEmitter { // Add all contents of the CFG. const cfg: JsonSDFGControlFlowRegion = cfgNode.data.block; const ownCfgId = cfg.cfg_list_id; + cfgs.add(ownCfgId); if (!(ownCfgId in blocks)) blocks[ownCfgId] = new Set(); - for (const blockId of cfgNode.data.graph.nodes()) { - const block = cfgNode.data.graph.node(blockId); - if (block instanceof ControlFlowRegion) { - const nCfg: JsonSDFGControlFlowRegion = block.data.block; - const nCfgId = nCfg.cfg_list_id; - cfgs.add(nCfgId); - addCutoutCFG(ownCfgId, block); - } else { - addCutoutState(ownCfgId, block); + if (cfgNode.data.graph) { + for (const blockId of cfgNode.data.block.nodes.keys()) { + const block = cfgNode.data.graph.node(blockId); + if (block instanceof ControlFlowRegion) { + const nCfg: JsonSDFGControlFlowRegion = block.data.block; + const nCfgId = nCfg.cfg_list_id; + cfgs.add(nCfgId); + addCutoutCFG(ownCfgId, block); + } else { + addCutoutState(ownCfgId, block); + } } + } else { + for (const blockId of cfgNode.data.block.nodes.keys()) + blocks[ownCfgId].add(blockId); } blocks[cfgId].add(cfgNode.id); @@ -3747,7 +3770,8 @@ export class SDFGRenderer extends EventEmitter { // Clear selection and redraw this.deselect(); - if (Object.keys(nodes).length === 0) { // Nothing to cut out + if (Object.keys(nodes).length + Object.keys(blocks).length === 0) { + // Nothing to cut out this.draw_async(); return; } @@ -4045,11 +4069,8 @@ function relayoutStateMachine( (g as any).width = bb.width; (g as any).height = bb.height; - // Add CFG to global store. - cfgList[stateMachine.cfg_list_id] = { - jsonObj: stateMachine, - graph: g, - }; + // Add CFG graph to global store. + cfgList[stateMachine.cfg_list_id].graph = g; return g; } diff --git a/src/utils/sdfg/sdfg_utils.ts b/src/utils/sdfg/sdfg_utils.ts index 4bec9bf3..6316834d 100644 --- a/src/utils/sdfg/sdfg_utils.ts +++ b/src/utils/sdfg/sdfg_utils.ts @@ -10,6 +10,7 @@ import { JsonSDFGState } from '../../index'; import { + ControlFlowRegion, Edge, SDFGElement, SDFGElementType, @@ -42,18 +43,23 @@ export function findExitForEntry( * * @returns String containing the UUID */ -export function get_uuid_graph_element(element: SDFGElement | null): string { +export function getGraphElementUUID(element: SDFGElement | null): string { const undefined_val = -1; + const cfgId = ( + element instanceof ControlFlowRegion ? + element.data.block.cfg_list_id : + element?.cfg?.cfg_list_id + ) ?? undefined_val; if (element instanceof State) { return ( - element.sdfg.cfg_list_id + '/' + + cfgId + '/' + element.id + '/' + undefined_val + '/' + undefined_val ); } else if (element instanceof SDFGNode) { return ( - element.sdfg.cfg_list_id + '/' + + cfgId + '/' + element.parent_id + '/' + element.id + '/' + undefined_val @@ -63,7 +69,7 @@ export function get_uuid_graph_element(element: SDFGElement | null): string { if (element.parent_id !== null && element.parent_id !== undefined) parent_id = element.parent_id; return ( - element.sdfg.cfg_list_id + '/' + + cfgId + '/' + parent_id + '/' + undefined_val + '/' + element.id @@ -114,9 +120,11 @@ export function findGraphElementByUUID( return null; const graph = cfgList[cfgId].graph; + if (graph === null) + return null; let state = null; - if (stateId !== '-1' && graph !== undefined) + if (stateId !== '-1') state = graph.node(stateId); let element = null; diff --git a/src/utils/sdfg/traversal.ts b/src/utils/sdfg/traversal.ts index 9d59bcee..1c926d9d 100644 --- a/src/utils/sdfg/traversal.ts +++ b/src/utils/sdfg/traversal.ts @@ -12,7 +12,8 @@ import { * signature as `func`). **/ export function traverseSDFGScopes( - sdfg: DagreGraph, func: CallableFunction, postSubscopeFunc?: CallableFunction + sdfg: DagreGraph, func: CallableFunction, + postSubscopeFunc?: CallableFunction ): void { function scopesRecursive( graph: DagreGraph, nodes: string[], processedNodes?: Set