diff --git a/src/overlays/avg_parallelism_overlay.ts b/src/overlays/avg_parallelism_overlay.ts index 9daf856..1ebd935 100644 --- a/src/overlays/avg_parallelism_overlay.ts +++ b/src/overlays/avg_parallelism_overlay.ts @@ -3,8 +3,6 @@ import { DagreGraph, Point2D, - SDFVSettings, - SimpleRect, SymbolMap, getGraphElementUUID, } from '../index'; @@ -14,15 +12,13 @@ import { SDFGRenderer, } from '../renderer/renderer'; import { + ConditionalBlock, ControlFlowBlock, - ControlFlowRegion, Edge, NestedSDFG, SDFGElement, SDFGNode, - State, } from '../renderer/renderer_elements'; -import { SDFV } from '../sdfv'; import { getTempColorHslString } from '../utils/utils'; import { GenericSdfgOverlay, OverlayType } from './generic_sdfg_overlay'; @@ -79,51 +75,68 @@ export class AvgParallelismOverlay extends GenericSdfgOverlay { g: DagreGraph, symbol_map: SymbolMap, avg_parallelism_values: number[] ): void { g.nodes().forEach(v => { - const state = g.node(v); + const node = g.node(v); this.calculate_avg_parallelism_node( - state, symbol_map, avg_parallelism_values + node, symbol_map, avg_parallelism_values ); - const state_graph = state.data.graph; - if (state_graph) { - state_graph.nodes().forEach((v: string) => { - const node = state_graph.node(v); - if (node instanceof NestedSDFG) { - const nested_symbols_map: SymbolMap = {}; - const mapping = - node.data.node.attributes.symbol_mapping ?? {}; - // Translate the symbol mappings for the nested SDFG - // based on the mapping described on the node. - Object.keys(mapping).forEach((symbol: string) => { - nested_symbols_map[symbol] = - this.symbolResolver.parse_symbol_expression( - mapping[symbol], - symbol_map - ); - }); - // Merge in the parent mappings. - Object.keys(symbol_map).forEach((symbol) => { - if (!(symbol in nested_symbols_map)) - nested_symbols_map[symbol] = symbol_map[symbol]; - }); - - this.calculate_avg_parallelism_node( - node, - nested_symbols_map, - avg_parallelism_values - ); + if (node instanceof ConditionalBlock) { + for (const [_, branch] of node.branches) { + this.calculate_avg_parallelism_node( + branch, symbol_map, avg_parallelism_values + ); + if (branch.data.graph) { this.calculate_avg_parallelism_graph( - node.data.graph, - nested_symbols_map, - avg_parallelism_values - ); - } else { - this.calculate_avg_parallelism_node( - node, - symbol_map, + branch.data.graph, symbol_map, avg_parallelism_values ); } - }); + } + } else { + const state_graph = node.data.graph; + if (state_graph) { + state_graph.nodes().forEach((v: string) => { + const node = state_graph.node(v); + if (node instanceof NestedSDFG) { + const nested_symbols_map: SymbolMap = {}; + const mapping = + node.data.node.attributes.symbol_mapping ?? {}; + // Translate the symbol mappings for the nested SDFG + // based on the mapping described on the node. + Object.keys(mapping).forEach((symbol: string) => { + nested_symbols_map[symbol] = + this.symbolResolver.parse_symbol_expression( + mapping[symbol], + symbol_map + ); + }); + // Merge in the parent mappings. + Object.keys(symbol_map).forEach((symbol) => { + if (!(symbol in nested_symbols_map)) { + nested_symbols_map[symbol] = symbol_map[ + symbol + ]; + } + }); + + this.calculate_avg_parallelism_node( + node, + nested_symbols_map, + avg_parallelism_values + ); + this.calculate_avg_parallelism_graph( + node.data.graph, + nested_symbols_map, + avg_parallelism_values + ); + } else { + this.calculate_avg_parallelism_node( + node, + symbol_map, + avg_parallelism_values + ); + } + }); + } } }); } @@ -161,13 +174,13 @@ export class AvgParallelismOverlay extends GenericSdfgOverlay { this.renderer.draw_async(); } - public shadeNode(node: SDFGNode, ctx: CanvasRenderingContext2D): void { - const avgParallelism = node.data.avg_parallelism; - const avgParallelismString = node.data.avg_parallelism_string; + private shadeElem(elem: SDFGNode, ctx: CanvasRenderingContext2D): void { + const avgParallelism = elem.data.avg_parallelism; + const avgParallelismString = elem.data.avg_parallelism_string; const mousepos = this.renderer.get_mousepos(); if (avgParallelismString !== undefined && mousepos && - node.intersect(mousepos.x, mousepos.y)) { + elem.intersect(mousepos.x, mousepos.y)) { // Show the computed avg_parallelism value if applicable. if (isNaN(avgParallelismString) && avgParallelism !== undefined) { @@ -196,7 +209,7 @@ export class AvgParallelismOverlay extends GenericSdfgOverlay { // for this node's avg_parallelism, that means that there's an // unresolved symbol. Shade the node grey to indicate that. if (avgParallelismString !== undefined) { - node.shade(this.renderer, ctx, 'gray'); + elem.shade(this.renderer, ctx, 'gray'); return; } else { return; @@ -212,83 +225,23 @@ export class AvgParallelismOverlay extends GenericSdfgOverlay { 1 - this.getSeverityValue(avgParallelism) ); - node.shade(this.renderer, ctx, color); + elem.shade(this.renderer, ctx, color); } - public recursivelyShadeCFG( - graph: DagreGraph, - ctx: CanvasRenderingContext2D, - ppp: number, - visibleRect: SimpleRect + protected shadeNode( + node: SDFGNode, ctx: CanvasRenderingContext2D, ...args: any[] ): void { - // First go over visible states, skipping invisible ones. We only draw - // something if the state is collapsed or we're zoomed out far enough. - // In that case, we draw the avg_parallelism calculated for the entire - // state. If expanded or zoomed in close enough, we traverse inside. - graph.nodes().forEach(v => { - const block: ControlFlowBlock = graph.node(v); - - // If the node's invisible, we skip it. - if (this.renderer.viewportOnly && !block.intersect( - visibleRect.x, visibleRect.y, - visibleRect.w, visibleRect.h - )) - return; - - const stateppp = Math.sqrt(block.width * block.height) / ppp; - if ((this.renderer.adaptiveHiding && - (stateppp < SDFVSettings.get('nestedLOD'))) || - block.attributes()?.is_collapsed) { - this.shadeNode(block, ctx); - } else if (block instanceof State) { - const stateGraph = block.data.graph; - if (stateGraph) { - stateGraph.nodes().forEach((v: any) => { - const node = stateGraph.node(v); - - // Skip the node if it's not visible. - if (this.renderer.viewportOnly && - !node.intersect( - visibleRect.x, visibleRect.y, visibleRect.w, - visibleRect.h - )) - return; + this.shadeElem(node, ctx); + } - if (node instanceof NestedSDFG && - !node.data.node.attributes.is_collapsed) { - const nodeppp = Math.sqrt( - node.width * node.height - ) / ppp; - if (this.renderer.adaptiveHiding && - nodeppp < - SDFVSettings.get('nestedLOD')) { - this.shadeNode(node, ctx); - } else if (node.attributes().sdfg && - node.attributes().sdfg.type !== 'SDFGShell') { - this.recursivelyShadeCFG( - node.data.graph, ctx, ppp, visibleRect - ); - } - } else { - this.shadeNode(node, ctx); - } - }); - } - } else if (block instanceof ControlFlowRegion) { - this.recursivelyShadeCFG( - block.data.graph, ctx, ppp, visibleRect - ); - } - }); + protected shadeBlock( + block: ControlFlowBlock, ctx: CanvasRenderingContext2D, ...args: any[] + ): void { + this.shadeElem(block, ctx); } public draw(): void { - const graph = this.renderer.get_graph(); - const ppp = this.renderer.get_canvas_manager()?.points_per_pixel(); - const context = this.renderer.get_context(); - const visible_rect = this.renderer.get_visible_rect(); - if (graph && ppp !== undefined && context && visible_rect) - this.recursivelyShadeCFG(graph, context, ppp, visible_rect); + this.shadeSDFG(); } public on_mouse_event( diff --git a/src/overlays/depth_overlay.ts b/src/overlays/depth_overlay.ts index d4b7660..7d3ebf1 100644 --- a/src/overlays/depth_overlay.ts +++ b/src/overlays/depth_overlay.ts @@ -3,8 +3,6 @@ import { DagreGraph, Point2D, - SDFVSettings, - SimpleRect, SymbolMap, getGraphElementUUID, } from '../index'; @@ -14,12 +12,13 @@ import { SDFGRenderer, } from '../renderer/renderer'; import { + ConditionalBlock, + ControlFlowBlock, Edge, NestedSDFG, SDFGElement, SDFGNode, } from '../renderer/renderer_elements'; -import { SDFV } from '../sdfv'; import { getTempColorHslString } from '../utils/utils'; import { GenericSdfgOverlay, OverlayType } from './generic_sdfg_overlay'; @@ -74,49 +73,63 @@ export class DepthOverlay extends GenericSdfgOverlay { g: DagreGraph, symbol_map: SymbolMap, depth_values: number[] ): void { g.nodes().forEach(v => { - const state = g.node(v); - this.calculate_depth_node(state, symbol_map, depth_values); - const state_graph = state.data.graph; - if (state_graph) { - state_graph.nodes().forEach((v: string) => { - const node = state_graph.node(v); - if (node instanceof NestedSDFG) { - const nested_symbols_map: SymbolMap = {}; - const mapping = - node.data.node.attributes.symbol_mapping ?? {}; - // Translate the symbol mappings for the nested SDFG - // based on the mapping described on the node. - Object.keys(mapping).forEach((symbol: string) => { - nested_symbols_map[symbol] = - this.symbolResolver.parse_symbol_expression( - mapping[symbol], - symbol_map - ); - }); - // Merge in the parent mappings. - Object.keys(symbol_map).forEach((symbol) => { - if (!(symbol in nested_symbols_map)) - nested_symbols_map[symbol] = symbol_map[symbol]; - }); - - this.calculate_depth_node( - node, - nested_symbols_map, - depth_values - ); + const node = g.node(v); + this.calculate_depth_node(node, symbol_map, depth_values); + if (node instanceof ConditionalBlock) { + for (const [_, branch] of node.branches) { + this.calculate_depth_node(branch, symbol_map, depth_values); + if (branch.data.graph) { this.calculate_depth_graph( - node.data.graph, - nested_symbols_map, - depth_values - ); - } else { - this.calculate_depth_node( - node, - symbol_map, - depth_values + branch.data.graph, symbol_map, depth_values ); } - }); + } + } else { + const state_graph = node.data.graph; + if (state_graph) { + state_graph.nodes().forEach((v: string) => { + const node = state_graph.node(v); + if (node instanceof NestedSDFG) { + const nested_symbols_map: SymbolMap = {}; + const mapping = + node.data.node.attributes.symbol_mapping ?? {}; + // Translate the symbol mappings for the nested SDFG + // based on the mapping described on the node. + Object.keys(mapping).forEach((symbol: string) => { + nested_symbols_map[symbol] = + this.symbolResolver.parse_symbol_expression( + mapping[symbol], + symbol_map + ); + }); + // Merge in the parent mappings. + Object.keys(symbol_map).forEach((symbol) => { + if (!(symbol in nested_symbols_map)) { + nested_symbols_map[symbol] = symbol_map[ + symbol + ]; + } + }); + + this.calculate_depth_node( + node, + nested_symbols_map, + depth_values + ); + this.calculate_depth_graph( + node.data.graph, + nested_symbols_map, + depth_values + ); + } else { + this.calculate_depth_node( + node, + symbol_map, + depth_values + ); + } + }); + } } }); } @@ -152,13 +165,13 @@ export class DepthOverlay extends GenericSdfgOverlay { this.renderer.draw_async(); } - public shade_node(node: SDFGNode, ctx: CanvasRenderingContext2D): void { - const depth = node.data.depth; - const depth_string = node.data.depth_string; + private shadeElem(elem: SDFGElement, ctx: CanvasRenderingContext2D): void { + const depth = elem.data.depth; + const depth_string = elem.data.depth_string; const mousepos = this.renderer.get_mousepos(); if (depth_string !== undefined && mousepos && - node.intersect(mousepos.x, mousepos.y)) { + elem.intersect(mousepos.x, mousepos.y)) { // Show the computed Depth value if applicable. if (isNaN(depth_string) && depth !== undefined) { this.renderer.set_tooltip(() => { @@ -183,7 +196,7 @@ export class DepthOverlay extends GenericSdfgOverlay { // node's Depth, that means that there's an unresolved symbol. Shade // the node grey to indicate that. if (depth_string !== undefined) { - node.shade(this.renderer, ctx, 'gray'); + elem.shade(this.renderer, ctx, 'gray'); return; } else { return; @@ -197,79 +210,23 @@ export class DepthOverlay extends GenericSdfgOverlay { // Calculate the severity color. const color = getTempColorHslString(this.getSeverityValue(depth)); - node.shade(this.renderer, ctx, color); + elem.shade(this.renderer, ctx, color); } - public recursively_shade_sdfg( - graph: DagreGraph, - ctx: CanvasRenderingContext2D, - ppp: number, - visible_rect: SimpleRect + protected shadeNode( + node: SDFGNode, ctx: CanvasRenderingContext2D, ...args: any[] ): void { - // First go over visible states, skipping invisible ones. We only draw - // something if the state is collapsed or we're zoomed out far enough. - // In that case, we draw the Depth calculated for the entire state. - // If it's expanded or zoomed in close enough, we traverse inside. - graph.nodes().forEach(v => { - const state = graph.node(v); - - // If the node's invisible, we skip it. - if (this.renderer.viewportOnly && !state.intersect( - visible_rect.x, visible_rect.y, - visible_rect.w, visible_rect.h - )) - return; - - const stateppp = Math.sqrt(state.width * state.height) / ppp; - if ((this.renderer.adaptiveHiding && - (stateppp < SDFVSettings.get('nestedLOD'))) || - state.data.state.attributes.is_collapsed) { - this.shade_node(state, ctx); - } else { - const state_graph = state.data.graph; - if (state_graph) { - state_graph.nodes().forEach((v: any) => { - const node = state_graph.node(v); - - // Skip the node if it's not visible. - if (this.renderer.viewportOnly && - !node.intersect( - visible_rect.x, visible_rect.y, visible_rect.w, - visible_rect.h - )) - return; + this.shadeElem(node, ctx); + } - if (node instanceof NestedSDFG && - !node.data.node.attributes.is_collapsed) { - const nodeppp = Math.sqrt( - node.width * node.height - ) / ppp; - if (this.renderer.adaptiveHiding && - nodeppp < - SDFVSettings.get('nestedLOD')) { - this.shade_node(node, ctx); - } else if (node.attributes().sdfg && - node.attributes().sdfg.type !== 'SDFGShell') { - this.recursively_shade_sdfg( - node.data.graph, ctx, ppp, visible_rect - ); - } - } else { - this.shade_node(node, ctx); - } - }); - } - } - }); + protected shadeBlock( + block: ControlFlowBlock, ctx: CanvasRenderingContext2D, ...args: any[] + ): void { + this.shadeElem(block, ctx); } public draw(): void { - const graph = this.renderer.get_graph(); - const ppp = this.renderer.get_canvas_manager()?.points_per_pixel(); - const context = this.renderer.get_context(); - const visible_rect = this.renderer.get_visible_rect(); - if (graph && ppp !== undefined && context && visible_rect) - this.recursively_shade_sdfg(graph, context, ppp, visible_rect); + this.shadeSDFG(); } public on_mouse_event( diff --git a/src/overlays/diff_overlay.ts b/src/overlays/diff_overlay.ts index e91b202..b9f5071 100644 --- a/src/overlays/diff_overlay.ts +++ b/src/overlays/diff_overlay.ts @@ -1,23 +1,16 @@ // Copyright 2019-2024 ETH Zurich and the DaCe authors. All rights reserved. -import { - DagreGraph, - JsonSDFG, - Point2D, - SDFVSettings, - SimpleRect, -} from '../index'; +import { Point2D } from '../index'; import { GraphElementInfo, SDFGElementGroup, SDFGRenderer, } from '../renderer/renderer'; import { - NestedSDFG, SDFGNode, SDFGElement, - SDFGElementType, Edge, + ControlFlowBlock, } from '../renderer/renderer_elements'; import { DiffMap } from '../sdfg_diff_viewer'; import { GenericSdfgOverlay, OverlayType } from './generic_sdfg_overlay'; @@ -27,10 +20,6 @@ export class DiffOverlay extends GenericSdfgOverlay { public static readonly type: OverlayType = OverlayType.BOTH; public readonly olClass: typeof GenericSdfgOverlay = DiffOverlay; - private readonly CHANGED_COLOR = 'orange'; - private readonly ADDED_COLOR = 'green'; - private readonly REMOVED_COLOR = 'red'; - public constructor( renderer: SDFGRenderer, private readonly diffMap?: DiffMap @@ -45,7 +34,7 @@ export class DiffOverlay extends GenericSdfgOverlay { } public shadeElem( - elem: Edge | SDFGNode, ctx: CanvasRenderingContext2D + elem: SDFGElement, ctx: CanvasRenderingContext2D ): void { if (this.diffMap?.addedKeys.has(elem.guid())) { elem.shade(this.renderer, ctx, this.renderer.getCssProperty( @@ -62,110 +51,26 @@ export class DiffOverlay extends GenericSdfgOverlay { } } - public recursivelyShadeCFG( - sdfg: JsonSDFG, graph: DagreGraph, ctx: CanvasRenderingContext2D, - ppp: number, visibleRect: SimpleRect + protected shadeBlock( + block: ControlFlowBlock, ctx: CanvasRenderingContext2D, ...args: any[] ): void { - // First go over visible states, skipping invisible ones. We only draw - // something if the state is collapsed or we're zoomed out far enough. - // In that case, we overlay the correct grouping color(s). - // If it's expanded or zoomed in close enough, we traverse inside. - if (!graph) - return; - - graph?.nodes().forEach(v => { - const block = graph.node(v); - - // If the node's invisible, we skip it. - if (this.renderer.viewportOnly && !block.intersect( - visibleRect.x, visibleRect.y, - visibleRect.w, visibleRect.h - )) - return; - - const blockppp = Math.sqrt(block.width * block.height) / ppp; - if ((this.renderer.adaptiveHiding && - (blockppp < SDFVSettings.get('nestedLOD'))) || - block.attributes().is_collapsed - ) { - this.shadeElem(block, ctx); - } else { - if (block.type() === SDFGElementType.SDFGState) { - const stateGraph = block.data.graph; - stateGraph?.nodes().forEach((v: string) => { - const node = stateGraph.node(v); - - // Skip the node if it's not visible. - if (this.renderer.viewportOnly && !node.intersect( - visibleRect.x, - visibleRect.y, visibleRect.w, visibleRect.h - )) - return; - - if (node.attributes().is_collapsed || - (this.renderer.adaptiveHiding && - ppp > SDFVSettings.get('nodeLOD'))) { - this.shadeElem(node, ctx); - } else { - if (node instanceof NestedSDFG && - node.attributes().sdfg && - node.attributes().sdfg.type !== 'SDFGShell' - ) { - this.recursivelyShadeCFG( - node.data.node.attributes.sdfg, - node.data.graph, ctx, ppp, visibleRect - ); - } else { - this.shadeElem(node, ctx); - } - } - }); - - stateGraph?.edges().forEach((v: any) => { - const edge = stateGraph.edge(v); - - // Skip if edge is invisible, or zoomed out far - if (this.renderer.adaptiveHiding && (!edge.intersect( - visibleRect.x, visibleRect.y, - visibleRect.w, visibleRect.h - ) || ppp > SDFVSettings.get('edgeLOD'))) - return; - - this.shadeElem(edge, ctx); - }); - } else { - this.recursivelyShadeCFG( - sdfg, block.data.graph, ctx, ppp, visibleRect - ); - } - } - }); - - graph?.edges().forEach((v: any) => { - const edge = graph.edge(v) as Edge; + this.shadeElem(block, ctx); + } - // Skip if edge is invisible, or zoomed out far - if (this.renderer.adaptiveHiding && (!edge.intersect( - visibleRect.x, visibleRect.y, - visibleRect.w, visibleRect.h - ) || ppp > SDFVSettings.get('edgeLOD'))) - return; + protected shadeNode( + node: SDFGNode, ctx: CanvasRenderingContext2D, ...args: any[] + ): void { + this.shadeElem(node, ctx); + } - this.shadeElem(edge, ctx); - }); + protected shadeEdge( + edge: Edge, ctx: CanvasRenderingContext2D, ...args: any[] + ): void { + this.shadeElem(edge, ctx); } public draw(): void { - const sdfg = this.renderer.get_sdfg(); - const graph = this.renderer.get_graph(); - const ppp = this.renderer.get_canvas_manager()?.points_per_pixel(); - const context = this.renderer.get_context(); - const visible_rect = this.renderer.get_visible_rect(); - if (graph && ppp !== undefined && context && visible_rect) { - this.recursivelyShadeCFG( - sdfg, graph, context, ppp, visible_rect - ); - } + this.shadeSDFG(); } public on_mouse_event( diff --git a/src/overlays/generic_sdfg_overlay.ts b/src/overlays/generic_sdfg_overlay.ts index 2ee7b2b..91bca13 100644 --- a/src/overlays/generic_sdfg_overlay.ts +++ b/src/overlays/generic_sdfg_overlay.ts @@ -1,14 +1,23 @@ // Copyright 2019-2024 ETH Zurich and the DaCe authors. All rights reserved. import { log, mean, median } from 'mathjs'; -import { Point2D } from '../index'; +import { DagreGraph, Point2D, SDFVSettings, SimpleRect } from '../index'; import { OverlayManager, SymbolResolver } from '../overlay_manager'; import { GraphElementInfo, SDFGElementGroup, SDFGRenderer, } from '../renderer/renderer'; -import { SDFGElement } from '../renderer/renderer_elements'; +import { + ConditionalBlock, + ControlFlowBlock, + ControlFlowRegion, + Edge, + NestedSDFG, + SDFGElement, + SDFGNode, + State, +} from '../renderer/renderer_elements'; declare const vscode: any; @@ -39,6 +48,153 @@ export class GenericSdfgOverlay { this.heatmap_hist_buckets = []; } + protected shadeBlock( + block: ControlFlowBlock, ctx: CanvasRenderingContext2D, ...args: any[] + ): void { + return; + } + + protected shadeNode( + node: SDFGNode, ctx: CanvasRenderingContext2D, ...args: any[] + ): void { + return; + } + + protected shadeEdge( + edge: Edge, ctx: CanvasRenderingContext2D, ...args: any[] + ): void { + return; + } + + protected shadeGraph( + graph: DagreGraph, ppp: number, ctx: CanvasRenderingContext2D, + vRect: SimpleRect, predicate: (elem: SDFGElement) => boolean, + shadeIfNestedVisible: boolean = false, ...args: any[] + ): void { + // Go over visible control flow blocks, skipping invisible ones. + for (const v of graph.nodes()) { + const block: ControlFlowBlock = graph.node(v); + + // If the node's invisible, we skip it. + if (this.renderer.viewportOnly && !block.intersect( + vRect.x, vRect.y, vRect.w, vRect.h + )) + continue; + + const blockppp = Math.sqrt(block.width * block.height) / ppp; + if ((this.renderer.adaptiveHiding && + (blockppp < SDFVSettings.get('nestedLOD'))) || + block.attributes()?.is_collapsed) { + // The block is collapsed or too small, so we don't need to + // traverse its insides. + if (predicate(block)) + this.shadeBlock(block, ctx, args); + continue; + } else if (shadeIfNestedVisible && predicate(block)) { + this.shadeBlock(block, ctx, args); + } + + if (block instanceof State) { + const stateGraph = block.data.graph; + if (!stateGraph) + continue; + for (const stateV of stateGraph.nodes()) { + const node = stateGraph.node(stateV); + + // Skip the node if it's not visible. + if (this.renderer.viewportOnly && !node.intersect( + vRect.x, vRect.y, vRect.w, vRect.h + )) + continue; + + if (node instanceof NestedSDFG && + node.attributes().sdfg && + node.attributes().sdfg.type !== 'SDFGShell') { + if (shadeIfNestedVisible && predicate(node)) + this.shadeNode(node, ctx, args); + + this.shadeGraph( + node.data.graph, ppp, ctx, vRect, predicate, + shadeIfNestedVisible, args + ); + } else { + if (!this.renderer.adaptiveHiding || + ppp < SDFVSettings.get('nodeLOD')) { + if (predicate(node)) + this.shadeNode(node, ctx, args); + } + } + } + + if (this.olClass.type === OverlayType.EDGE || + this.olClass.type === OverlayType.BOTH) { + for (const e of stateGraph.edges()) { + const edge: Edge = stateGraph.edge(e); + + // Skip if edge is invisible, or zoomed out far + if (this.renderer.adaptiveHiding && (!edge.intersect( + vRect.x, vRect.y, vRect.w, vRect.h + ) || ppp > SDFVSettings.get('edgeLOD'))) + continue; + + if (predicate(edge)) + this.shadeEdge(edge, ctx, args); + } + } + } else if (block instanceof ControlFlowRegion) { + if (block.data.graph) { + this.shadeGraph( + block.data.graph, ppp, ctx, vRect, predicate, + shadeIfNestedVisible, args + ); + } + } else if (block instanceof ConditionalBlock) { + for (const [_, branch] of block.branches) { + if (shadeIfNestedVisible && predicate(branch)) + this.shadeBlock(branch, ctx, args); + + if (branch.data.graph) { + this.shadeGraph( + branch.data.graph, ppp, ctx, vRect, predicate, + shadeIfNestedVisible, args + ); + } + } + } + } + + if (this.olClass.type === OverlayType.EDGE || + this.olClass.type === OverlayType.BOTH) { + for (const e of graph.edges()) { + const edge: Edge = (graph as any).edge(e); + + // Skip if edge is invisible, or zoomed out far + if (this.renderer.adaptiveHiding && (!edge.intersect( + vRect.x, vRect.y, vRect.w, vRect.h + ) || ppp > SDFVSettings.get('edgeLOD'))) + continue; + + if (predicate(edge)) + this.shadeEdge(edge, ctx, args); + } + } + } + + protected shadeSDFG( + predicate: (elem: SDFGElement) => boolean = () => true, + shadeIfNestedVisible: boolean = false, ...args: any[] + ): void { + const g = this.renderer.get_graph(); + const ppp = this.renderer.get_canvas_manager()?.points_per_pixel(); + const ctx = this.renderer.get_context(); + const vRect = this.renderer.get_visible_rect(); + if (g === null || ppp === undefined || ctx === null || vRect === null) + return; + this.shadeGraph( + g, ppp, ctx, vRect, predicate, shadeIfNestedVisible, args + ); + } + public draw(): void { return; } diff --git a/src/overlays/logical_group_overlay.ts b/src/overlays/logical_group_overlay.ts index 94a318e..07e053f 100644 --- a/src/overlays/logical_group_overlay.ts +++ b/src/overlays/logical_group_overlay.ts @@ -1,23 +1,17 @@ // Copyright 2019-2024 ETH Zurich and the DaCe authors. All rights reserved. -import { - DagreGraph, - JsonSDFG, - Point2D, - SDFVSettings, - SimpleRect, -} from '../index'; +import { Point2D } from '../index'; import { GraphElementInfo, SDFGElementGroup, SDFGRenderer, } from '../renderer/renderer'; import { - NestedSDFG, SDFGNode, SDFGElement, State, - SDFGElementType, + ControlFlowBlock, + Edge, } from '../renderer/renderer_elements'; import { GenericSdfgOverlay, OverlayType } from './generic_sdfg_overlay'; @@ -43,22 +37,23 @@ export class LogicalGroupOverlay extends GenericSdfgOverlay { this.renderer.draw_async(); } - public shadeNode( - node: SDFGNode, groups: LogicalGroup[], ctx: CanvasRenderingContext2D + private shadeElem( + elem: SDFGElement, ctx: CanvasRenderingContext2D, ...args: any[] ): void { + const groups: LogicalGroup[] = args[0]; const allGroups: LogicalGroup[] = []; - if (node instanceof State) { + if (elem instanceof State) { groups.forEach(group => { - if (group.states.includes(node.id)) { - node.shade(this.renderer, ctx, group.color, 0.3); + if (group.states.includes(elem.id)) { + elem.shade(this.renderer, ctx, group.color, 0.3); allGroups.push(group); } }); } else { groups.forEach(group => { group.nodes.forEach(n => { - if (n[0] === node.parent_id && n[1] === node.id) { - node.shade(this.renderer, ctx, group.color, 0.3); + if (n[0] === elem.parent_id && n[1] === elem.id) { + elem.shade(this.renderer, ctx, group.color, 0.3); allGroups.push(group); } }); @@ -67,7 +62,7 @@ export class LogicalGroupOverlay extends GenericSdfgOverlay { const mousepos = this.renderer.get_mousepos(); if (allGroups.length > 0 && mousepos && - node.intersect(mousepos.x, mousepos.y)) { + elem.intersect(mousepos.x, mousepos.y)) { // Show the corresponding group. this.renderer.set_tooltip(() => { const tt_cont = this.renderer.get_tooltip_container(); @@ -88,90 +83,31 @@ export class LogicalGroupOverlay extends GenericSdfgOverlay { } } - public recursivelyShadeCFG( - sdfg: JsonSDFG, graph: DagreGraph, ctx: CanvasRenderingContext2D, - ppp: number, visibleRect: SimpleRect + protected shadeBlock( + block: ControlFlowBlock, ctx: CanvasRenderingContext2D, ...args: any[] ): void { - // First go over visible states, skipping invisible ones. We only draw - // something if the state is collapsed or we're zoomed out far enough. - // In that case, we overlay the correct grouping color(s). - // If it's expanded or zoomed in close enough, we traverse inside. - const sdfgGroups = sdfg.attributes.logical_groups; - if (sdfgGroups === undefined || sdfgGroups.length === 0) - return; + this.shadeElem(block, ctx, args); + } - if (!graph) - return; + protected shadeNode( + node: SDFGNode, ctx: CanvasRenderingContext2D, ...args: any[] + ): void { + this.shadeElem(node, ctx, args); + } - graph?.nodes().forEach(v => { - const block = graph.node(v); - - // If the node's invisible, we skip it. - if (this.renderer.viewportOnly && !block.intersect( - visibleRect.x, visibleRect.y, - visibleRect.w, visibleRect.h - )) - return; - - const blockppp = Math.sqrt(block.width * block.height) / ppp; - if ((this.renderer.adaptiveHiding && - (blockppp < SDFVSettings.get('nestedLOD'))) || - block.attributes().is_collapsed - ) { - this.shadeNode(block, sdfgGroups, ctx); - } else { - if (block.type() === SDFGElementType.SDFGState) { - const stateGraph = block.data.graph; - if (stateGraph) { - stateGraph.nodes().forEach((v: string) => { - const node = stateGraph.node(v); - - // Skip the node if it's not visible. - if (this.renderer.viewportOnly && !node.intersect( - visibleRect.x, - visibleRect.y, visibleRect.w, visibleRect.h - )) - return; - - if (node.attributes().is_collapsed || - (this.renderer.adaptiveHiding && - ppp > SDFVSettings.get('nodeLOD'))) { - this.shadeNode(node, sdfgGroups, ctx); - } else { - if (node instanceof NestedSDFG && - node.attributes().sdfg && - node.attributes().sdfg.type !== 'SDFGShell' - ) { - this.recursivelyShadeCFG( - node.data.node.attributes.sdfg, - node.data.graph, ctx, ppp, visibleRect - ); - } else { - this.shadeNode(node, sdfgGroups, ctx); - } - } - }); - } - } else { - this.recursivelyShadeCFG( - sdfg, block.data.graph, ctx, ppp, visibleRect - ); - } - } - }); + protected shadeEdge( + edge: Edge, ctx: CanvasRenderingContext2D, ...args: any[] + ): void { + this.shadeElem(edge, ctx, args); } public draw(): void { const sdfg = this.renderer.get_sdfg(); - const graph = this.renderer.get_graph(); - const ppp = this.renderer.get_canvas_manager()?.points_per_pixel(); - const context = this.renderer.get_context(); - const visible_rect = this.renderer.get_visible_rect(); - if (graph && ppp !== undefined && context && visible_rect) { - this.recursivelyShadeCFG( - sdfg, graph, context, ppp, visible_rect - ); - } + const sdfgGroups = sdfg.attributes.logical_groups; + if (sdfgGroups === undefined || sdfgGroups.length === 0) + return; + + this.shadeSDFG(() => true, true, [sdfgGroups]); } public on_mouse_event( diff --git a/src/overlays/memory_location_overlay.ts b/src/overlays/memory_location_overlay.ts index 457a87a..fe56442 100644 --- a/src/overlays/memory_location_overlay.ts +++ b/src/overlays/memory_location_overlay.ts @@ -1,6 +1,6 @@ // Copyright 2019-2024 ETH Zurich and the DaCe authors. All rights reserved. -import { DagreGraph, Point2D, SDFVSettings, SimpleRect } from '../index'; +import { Point2D } from '../index'; import { GraphElementInfo, SDFGElementGroup, @@ -8,12 +8,8 @@ import { } from '../renderer/renderer'; import { AccessNode, - ControlFlowBlock, - ControlFlowRegion, - NestedSDFG, SDFGElement, SDFGNode, - State, } from '../renderer/renderer_elements'; import { KELLY_COLORS } from '../utils/utils'; import { GenericSdfgOverlay, OverlayType } from './generic_sdfg_overlay'; @@ -205,7 +201,7 @@ export class MemoryLocationOverlay extends GenericSdfgOverlay { }; } - public shadeNode(node: AccessNode, ctx: CanvasRenderingContext2D): void { + protected shadeNode(node: AccessNode, ctx: CanvasRenderingContext2D): void { const storageType = MemoryLocationOverlay.getStorageType(node); const mousepos = this.renderer.get_mousepos(); if (mousepos && node.intersect(mousepos.x, mousepos.y)) { @@ -228,73 +224,10 @@ export class MemoryLocationOverlay extends GenericSdfgOverlay { node.shade(this.renderer, ctx, '#' + color); } - public recursivelyShadeCFG( - graph: DagreGraph, - ctx: CanvasRenderingContext2D, - ppp: number, - visibleRect: SimpleRect - ): void { - // First go over visible control flow blocks, skipping invisible ones. - // We traverse inside to shade memory nodes wherever applicable. - graph.nodes().forEach(v => { - const block: ControlFlowBlock = graph.node(v); - - // If the node's invisible, we skip it. - if (this.renderer.viewportOnly && !block.intersect( - visibleRect.x, visibleRect.y, - visibleRect.w, visibleRect.h - )) - return; - - const stateppp = Math.sqrt(block.width * block.height) / ppp; - if ((this.renderer.adaptiveHiding && - (stateppp < SDFVSettings.get('nestedLOD'))) || - block.attributes()?.is_collapsed) { - // The state is collapsed or too small, so we don't need to - // traverse its insides. - return; - } else if (block instanceof State) { - const stateGraph = block.data.graph; - if (stateGraph) { - stateGraph.nodes().forEach((v: any) => { - const node = stateGraph.node(v); - - // Skip the node if it's not visible. - if (this.renderer.viewportOnly && - !node.intersect( - visibleRect.x, visibleRect.y, visibleRect.w, - visibleRect.h - )) - return; - - if (node instanceof NestedSDFG && - node.attributes().sdfg && - node.attributes().sdfg.type !== 'SDFGShell') { - this.recursivelyShadeCFG( - node.data.graph, ctx, ppp, visibleRect - ); - } else if (node instanceof AccessNode) { - if (!this.renderer.adaptiveHiding || - ppp < SDFVSettings.get('nodeLOD')) - this.shadeNode(node, ctx); - } - }); - } - } else if (block instanceof ControlFlowRegion) { - this.recursivelyShadeCFG( - block.data.graph, ctx, ppp, visibleRect - ); - } - }); - } - public draw(): void { - const graph = this.renderer.get_graph(); - const ppp = this.renderer.get_canvas_manager()?.points_per_pixel(); - const context = this.renderer.get_context(); - const visibleRect = this.renderer.get_visible_rect(); - if (graph && ppp !== undefined && context && visibleRect) - this.recursivelyShadeCFG(graph, context, ppp, visibleRect); + this.shadeSDFG((elem) => { + return elem instanceof AccessNode; + }); } public on_mouse_event( diff --git a/src/overlays/memory_volume_overlay.ts b/src/overlays/memory_volume_overlay.ts index b55b430..7bbeb17 100644 --- a/src/overlays/memory_volume_overlay.ts +++ b/src/overlays/memory_volume_overlay.ts @@ -1,24 +1,18 @@ // Copyright 2019-2024 ETH Zurich and the DaCe authors. All rights reserved. -import { - DagreGraph, - Point2D, - SDFVSettings, - SimpleRect, - SymbolMap, -} from '../index'; +import { DagreGraph, Point2D, SymbolMap } from '../index'; import { GraphElementInfo, SDFGElementGroup, SDFGRenderer, } from '../renderer/renderer'; import { + ConditionalBlock, ControlFlowBlock, ControlFlowRegion, Edge, NestedSDFG, SDFGElement, - SDFGNode, State, } from '../renderer/renderer_elements'; import { getTempColorHslString } from '../utils/utils'; @@ -126,9 +120,19 @@ export class MemoryVolumeOverlay extends GenericSdfgOverlay { }); } } else if (block instanceof ControlFlowRegion) { - this.calculateVolumeGraph( - block.data.graph, symbolMaps, volumes - ); + if (block.data.graph) { + this.calculateVolumeGraph( + block.data.graph, symbolMaps, volumes + ); + } + } else if (block instanceof ConditionalBlock) { + for (const [_, branch] of block.branches) { + if (branch.data.graph) { + this.calculateVolumeGraph( + branch.data.graph, symbolMaps, volumes + ); + } + } } }); } @@ -159,100 +163,16 @@ export class MemoryVolumeOverlay extends GenericSdfgOverlay { this.renderer.draw_async(); } - public shadeEdge(edge: Edge, ctx: CanvasRenderingContext2D): void { + protected shadeEdge(edge: Edge, ctx: CanvasRenderingContext2D): void { const volume = edge.data.volume; - if (volume !== undefined) { - // Only draw positive volumes. - if (volume <= 0) - return; - - // Calculate the severity color. - const color = getTempColorHslString( - this.getSeverityValue(volume) - ); - - edge.shade(this.renderer, ctx, color); - } - } - - public recursivelyShadeCFG( - graph: DagreGraph, ctx: CanvasRenderingContext2D, ppp: number, - visibleRect: SimpleRect - ): void { - graph.nodes().forEach(v => { - const block: ControlFlowBlock = graph.node(v); - - // If the node's invisible, we skip it. - if (this.renderer.viewportOnly && !block.intersect( - visibleRect.x, visibleRect.y, - visibleRect.w, visibleRect.h - ) || block.attributes()?.is_collapsed) - return; - - // If we're zoomed out enough that the contents aren't visible, we - // skip the state. - const stateppp = Math.sqrt(block.width * block.height) / ppp; - if (this.renderer.adaptiveHiding && - (stateppp < SDFVSettings.get('nestedLOD'))) - return; - - if (block instanceof State) { - const state_graph = block.data.graph; - if (state_graph) { - state_graph.nodes().forEach((v: string) => { - const node: SDFGNode = state_graph.node(v); - - // Skip the node if it's not visible. - if (this.renderer.viewportOnly && !node.intersect( - visibleRect.x, visibleRect.y, - visibleRect.w, visibleRect.h - )) - return; - - // If we're zoomed out enough that the node's contents - // aren't visible or the node is collapsed, we skip it. - if (node.data.node.attributes.is_collapsed || - (this.renderer.adaptiveHiding && - ppp > SDFVSettings.get('nodeLOD'))) - return; - - if (node instanceof NestedSDFG && - node.attributes().sdfg && - node.attributes().sdfg.type !== 'SDFGShell') { - this.recursivelyShadeCFG( - node.data.graph, ctx, ppp, visibleRect - ); - } - }); - - state_graph.edges().forEach((e: any) => { - const edge: Edge = state_graph.edge(e); - - // Skip if edge is invisible, or zoomed out far - if (this.renderer.adaptiveHiding && (!edge.intersect( - visibleRect.x, visibleRect.y, - visibleRect.w, visibleRect.h - ) || ppp > SDFVSettings.get('edgeLOD'))) - return; - - this.shadeEdge(edge, ctx); - }); - } - } else if (block instanceof ControlFlowRegion) { - this.recursivelyShadeCFG( - block.data.graph, ctx, ppp, visibleRect - ); - } - }); + const color = getTempColorHslString(this.getSeverityValue(volume)); + edge.shade(this.renderer, ctx, color); } public draw(): void { - const graph = this.renderer.get_graph(); - const ppp = this.renderer.get_canvas_manager()?.points_per_pixel(); - const context = this.renderer.get_context(); - const visible_rect = this.renderer.get_visible_rect(); - if (graph && ppp !== undefined && context && visible_rect) - this.recursivelyShadeCFG(graph, context, ppp, visible_rect); + this.shadeSDFG((elem) => { + return elem.data?.volume !== undefined && elem.data.volume > 0; + }); } public on_mouse_event( diff --git a/src/overlays/operational_intensity_overlay.ts b/src/overlays/operational_intensity_overlay.ts index 143a08e..3796d2f 100644 --- a/src/overlays/operational_intensity_overlay.ts +++ b/src/overlays/operational_intensity_overlay.ts @@ -3,8 +3,6 @@ import { DagreGraph, Point2D, - SDFVSettings, - SimpleRect, SymbolMap, getGraphElementUUID, } from '../index'; @@ -14,6 +12,8 @@ import { SDFGRenderer, } from '../renderer/renderer'; import { + ConditionalBlock, + ControlFlowBlock, Edge, NestedSDFG, SDFGElement, @@ -139,49 +139,63 @@ export class OperationalIntensityOverlay extends GenericSdfgOverlay { g: DagreGraph, symbol_map: SymbolMap, flops_values: number[] ): void { g.nodes().forEach(v => { - const state = g.node(v); - this.calculate_opint_node(state, symbol_map, flops_values); - const state_graph = state.data.graph; - if (state_graph) { - state_graph.nodes().forEach((v: string) => { - const node = state_graph.node(v); - if (node instanceof NestedSDFG) { - const nested_symbols_map: SymbolMap = {}; - const mapping = - node.data.node.attributes.symbol_mapping ?? {}; - // Translate the symbol mappings for the nested SDFG - // based on the mapping described on the node. - Object.keys(mapping).forEach((symbol: string) => { - nested_symbols_map[symbol] = - this.symbolResolver.parse_symbol_expression( - mapping[symbol], - symbol_map - ); - }); - // Merge in the parent mappings. - Object.keys(symbol_map).forEach((symbol) => { - if (!(symbol in nested_symbols_map)) - nested_symbols_map[symbol] = symbol_map[symbol]; - }); - - this.calculate_opint_node( - node, - nested_symbols_map, - flops_values - ); + const node = g.node(v); + this.calculate_opint_node(node, symbol_map, flops_values); + if (node instanceof ConditionalBlock) { + for (const [_, branch] of node.branches) { + this.calculate_opint_node(branch, symbol_map, flops_values); + if (branch.data.graph) { this.calculate_opint_graph( - node.data.graph, - nested_symbols_map, - flops_values - ); - } else { - this.calculate_opint_node( - node, - symbol_map, - flops_values + branch.data.graph, symbol_map, flops_values ); } - }); + } + } else { + const state_graph = node.data.graph; + if (state_graph) { + state_graph.nodes().forEach((v: string) => { + const node = state_graph.node(v); + if (node instanceof NestedSDFG) { + const nested_symbols_map: SymbolMap = {}; + const mapping = + node.data.node.attributes.symbol_mapping ?? {}; + // Translate the symbol mappings for the nested SDFG + // based on the mapping described on the node. + Object.keys(mapping).forEach((symbol: string) => { + nested_symbols_map[symbol] = + this.symbolResolver.parse_symbol_expression( + mapping[symbol], + symbol_map + ); + }); + // Merge in the parent mappings. + Object.keys(symbol_map).forEach((symbol) => { + if (!(symbol in nested_symbols_map)) { + nested_symbols_map[symbol] = symbol_map[ + symbol + ]; + } + }); + + this.calculate_opint_node( + node, + nested_symbols_map, + flops_values + ); + this.calculate_opint_graph( + node.data.graph, + nested_symbols_map, + flops_values + ); + } else { + this.calculate_opint_node( + node, + symbol_map, + flops_values + ); + } + }); + } } }); } @@ -217,12 +231,12 @@ export class OperationalIntensityOverlay extends GenericSdfgOverlay { this.renderer.draw_async(); } - public shade_node(node: SDFGNode, ctx: CanvasRenderingContext2D): void { - const opint = node.data.opint; + private shadeElem(elem: SDFGElement, ctx: CanvasRenderingContext2D): void { + const opint = elem.data.opint; const mousepos = this.renderer.get_mousepos(); if (opint !== undefined && mousepos && - node.intersect(mousepos.x, mousepos.y)) { + elem.intersect(mousepos.x, mousepos.y)) { // Show the computed OP-INT value if applicable. this.renderer.set_tooltip(() => { const tt_cont = this.renderer.get_tooltip_container(); @@ -241,77 +255,23 @@ export class OperationalIntensityOverlay extends GenericSdfgOverlay { // Calculate the severity color. const color = getTempColorHslString(this.getSeverityValue(opint)); - node.shade(this.renderer, ctx, color); + elem.shade(this.renderer, ctx, color); } - public recursively_shade_sdfg( - graph: DagreGraph, - ctx: CanvasRenderingContext2D, - ppp: number, - visible_rect: SimpleRect + protected shadeNode( + node: SDFGNode, ctx: CanvasRenderingContext2D, ...args: any[] ): void { - // First go over visible states, skipping invisible ones. We only draw - // something if the state is collapsed or we're zoomed out far enough. - // In that case, we draw the OP-INT calculated for the entire state. - // If it's expanded or zoomed in close enough, we traverse inside. - graph.nodes().forEach(v => { - const state = graph.node(v); - - // If the node's invisible, we skip it. - if (this.renderer.viewportOnly && !state.intersect( - visible_rect.x, visible_rect.y, - visible_rect.w, visible_rect.h - )) - return; - - const stateppp = Math.sqrt(state.width * state.height) / ppp; - if ((this.renderer.adaptiveHiding && - (stateppp < SDFVSettings.get('nestedLOD'))) || - state.data.state.attributes.is_collapsed) { - this.shade_node(state, ctx); - } else { - const state_graph = state.data.graph; - if (state_graph) { - state_graph.nodes().forEach((v: any) => { - const node = state_graph.node(v); + this.shadeElem(node, ctx); + } - // Skip the node if it's not visible. - if (this.renderer.viewportOnly && !node.intersect( - visible_rect.x, visible_rect.y, visible_rect.w, - visible_rect.h - )) - return; - - if (node instanceof NestedSDFG && - !node.data.node.attributes.is_collapsed) { - const nodeppp = Math.sqrt( - node.width * node.height - ) / ppp; - if (this.renderer.adaptiveHiding && - nodeppp < SDFVSettings.get('nodeLOD')) { - this.shade_node(node, ctx); - } else if (node.attributes().sdfg && - node.attributes().sdfg.type !== 'SDFGShell') { - this.recursively_shade_sdfg( - node.data.graph, ctx, ppp, visible_rect - ); - } - } else { - this.shade_node(node, ctx); - } - }); - } - } - }); + protected shadeBlock( + block: ControlFlowBlock, ctx: CanvasRenderingContext2D, ...args: any[] + ): void { + this.shadeElem(block, ctx); } public draw(): void { - const graph = this.renderer.get_graph(); - const ppp = this.renderer.get_canvas_manager()?.points_per_pixel(); - const context = this.renderer.get_context(); - const visible_rect = this.renderer.get_visible_rect(); - if (graph && ppp !== undefined && context && visible_rect) - this.recursively_shade_sdfg(graph, context, ppp, visible_rect); + this.shadeSDFG(); } public on_mouse_event( diff --git a/src/overlays/runtime_micro_seconds_overlay.ts b/src/overlays/runtime_micro_seconds_overlay.ts index 896d87a..5557016 100644 --- a/src/overlays/runtime_micro_seconds_overlay.ts +++ b/src/overlays/runtime_micro_seconds_overlay.ts @@ -1,14 +1,12 @@ // Copyright 2019-2024 ETH Zurich and the DaCe authors. All rights reserved. -import { Node } from 'dagre'; -import { - DagreGraph, - SDFVSettings, - SimpleRect, - getGraphElementUUID, -} from '../index'; +import { getGraphElementUUID } from '../index'; import { SDFGRenderer } from '../renderer/renderer'; -import { NestedSDFG, SDFGNode } from '../renderer/renderer_elements'; +import { + ControlFlowBlock, + SDFGElement, + SDFGNode, +} from '../renderer/renderer_elements'; import { getTempColorHslString } from '../utils/utils'; import { GenericSdfgOverlay, @@ -69,14 +67,14 @@ export class RuntimeMicroSecondsOverlay extends RuntimeReportOverlay { return value.toString() + ' ' + unit; } - public shade_node(node: SDFGNode, ctx: CanvasRenderingContext2D): void { - const rt_summary = this.runtime_map[getGraphElementUUID(node)]; + private shadeElem(elem: SDFGElement, ctx: CanvasRenderingContext2D): void { + const rt_summary = this.runtime_map[getGraphElementUUID(elem)]; if (rt_summary === undefined) return; const mousepos = this.renderer.get_mousepos(); - if (mousepos && node.intersect(mousepos.x, mousepos.y)) { + if (mousepos && elem.intersect(mousepos.x, mousepos.y)) { // Show the measured runtime. if (rt_summary['min'] === rt_summary['max']) { this.renderer.set_tooltip(() => { @@ -112,78 +110,23 @@ export class RuntimeMicroSecondsOverlay extends RuntimeReportOverlay { const micros = rt_summary[this.criterium]; const color = getTempColorHslString(this.getSeverityValue(micros)); - node.shade(this.renderer, ctx, color); + elem.shade(this.renderer, ctx, color); } - public recursively_shade_sdfg( - graph: DagreGraph, - ctx: CanvasRenderingContext2D, - ppp: number, - visible_rect: SimpleRect + protected shadeBlock( + block: ControlFlowBlock, ctx: CanvasRenderingContext2D, ...args: any[] ): void { - // First go over visible states, skipping invisible ones. We only draw - // something if the state is collapsed or we're zoomed out far enough. - // In that case, we draw the measured runtime for the entire state. - // If it's expanded or zoomed in close enough, we traverse inside. - graph.nodes().forEach(v => { - const state: Node = graph.node(v); - - // If the node's invisible, we skip it. - if (this.renderer.viewportOnly && !state.intersect( - visible_rect.x, visible_rect.y, - visible_rect.w, visible_rect.h - )) - return; - - const stateppp = Math.sqrt(state.width * state.height) / ppp; - if ((this.renderer.adaptiveHiding && - (stateppp < SDFVSettings.get('nestedLOD'))) || - state.data.state.attributes.is_collapsed) { - this.shade_node(state, ctx); - } else { - const state_graph = state.data.graph; - if (state_graph) { - state_graph.nodes().forEach((v: string) => { - const node = state_graph.node(v); - - // Skip the node if it's not visible. - if (this.renderer.viewportOnly && !node.intersect( - visible_rect.x, visible_rect.y, visible_rect.w, - visible_rect.h - )) - return; - - if (node instanceof NestedSDFG && - !node.data.node.attributes.is_collapsed) { - const nodeppp = Math.sqrt( - node.width * node.height - ) / ppp; - if (this.renderer.adaptiveHiding && - nodeppp < - SDFVSettings.get('nestedLOD')) { - this.shade_node(node, ctx); - } else if (node.attributes().sdfg && - node.attributes().sdfg.type !== 'SDFGShell') { - this.recursively_shade_sdfg( - node.data.graph, ctx, ppp, visible_rect - ); - } - } else { - this.shade_node(node, ctx); - } - }); - } - } - }); + this.shadeElem(block, ctx); + } + + protected shadeNode( + node: SDFGNode, ctx: CanvasRenderingContext2D, ...args: any[] + ): void { + this.shadeElem(node, ctx); } public draw(): void { - const graph = this.renderer.get_graph(); - const ppp = this.renderer.get_canvas_manager()?.points_per_pixel(); - const context = this.renderer.get_context(); - const visible_rect = this.renderer.get_visible_rect(); - if (graph && ppp !== undefined && context && visible_rect) - this.recursively_shade_sdfg(graph, context, ppp, visible_rect); + this.shadeSDFG(); } public set_runtime_map(runtime_map: { [uuids: string]: any }): void { diff --git a/src/overlays/simulated_operational_intensity_overlay.ts b/src/overlays/simulated_operational_intensity_overlay.ts index 4039702..dc8a854 100644 --- a/src/overlays/simulated_operational_intensity_overlay.ts +++ b/src/overlays/simulated_operational_intensity_overlay.ts @@ -3,8 +3,6 @@ import { DagreGraph, Point2D, - SDFVSettings, - SimpleRect, SymbolMap, getGraphElementUUID, } from '../index'; @@ -14,6 +12,8 @@ import { SDFGRenderer, } from '../renderer/renderer'; import { + ConditionalBlock, + ControlFlowBlock, Edge, NestedSDFG, SDFGElement, @@ -76,49 +76,60 @@ export class SimulatedOperationalIntensityOverlay extends GenericSdfgOverlay { g: DagreGraph, symbol_map: SymbolMap, op_in_values: number[] ): void { g.nodes().forEach(v => { - const state = g.node(v); - this.calculate_op_in_node(state, symbol_map, op_in_values); - const state_graph = state.data.graph; - if (state_graph) { - state_graph.nodes().forEach((v: string) => { - const node = state_graph.node(v); - if (node instanceof NestedSDFG) { - const nested_symbols_map: SymbolMap = {}; - const mapping = - node.data.node.attributes.symbol_mapping; - // Translate the symbol mappings for the nested SDFG - // based on the mapping described on the node. - Object.keys(mapping).forEach((symbol: string) => { - nested_symbols_map[symbol] = - this.symbolResolver.parse_symbol_expression( - mapping[symbol], - symbol_map - ); - }); - // Merge in the parent mappings. - Object.keys(symbol_map).forEach((symbol) => { - if (!(symbol in nested_symbols_map)) - nested_symbols_map[symbol] = symbol_map[symbol]; - }); - - this.calculate_op_in_node( - node, - nested_symbols_map, - op_in_values - ); + const node = g.node(v); + this.calculate_op_in_node(node, symbol_map, op_in_values); + if (node instanceof ConditionalBlock) { + for (const [_, branch] of node.branches) { + this.calculate_op_in_node(branch, symbol_map, op_in_values); + if (branch.data.graph) { this.calculate_op_in_graph( - node.data.graph, - nested_symbols_map, - op_in_values - ); - } else { - this.calculate_op_in_node( - node, - symbol_map, - op_in_values + branch.data.graph, symbol_map, op_in_values ); } - }); + } + } else { + const state_graph = node.data.graph; + if (state_graph) { + state_graph.nodes().forEach((v: string) => { + const node = state_graph.node(v); + if (node instanceof NestedSDFG) { + const nested_symbols_map: SymbolMap = {}; + const mapping = + node.data.node.attributes.symbol_mapping; + // Translate the symbol mappings for the nested SDFG + // based on the mapping described on the node. + Object.keys(mapping).forEach((symbol: string) => { + nested_symbols_map[symbol] = + this.symbolResolver.parse_symbol_expression( + mapping[symbol], + symbol_map + ); + }); + // Merge in the parent mappings. + Object.keys(symbol_map).forEach((symbol) => { + if (!(symbol in nested_symbols_map)) + nested_symbols_map[symbol] = symbol_map[symbol]; + }); + + this.calculate_op_in_node( + node, + nested_symbols_map, + op_in_values + ); + this.calculate_op_in_graph( + node.data.graph, + nested_symbols_map, + op_in_values + ); + } else { + this.calculate_op_in_node( + node, + symbol_map, + op_in_values + ); + } + }); + } } }); } @@ -154,13 +165,13 @@ export class SimulatedOperationalIntensityOverlay extends GenericSdfgOverlay { this.renderer.draw_async(); } - public shade_node(node: SDFGNode, ctx: CanvasRenderingContext2D): void { - const op_in = node.data.op_in; - const op_in_string = node.data.op_in_string; + private shadeElem(elem: SDFGElement, ctx: CanvasRenderingContext2D): void { + const op_in = elem.data.op_in; + const op_in_string = elem.data.op_in_string; const mousepos = this.renderer.get_mousepos(); if (op_in_string !== undefined && mousepos && - node.intersect(mousepos.x, mousepos.y)) { + elem.intersect(mousepos.x, mousepos.y)) { // Show the computed op_in value if applicable. if (isNaN(op_in_string) && op_in !== undefined) { this.renderer.set_tooltip(() => { @@ -188,7 +199,7 @@ export class SimulatedOperationalIntensityOverlay extends GenericSdfgOverlay { // node's op_in, that means that there's an unresolved symbol. Shade // the node grey to indicate that. if (op_in_string !== undefined) { - node.shade(this.renderer, ctx, 'gray'); + elem.shade(this.renderer, ctx, 'gray'); return; } else { return; @@ -202,78 +213,23 @@ export class SimulatedOperationalIntensityOverlay extends GenericSdfgOverlay { // Calculate the severity color. const color = getTempColorHslString(1 - this.getSeverityValue(op_in)); - node.shade(this.renderer, ctx, color); + elem.shade(this.renderer, ctx, color); } - public recursively_shade_sdfg( - graph: DagreGraph, - ctx: CanvasRenderingContext2D, - ppp: number, - visible_rect: SimpleRect + protected shadeBlock( + block: ControlFlowBlock, ctx: CanvasRenderingContext2D, ...args: any[] ): void { - // First go over visible states, skipping invisible ones. We only draw - // something if the state is collapsed or we're zoomed out far enough. - // In that case, we draw the op_in calculated for the entire state. - // If it's expanded or zoomed in close enough, we traverse inside. - graph.nodes().forEach(v => { - const state = graph.node(v); - - // If the node's invisible, we skip it. - if (this.renderer.viewportOnly && !state.intersect( - visible_rect.x, visible_rect.y, - visible_rect.w, visible_rect.h - )) - return; - - const stateppp = Math.sqrt(state.width * state.height) / ppp; - if ((this.renderer.adaptiveHiding && - (stateppp < SDFVSettings.get('nestedLOD'))) || - state.data.state.attributes.is_collapsed) { - this.shade_node(state, ctx); - } else { - const state_graph = state.data.graph; - if (state_graph) { - state_graph.nodes().forEach((v: any) => { - const node = state_graph.node(v); - - // Skip the node if it's not visible. - if (this.renderer.viewportOnly && !node.intersect( - visible_rect.x, visible_rect.y, visible_rect.w, - visible_rect.h - )) - return; + this.shadeElem(block, ctx); + } - if (node instanceof NestedSDFG && - !node.data.node.attributes.is_collapsed) { - const nodeppp = Math.sqrt( - node.width * node.height - ) / ppp; - if (this.renderer.adaptiveHiding && - nodeppp < - SDFVSettings.get('nestedLOD')) { - this.shade_node(node, ctx); - } else if (node.attributes().sdfg && - node.attributes().sdfg.type !== 'SDFGShell') { - this.recursively_shade_sdfg( - node.data.graph, ctx, ppp, visible_rect - ); - } - } else { - this.shade_node(node, ctx); - } - }); - } - } - }); + protected shadeNode( + node: SDFGNode, ctx: CanvasRenderingContext2D, ...args: any[] + ): void { + this.shadeElem(node, ctx); } public draw(): void { - const graph = this.renderer.get_graph(); - const ppp = this.renderer.get_canvas_manager()?.points_per_pixel(); - const context = this.renderer.get_context(); - const visible_rect = this.renderer.get_visible_rect(); - if (graph && ppp !== undefined && context && visible_rect) - this.recursively_shade_sdfg(graph, context, ppp, visible_rect); + this.shadeSDFG(); } public on_mouse_event( diff --git a/src/overlays/static_flops_overlay.ts b/src/overlays/static_flops_overlay.ts index b2a1264..903d1eb 100644 --- a/src/overlays/static_flops_overlay.ts +++ b/src/overlays/static_flops_overlay.ts @@ -3,8 +3,6 @@ import { DagreGraph, Point2D, - SDFVSettings, - SimpleRect, SymbolMap, getGraphElementUUID, } from '../index'; @@ -14,6 +12,8 @@ import { SDFGRenderer, } from '../renderer/renderer'; import { + ConditionalBlock, + ControlFlowBlock, Edge, NestedSDFG, SDFGElement, @@ -73,49 +73,63 @@ export class StaticFlopsOverlay extends GenericSdfgOverlay { g: DagreGraph, symbol_map: SymbolMap, flops_values: number[] ): void { g.nodes().forEach(v => { - const state = g.node(v); - this.calculate_flops_node(state, symbol_map, flops_values); - const state_graph = state.data.graph; - if (state_graph) { - state_graph.nodes().forEach((v: string) => { - const node = state_graph.node(v); - if (node instanceof NestedSDFG) { - const nested_symbols_map: SymbolMap = {}; - const mapping = - node.data.node.attributes.symbol_mapping ?? {}; - // Translate the symbol mappings for the nested SDFG - // based on the mapping described on the node. - Object.keys(mapping).forEach((symbol: string) => { - nested_symbols_map[symbol] = - this.symbolResolver.parse_symbol_expression( - mapping[symbol], - symbol_map - ); - }); - // Merge in the parent mappings. - Object.keys(symbol_map).forEach((symbol) => { - if (!(symbol in nested_symbols_map)) - nested_symbols_map[symbol] = symbol_map[symbol]; - }); - - this.calculate_flops_node( - node, - nested_symbols_map, - flops_values - ); + const node = g.node(v); + this.calculate_flops_node(node, symbol_map, flops_values); + if (node instanceof ConditionalBlock) { + for (const [_, branch] of node.branches) { + this.calculate_flops_node(branch, symbol_map, flops_values); + if (branch.data.graph) { this.calculate_flops_graph( - node.data.graph, - nested_symbols_map, - flops_values - ); - } else { - this.calculate_flops_node( - node, - symbol_map, - flops_values + branch.data.graph, symbol_map, flops_values ); } - }); + } + } else { + const state_graph = node.data.graph; + if (state_graph) { + state_graph.nodes().forEach((v: string) => { + const node = state_graph.node(v); + if (node instanceof NestedSDFG) { + const nested_symbols_map: SymbolMap = {}; + const mapping = + node.data.node.attributes.symbol_mapping ?? {}; + // Translate the symbol mappings for the nested SDFG + // based on the mapping described on the node. + Object.keys(mapping).forEach((symbol: string) => { + nested_symbols_map[symbol] = + this.symbolResolver.parse_symbol_expression( + mapping[symbol], + symbol_map + ); + }); + // Merge in the parent mappings. + Object.keys(symbol_map).forEach((symbol) => { + if (!(symbol in nested_symbols_map)) { + nested_symbols_map[symbol] = symbol_map[ + symbol + ]; + } + }); + + this.calculate_flops_node( + node, + nested_symbols_map, + flops_values + ); + this.calculate_flops_graph( + node.data.graph, + nested_symbols_map, + flops_values + ); + } else { + this.calculate_flops_node( + node, + symbol_map, + flops_values + ); + } + }); + } } }); } @@ -151,13 +165,15 @@ export class StaticFlopsOverlay extends GenericSdfgOverlay { this.renderer.draw_async(); } - public shade_node(node: SDFGNode, ctx: CanvasRenderingContext2D): void { - const flops = node.data.flops; - const flops_string = node.data.flops_string; + private shadeElem( + elem: SDFGElement, ctx: CanvasRenderingContext2D + ): void { + const flops = elem.data.flops; + const flops_string = elem.data.flops_string; const mousepos = this.renderer.get_mousepos(); if (flops_string !== undefined && mousepos && - node.intersect(mousepos.x, mousepos.y)) { + elem.intersect(mousepos.x, mousepos.y)) { // Show the computed FLOPS value if applicable. if (isNaN(flops_string) && flops !== undefined) { this.renderer.set_tooltip(() => { @@ -182,7 +198,7 @@ export class StaticFlopsOverlay extends GenericSdfgOverlay { // node's FLOPS, that means that there's an unresolved symbol. Shade // the node grey to indicate that. if (flops_string !== undefined) { - node.shade(this.renderer, ctx, 'gray'); + elem.shade(this.renderer, ctx, 'gray'); return; } else { return; @@ -196,78 +212,25 @@ export class StaticFlopsOverlay extends GenericSdfgOverlay { // Calculate the severity color. const color = getTempColorHslString(this.getSeverityValue(flops)); - node.shade(this.renderer, ctx, color); + elem.shade(this.renderer, ctx, color); } - public recursively_shade_sdfg( - graph: DagreGraph, - ctx: CanvasRenderingContext2D, - ppp: number, - visible_rect: SimpleRect + protected shadeNode( + node: SDFGNode, ctx: CanvasRenderingContext2D, ...args: any[] ): void { - // First go over visible states, skipping invisible ones. We only draw - // something if the state is collapsed or we're zoomed out far enough. - // In that case, we draw the FLOPS calculated for the entire state. - // If it's expanded or zoomed in close enough, we traverse inside. - graph.nodes().forEach(v => { - const state = graph.node(v); - - // If the node's invisible, we skip it. - if (this.renderer.viewportOnly && !state.intersect( - visible_rect.x, visible_rect.y, - visible_rect.w, visible_rect.h - )) - return; - - const stateppp = Math.sqrt(state.width * state.height) / ppp; - if ((this.renderer.adaptiveHiding && - (stateppp < SDFVSettings.get('nestedLOD'))) || - state.data.state.attributes.is_collapsed) { - this.shade_node(state, ctx); - } else { - const state_graph = state.data.graph; - if (state_graph) { - state_graph.nodes().forEach((v: any) => { - const node = state_graph.node(v); - - // Skip the node if it's not visible. - if (this.renderer.viewportOnly && !node.intersect( - visible_rect.x, visible_rect.y, visible_rect.w, - visible_rect.h - )) - return; + this.shadeElem(node, ctx); + } - if (node instanceof NestedSDFG && - !node.data.node.attributes.is_collapsed) { - const nodeppp = Math.sqrt( - node.width * node.height - ) / ppp; - if (this.renderer.adaptiveHiding && - nodeppp < - SDFVSettings.get('nestedLOD')) { - this.shade_node(node, ctx); - } else if (node.attributes().sdfg && - node.attributes().sdfg.type !== 'SDFGShell') { - this.recursively_shade_sdfg( - node.data.graph, ctx, ppp, visible_rect - ); - } - } else { - this.shade_node(node, ctx); - } - }); - } - } - }); + protected shadeBlock( + block: ControlFlowBlock, ctx: CanvasRenderingContext2D, ...args: any[] + ): void { + this.shadeElem(block, ctx); } public draw(): void { - const graph = this.renderer.get_graph(); - const ppp = this.renderer.get_canvas_manager()?.points_per_pixel(); - const context = this.renderer.get_context(); - const visible_rect = this.renderer.get_visible_rect(); - if (graph && ppp !== undefined && context && visible_rect) - this.recursively_shade_sdfg(graph, context, ppp, visible_rect); + this.shadeSDFG((elem) => { + return elem instanceof SDFGNode || elem instanceof ControlFlowBlock; + }); } public on_mouse_event( diff --git a/src/renderer/renderer.ts b/src/renderer/renderer.ts index e331331..da916a0 100644 --- a/src/renderer/renderer.ts +++ b/src/renderer/renderer.ts @@ -47,7 +47,7 @@ import { import { showErrorModal } from '../utils/utils'; import { CanvasManager } from './canvas_manager'; import { - AccessNode, Connector, + AccessNode, ConditionalBlock, Connector, ControlFlowBlock, ControlFlowRegion, Edge, EntryNode, InterstateEdge, Memlet, NestedSDFG, @@ -70,9 +70,10 @@ declare const canvas2pdf: any; // Some global functions and variables which are only accessible within VSCode: declare const vscode: any | null; -export type SDFGElementGroup = ('states' | 'nodes' | 'edges' | 'isedges' | - 'connectors' | 'controlFlowRegions' | - 'controlFlowBlocks'); +export type SDFGElementGroup = ( + 'states' | 'nodes' | 'edges' | 'isedges' | 'connectors' | + 'controlFlowRegions' | 'controlFlowBlocks' +); export interface SDFGElementInfo { sdfg: JsonSDFG, id: number, @@ -2698,6 +2699,34 @@ export class SDFGRenderer extends EventEmitter { const ng = block.data.graph; if (ng) doRecursive(ng, block.data.block, sdfg); + } else if (block instanceof ConditionalBlock) { + func( + 'controlFlowBlocks', + { + sdfg: sdfg, + graph: g, + id: blockId, + cfgId: cfg.cfg_list_id, + stateId: -1, + }, + block + ); + for (const [_, branch] of block.branches) { + func( + 'controlFlowRegions', + { + sdfg: sdfg, + graph: g, + id: blockId, + cfgId: cfg.cfg_list_id, + stateId: -1, + }, + branch + ); + const ng = branch.data.graph; + if (ng) + doRecursive(ng, branch.data.block, sdfg); + } } else { // Other (unknown) control flow blocks. func( diff --git a/src/sdfg_diff_viewer.ts b/src/sdfg_diff_viewer.ts index c97dbfe..8d40c2b 100644 --- a/src/sdfg_diff_viewer.ts +++ b/src/sdfg_diff_viewer.ts @@ -3,6 +3,7 @@ import $ from 'jquery'; import { + ConditionalBlock, ControlFlowRegion, DagreGraph, graphFindRecursive, @@ -87,8 +88,14 @@ export abstract class SDFGDiffViewer implements ISDFV { dict.set(node.guid(), node); if (node instanceof ControlFlowRegion || node instanceof State || - node instanceof NestedSDFG) + node instanceof NestedSDFG) { recursiveAddIds(node.data.graph, dict); + } else if (node instanceof ConditionalBlock) { + for (const [_, branch] of node.branches) { + if (branch.data.graph) + recursiveAddIds(branch.data.graph, dict); + } + } } for (const eid of graph.edges()) { const edge = graph.edge(eid); @@ -318,7 +325,9 @@ export class WebSDFGDiffViewer extends SDFGDiffViewer { ); } - public static init(graphA: JsonSDFG, graphB: JsonSDFG, precomputedDiff?: DiffMap): WebSDFGDiffViewer { + public static init( + graphA: JsonSDFG, graphB: JsonSDFG, precomputedDiff?: DiffMap + ): WebSDFGDiffViewer { const leftContainer = document.getElementById('diff-contents-A'); const rightContainer = document.getElementById('diff-contents-B'); if (!leftContainer || !rightContainer) diff --git a/src/sdfv.ts b/src/sdfv.ts index 5262cbc..7186490 100644 --- a/src/sdfv.ts +++ b/src/sdfv.ts @@ -23,6 +23,7 @@ import { import { OverlayManager } from './overlay_manager'; import { SDFGRenderer } from './renderer/renderer'; import { + ConditionalBlock, SDFG, SDFGElement, SDFGNode, @@ -570,8 +571,14 @@ export function graphFindRecursive( if (predicate(graph, node)) results.push(node); // Enter states or nested SDFGs recursively - if (node.data.graph) + if (node.data.graph) { graphFindRecursive(node.data.graph, predicate, results); + } else if (node instanceof ConditionalBlock) { + for (const [_, branch] of node.branches) { + if (branch.data.graph) + graphFindRecursive(branch.data.graph, predicate, results); + } + } } for (const edgeid of graph.edges()) { const edge = graph.edge(edgeid); diff --git a/src/utils/sdfg/memlet_trees.ts b/src/utils/sdfg/memlet_trees.ts index 3594dde..b0c09f3 100644 --- a/src/utils/sdfg/memlet_trees.ts +++ b/src/utils/sdfg/memlet_trees.ts @@ -3,6 +3,7 @@ import { JsonSDFG, JsonSDFGBlock, + JsonSDFGConditionalBlock, JsonSDFGControlFlowRegion, JsonSDFGEdge, JsonSDFGNode, @@ -90,10 +91,14 @@ export function memletTreeNested( ); } }); - } else { + } else if (block.type.endsWith('Region')) { checkNested( block as JsonSDFGControlFlowRegion, nsdfg, name, direction ); + } else if (block.type === SDFGElementType.ConditionalBlock) { + const condBlock = block as JsonSDFGConditionalBlock; + for (const [_, branch] of condBlock.branches) + checkNested(branch, nsdfg, name, direction); } }); } @@ -212,10 +217,14 @@ export function memletTreeRecursive( trees = trees.concat(t); } }); - } else { + } else if (block.type.endsWith('Region')) { trees = trees.concat(memletTreeRecursive( block as JsonSDFGControlFlowRegion, sdfg )); + } else if (block.type === SDFGElementType.ConditionalBlock) { + const condBlock = block as JsonSDFGConditionalBlock; + for (const [_, branch] of condBlock.branches) + trees = trees.concat(memletTreeRecursive(branch, sdfg)); } }); diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 4ebd7ee..3fbe3d3 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -51,16 +51,16 @@ export function deepCopy(obj: T): T { * * If a parent is provided, the element is automatically added as a child. * - * @param {*} type Element tag (div, span, etc.) - * @param {*} id Optional element id - * @param {*} classList Optional array of class names - * @param {*} parent Optional parent element + * @param type Element tag (div, span, etc.) + * @param id Optional element id + * @param classList Optional array of class names + * @param parent Optional parent element * * @returns The created DOM element */ export function createElement( type: K, - id = '', + id: string = '', classList: string[] = [], parent: Node | undefined = undefined ): HTMLElementTagNameMap[K] { @@ -180,8 +180,8 @@ function tempColor(badness: number): [number, number, number] { /** * Get the color on a green-red temperature scale based on a fractional value. - * @param {Number} val Value between 0 and 1, 0 = green, .5 = yellow, 1 = red - * @returns HSL color string + * @param val Value between 0 and 1, 0 = green, .5 = yellow, 1 = red + * @returns HSL color string */ export function getTempColorHslString(badness: number): string { const col = tempColor(badness); @@ -191,8 +191,8 @@ export function getTempColorHslString(badness: number): string { /** * Get the color on a green-red temperature scale based on a fractional value. - * @param {Number} val Value between 0 and 1, 0 = green, .5 = yellow, 1 = red - * @returns Hex color number + * @param val Value between 0 and 1, 0 = green, .5 = yellow, 1 = red + * @returns Hex color number */ export function getTempColorHEX(badness: number): number { return rgb2hex(hsl2rgb(...tempColor(badness)));