Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reorient view during relayout based on visible elements #163

Merged
merged 6 commits into from
Sep 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/renderer/canvas_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -382,10 +382,10 @@ export class CanvasManager {
if (parent_graph && !(el instanceof Edge)) {
// Find all the edges connected to the moving node
parent_graph.outEdges(el.id.toString())?.forEach(edge_id => {
out_edges.push(parent_graph.edge(edge_id));
out_edges.push(parent_graph!.edge(edge_id));
});
parent_graph.inEdges(el.id.toString())?.forEach(edge_id => {
in_edges.push(parent_graph.edge(edge_id));
in_edges.push(parent_graph!.edge(edge_id));
});
}

Expand Down
142 changes: 113 additions & 29 deletions src/renderer/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ declare const canvas2pdf: any;
declare const vscode: any | null;

export type SDFGElementGroup = ('states' | 'nodes' | 'edges' | 'isedges' |
'connectors' | 'controlFlowRegions' |
'controlFlowBlocks');
'connectors' | 'controlFlowRegions' |
'controlFlowBlocks');
export interface SDFGElementInfo {
sdfg: JsonSDFG,
id: number,
Expand Down Expand Up @@ -121,6 +121,13 @@ export type CFGListType = {
}
};

export type VisibleElementsType = {
type: string,
stateId: number,
cfgId: number,
id: number,
}[];

function check_valid_add_position(
type: SDFGElementType | null,
foreground_elem: SDFGElement | undefined | null, lib: any, _mousepos: any
Expand Down Expand Up @@ -697,7 +704,7 @@ export class SDFGRenderer extends EventEmitter {
add_btn.onclick = () => {
this.mouse_mode = 'add';
this.add_type =
<SDFGElementType> add_btn.getAttribute('type');
<SDFGElementType>add_btn.getAttribute('type');
this.add_mode_lib = null;
this.add_edge_start = null;
this.add_edge_start_conn = null;
Expand Down Expand Up @@ -1242,10 +1249,15 @@ export class SDFGRenderer extends EventEmitter {
}

// Re-layout graph and nested graphs
public relayout(): DagreGraph {
public relayout(instigator: SDFGElement | null = null): DagreGraph {
if (!this.ctx)
throw new Error('No context found while performing layouting');

// Collect currently-visible elements for reorientation
const elements = this.getVisibleElementsAsObjects(true);
if (instigator)
elements.push(instigator);

for (const cfgId in this.cfgList) {
this.cfgList[cfgId].graph = null;
this.cfgList[cfgId].nsdfgNode = null;
Expand All @@ -1259,6 +1271,10 @@ export class SDFGRenderer extends EventEmitter {
for (const bId of this.graph.nodes())
topLevelBlocks.push(this.graph.node(bId));
this.graphBoundingBox = boundingBox(topLevelBlocks);

// Reorient view based on an approximate set of visible elements
this.reorient(elements);

this.onresize();

this.update_fast_memlet_lookup();
Expand Down Expand Up @@ -1287,6 +1303,41 @@ export class SDFGRenderer extends EventEmitter {
return this.graph;
}

public reorient(old_visible_elements: SDFGElement[]): void {
// Reorient view based on an approximate set of visible elements

// Nothing to reorient to
if (!old_visible_elements || old_visible_elements.length === 0)
return;

// If the current view contains everything that was visible before,
// no need to change anything.
const new_visible_elements = this.getVisibleElementsAsObjects(true);
const old_nodes = old_visible_elements.filter(x => (
x instanceof ControlFlowBlock ||
x instanceof SDFGNode));
const new_nodes = new_visible_elements.filter(x => (
x instanceof ControlFlowBlock ||
x instanceof SDFGNode));
const old_set = new Set(old_nodes.map(x => x.guid()));
const new_set = new Set(new_nodes.map(x => x.guid()));
const diff = old_set.difference(new_set);
if (diff.size === 0)
return;

// Reorient based on old visible elements refreshed to new locations
const old_elements_in_new_layout: SDFGElement[] = [];
this.doForAllGraphElements((group: SDFGElementGroup,
info: GraphElementInfo, elem: SDFGElement) => {
if (elem instanceof ControlFlowBlock || elem instanceof SDFGNode) {
const guid = elem.guid();
if (guid && old_set.has(guid))
old_elements_in_new_layout.push(elem);
}
});
this.zoom_to_view(old_elements_in_new_layout, true, undefined, false);
}

public translateMovedElements(): void {
if (!this.graph)
return;
Expand Down Expand Up @@ -1378,7 +1429,8 @@ export class SDFGRenderer extends EventEmitter {
// Change translation and scale such that the chosen elements
// (or entire graph if null) is in view
public zoom_to_view(
elements: any = null, animate: boolean = true, padding?: number
elements: any = null, animate: boolean = true, padding?: number,
redraw: boolean = true
): void {
if (!elements || elements.length === 0) {
elements = this.graph?.nodes().map(x => this.graph?.node(x));
Expand All @@ -1400,7 +1452,8 @@ export class SDFGRenderer extends EventEmitter {
const bb = boundingBox(elements, paddingAbs);
this.canvas_manager?.set_view(bb, animate);

this.draw_async();
if (redraw)
this.draw_async();
}

public zoomToFitWidth(): void {
Expand Down Expand Up @@ -1526,7 +1579,7 @@ export class SDFGRenderer extends EventEmitter {

traverseSDFGScopes(
this.graph, (node: SDFGNode, _: DagreGraph) => {
if(node.attributes().is_collapsed) {
if (node.attributes().is_collapsed) {
node.attributes().is_collapsed = false;
return false;
}
Expand Down Expand Up @@ -2047,12 +2100,7 @@ export class SDFGRenderer extends EventEmitter {
this.draw_async();
}

public getVisibleElements(): {
type: string,
stateId: number,
cfgId: number,
id: number,
}[] {
public getVisibleElements(): VisibleElementsType {
if (!this.canvas_manager)
return [];

Expand Down Expand Up @@ -2098,6 +2146,37 @@ export class SDFGRenderer extends EventEmitter {
return elements;
}

public getVisibleElementsAsObjects(
entirely_visible: boolean
): SDFGElement[] {
if (!this.canvas_manager)
return [];

const curx = this.canvas_manager.mapPixelToCoordsX(0);
const cury = this.canvas_manager.mapPixelToCoordsY(0);
const canvasw = this.canvas?.width;
const canvash = this.canvas?.height;
let endx = null;
if (canvasw)
endx = this.canvas_manager.mapPixelToCoordsX(canvasw);
let endy = null;
if (canvash)
endy = this.canvas_manager.mapPixelToCoordsY(canvash);
const curw = (endx ? endx : 0) - curx;
const curh = (endy ? endy : 0) - cury;
const elements: any[] = [];
this.doForIntersectedElements(
curx, cury, curw, curh,
(group, objInfo, _obj) => {
if (entirely_visible &&
!_obj.contained_in(curx, cury, curw, curh))
return;
elements.push(_obj);
}
);
return elements;
}

public doForVisibleElements(func: GraphElemFunction): void {
if (!this.canvas_manager)
return;
Expand Down Expand Up @@ -2675,7 +2754,7 @@ export class SDFGRenderer extends EventEmitter {
}
} else if (e instanceof InterstateEdge) {
if (!e.parentElem ||
(e.parentElem && e.parentElem instanceof SDFG)) {
(e.parentElem && e.parentElem instanceof SDFG)) {
e.sdfg.edges = e.sdfg.edges.filter(
(_, ind: number) => ind !== e.id
);
Expand Down Expand Up @@ -2770,7 +2849,12 @@ export class SDFGRenderer extends EventEmitter {

// Toggles collapsed state of foreground_elem if applicable.
// Returns true if re-layout occured and re-draw is necessary.
public toggle_element_collapse(foreground_elem: any): boolean {
public toggle_element_collapse(
foreground_elem: SDFGElement | null
): boolean {
if (!foreground_elem)
return false;

const sdfg = (foreground_elem ? foreground_elem.sdfg : null);
let sdfg_elem = null;
if (foreground_elem instanceof State) {
Expand All @@ -2783,29 +2867,29 @@ export class SDFGRenderer extends EventEmitter {
// If a scope exit node, use entry instead
if (sdfg_elem.type.endsWith('Exit') &&
foreground_elem.parent_id !== null) {
sdfg_elem = sdfg.nodes[foreground_elem.parent_id].nodes[
sdfg_elem.scope_entry
];
const parent = sdfg!.nodes[foreground_elem.parent_id];
if (parent.nodes)
sdfg_elem = parent.nodes[sdfg_elem.scope_entry];
}
} else {
sdfg_elem = null;
}

// Toggle collapsed state
if (foreground_elem.COLLAPSIBLE) {
if ('is_collapsed' in sdfg_elem.attributes) {
sdfg_elem.attributes.is_collapsed =
!sdfg_elem.attributes.is_collapsed;
} else {
sdfg_elem.attributes['is_collapsed'] = true;
}

this.emit('collapse_state_changed');

// Re-layout SDFG
this.add_loading_animation();
setTimeout(() => {
this.relayout();
if ('is_collapsed' in sdfg_elem.attributes) {
sdfg_elem.attributes.is_collapsed =
!sdfg_elem.attributes.is_collapsed;
} else {
sdfg_elem.attributes['is_collapsed'] = true;
}

this.relayout(foreground_elem);
this.draw_async();
}, 10);

Expand Down Expand Up @@ -3402,7 +3486,7 @@ export class SDFGRenderer extends EventEmitter {
if (obj.hovered && hover_changed &&
obj instanceof SDFGNode &&
(obj.in_summary_has_effect ||
obj.out_summary_has_effect)) {
obj.out_summary_has_effect)) {
// Setting these to false will cause the summary
// symbol not to be drawn in renderer_elements.ts
obj.summarize_in_edges = false;
Expand Down Expand Up @@ -3833,7 +3917,7 @@ export class SDFGRenderer extends EventEmitter {
return this.cfgList;
}

public getCFGTree(): { [key: number]: number} {
public getCFGTree(): { [key: number]: number } {
return this.cfgTree;
}

Expand Down Expand Up @@ -4462,7 +4546,7 @@ function relayoutSDFGState(
// If it's a nested SDFG, we need to record the node as all of its
// state's parent node.
if ((node.type === SDFGElementType.NestedSDFG ||
node.type === SDFGElementType.ExternalNestedSDFG) &&
node.type === SDFGElementType.ExternalNestedSDFG) &&
node.attributes.sdfg && node.attributes.sdfg.type !== 'SDFGShell') {
stateParentList[node.attributes.sdfg.cfg_list_id] = obj;
sdfgList[node.attributes.sdfg.cfg_list_id].nsdfgNode = obj;
Expand Down
16 changes: 13 additions & 3 deletions src/renderer/renderer_elements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,12 @@ export class SDFGElement {
return this.data.label;
}

public guid(): string {
// If GUID does not exist, fall back to element ID
return this.cfg?.cfg_list_id + '/' + (
this.parent_id ?? -1) + '/' + this.id;
}

// Text used for matching the element during a search
public text_for_find(): string {
return this.label();
Expand Down Expand Up @@ -1399,8 +1405,8 @@ export abstract class Edge extends SDFGElement {
};

// Check if the two rectangles intersect
if (r.x + r.w >= x && r.x <= x+w &&
r.y + r.h >= y && r.y <= y+h)
if (r.x + r.w >= x && r.x <= x + w &&
r.y + r.h >= y && r.y <= y + h)
return true;
}
return false;
Expand Down Expand Up @@ -1811,6 +1817,10 @@ export class Connector extends SDFGElement {
public linkedElem?: SDFGElement;
public connectorType: 'in' | 'out' = 'in';

public guid(): string {
return ''; // Connectors have no GUID
}

public draw(
renderer: SDFGRenderer, ctx: CanvasRenderingContext2D,
_mousepos?: Point2D, edge: Edge | null = null
Expand Down Expand Up @@ -3350,7 +3360,7 @@ export function drawOctagon(
export 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);
ctx.ellipse(x + w / 2, y + h / 2, w / 2, h / 2, 0, 0, 2 * Math.PI);
}

export function drawTrapezoid(
Expand Down
Loading