diff --git a/diode/webclient/context_menu.js b/diode/webclient/context_menu.js
index 8c558cec89..29693bdf22 100644
--- a/diode/webclient/context_menu.js
+++ b/diode/webclient/context_menu.js
@@ -42,6 +42,16 @@ class ContextMenu {
});
}
+ addCheckableOption(name, checked, onselect, onhover=null) {
+ this._options.push({
+ name: name,
+ checkbox: true,
+ checked: checked,
+ func: onselect,
+ onhover: onhover
+ });
+ }
+
destroy() {
if (!this._cmenu_elem)
return;
@@ -79,10 +89,21 @@ class ContextMenu {
elem.addEventListener('click', x.func);
elem.classList.add("context_menu_option");
- elem.innerText = x.name;
+ if (x.checkbox) {
+ let markelem = document.createElement('span');
+ markelem.classList = x.checked ? 'checkmark_checked' : 'checkmark';
+ elem.appendChild(markelem);
+ elem.innerHTML += x.name;
+ elem.addEventListener('click', elem => {
+ x.checked = !x.checked;
+ x.func(elem, x.checked);
+ });
+ } else {
+ elem.innerText = x.name;
+ elem.addEventListener('click', x.func);
+ }
cmenu_div.appendChild(elem);
}
-
}
else {
cmenu_div.innerHTML = this._html_content;
diff --git a/diode/webclient/renderer.js b/diode/webclient/renderer.js
index 86ee9823e6..64c8137516 100644
--- a/diode/webclient/renderer.js
+++ b/diode/webclient/renderer.js
@@ -670,6 +670,9 @@ class SDFGRenderer {
this.tooltip = null;
this.tooltip_container = null;
+ // View options
+ this.inclusive_ranges = false;
+
// Mouse-related fields
this.mousepos = null; // Last position of the mouse pointer (in canvas coordinates)
this.realmousepos = null; // Last position of the mouse pointer (in pixel coordinates)
@@ -693,6 +696,10 @@ class SDFGRenderer {
}
}
+ view_settings() {
+ return {inclusive_ranges: this.inclusive_ranges};
+ }
+
// Initializes the DOM
init_elements() {
@@ -721,8 +728,9 @@ class SDFGRenderer {
cmenu.addOption("Save view as PNG", x => that.save_as_png());
cmenu.addOption("Save view as PDF", x => that.save_as_pdf());
cmenu.addOption("Save all as PDF", x => that.save_as_pdf(true));
+ cmenu.addCheckableOption("Inclusive ranges", that.inclusive_ranges, (x, checked) => {that.inclusive_ranges = checked;});
that.menu = cmenu;
- cmenu.show(rect.left, rect.bottom);
+ that.menu.show(rect.left, rect.bottom);
};
d.title = 'Menu';
this.toolbar.appendChild(d);
diff --git a/diode/webclient/renderer_elements.js b/diode/webclient/renderer_elements.js
index 747bc20ba2..14fa24daf9 100644
--- a/diode/webclient/renderer_elements.js
+++ b/diode/webclient/renderer_elements.js
@@ -199,7 +199,7 @@ class Edge extends SDFGElement {
let style = this.strokeStyle();
if (style !== 'black')
- renderer.tooltip = (c) => this.tooltip(c);
+ renderer.tooltip = (c) => this.tooltip(c, renderer);
if (this.parent_id == null && style === 'black') { // Interstate edge
style = 'blue';
}
@@ -224,7 +224,8 @@ class Edge extends SDFGElement {
ctx.strokeStyle = "black";
}
- tooltip(container) {
+ tooltip(container, renderer) {
+ let dsettings = renderer.view_settings();
let attr = this.attributes();
// Memlet
if (attr.subset !== undefined) {
@@ -233,15 +234,15 @@ class Edge extends SDFGElement {
return;
}
let contents = attr.data;
- contents += sdfg_property_to_string(attr.subset);
+ contents += sdfg_property_to_string(attr.subset, dsettings);
if (attr.other_subset)
- contents += ' -> ' + sdfg_property_to_string(attr.other_subset);
+ contents += ' -> ' + sdfg_property_to_string(attr.other_subset, dsettings);
if (attr.wcr)
- contents += '
CR: ' + sdfg_property_to_string(attr.wcr) +'';
+ contents += '
CR: ' + sdfg_property_to_string(attr.wcr, dsettings) +'';
- let num_accesses = sdfg_property_to_string(attr.num_accesses);
+ let num_accesses = sdfg_property_to_string(attr.num_accesses, dsettings);
if (num_accesses == -1)
num_accesses = "Dynamic";
@@ -324,13 +325,13 @@ class AccessNode extends Node {
let nodedesc = this.sdfg.attributes._arrays[this.data.node.attributes.data];
// Streams have dashed edges
- if (nodedesc.type === "Stream") {
+ if (nodedesc && nodedesc.type === "Stream") {
ctx.setLineDash([5, 3]);
} else {
ctx.setLineDash([1, 0]);
}
- if (nodedesc.attributes.transient === false) {
+ if (nodedesc && nodedesc.attributes.transient === false) {
ctx.lineWidth = 3.0;
} else {
ctx.lineWidth = 1.0;
@@ -378,23 +379,54 @@ class ScopeNode extends Node {
ctx.fill();
ctx.fillStyle = "black";
- let far_label = this.attributes().label;
+ let far_label = this.far_label();
+ drawAdaptiveText(ctx, renderer, far_label,
+ this.close_label(renderer), this.x, this.y,
+ this.width, this.height,
+ SCOPE_LOD);
+ }
+
+ far_label() {
+ let result = this.attributes().label;
if (this.scopeend()) { // Get label from scope entry
let entry = this.sdfg.nodes[this.parent_id].nodes[this.data.node.scope_entry];
if (entry !== undefined)
- far_label = entry.attributes.label;
+ result = entry.attributes.label;
else {
- far_label = this.label();
- let ind = far_label.indexOf('[');
+ result = this.data.node.label;
+ let ind = result.indexOf('[');
if (ind > 0)
- far_label = far_label.substring(0, ind);
+ result = result.substring(0, ind);
}
}
+ return result;
+ }
- drawAdaptiveText(ctx, renderer, far_label,
- this.label(), this.x, this.y,
- this.width, this.height,
- SCOPE_LOD);
+ close_label(renderer) {
+ if (!renderer.inclusive_ranges)
+ return this.label();
+
+ let result = this.far_label();
+ let attrs = this.attributes();
+ if (this.scopeend()) {
+ let entry = this.sdfg.nodes[this.parent_id].nodes[this.data.node.scope_entry];
+ if (entry !== undefined)
+ attrs = entry.attributes;
+ else
+ return this.label();
+ }
+ result += ' [';
+
+ if (this instanceof ConsumeEntry || this instanceof ConsumeExit) {
+ result += attrs.pe_index + '=' + '0..' + (attrs.num_pes - 1).toString();
+ } else {
+ for (let i = 0; i < attrs.params.length; ++i) {
+ result += attrs.params[i] + '=';
+ result += sdfg_range_elem_to_string(attrs.range.ranges[i], renderer.view_settings()) + ', ';
+ }
+ result = result.substring(0, result.length-2); // Remove trailing comma
+ }
+ return result + ']';
}
}
diff --git a/diode/webclient/sdfg_utils.js b/diode/webclient/sdfg_utils.js
index 154659a498..7c2023ff8a 100644
--- a/diode/webclient/sdfg_utils.js
+++ b/diode/webclient/sdfg_utils.js
@@ -61,14 +61,43 @@ function stringify_sdfg(sdfg) {
return JSON.stringify(sdfg, (name, val) => replacer(name, val, sdfg));
}
+function sdfg_range_elem_to_string(range, settings=null) {
+ let preview = '';
+ if (range.start == range.end && range.step == 1 && range.tile == 1)
+ preview += sdfg_property_to_string(range.start, settings);
+ else {
+ if (settings && settings.inclusive_ranges) {
+ preview += sdfg_property_to_string(range.start, settings) + '..' +
+ sdfg_property_to_string(range.end, settings);
+ } else {
+ let endp1 = sdfg_property_to_string(range.end, settings) + ' + 1';
+ // Try to simplify using math.js
+ try {
+ endp1 = math.simplify(endp1).toString();
+ } catch(e) {}
+
+ preview += sdfg_property_to_string(range.start, settings) + ':' +
+ endp1;
+ }
+ if (range.step != 1) {
+ preview += ':' + sdfg_property_to_string(range.step, settings);
+ if (range.tile != 1)
+ preview += ':' + sdfg_property_to_string(range.tile, settings);
+ } else if (range.tile != 1) {
+ preview += '::' + sdfg_property_to_string(range.tile, settings);
+ }
+ }
+ return preview;
+}
+
// Includes various properties and returns their string representation
-function sdfg_property_to_string(prop) {
+function sdfg_property_to_string(prop, settings=null) {
if (prop === null) return prop;
if (prop.type === "Indices" || prop.type === "subsets.Indices") {
let indices = prop.indices;
let preview = '[';
for (let index of indices) {
- preview += sdfg_property_to_string(index) + ', ';
+ preview += sdfg_property_to_string(index, settings) + ', ';
}
return preview.slice(0, -2) + ']';
} else if (prop.type === "Range" || prop.type === "subsets.Range") {
@@ -77,26 +106,7 @@ function sdfg_property_to_string(prop) {
// Generate string from range
let preview = '[';
for (let range of ranges) {
- if (range.start == range.end && range.step == 1 && range.tile == 1)
- preview += sdfg_property_to_string(range.start);
- else {
- let endp1 = sdfg_property_to_string(range.end) + ' + 1';
- // Try to simplify using math.js
- try {
- endp1 = math.simplify(endp1).toString();
- } catch(e) {}
-
- preview += sdfg_property_to_string(range.start) + ':' +
- endp1;
- if (range.step != 1) {
- preview += ':' + sdfg_property_to_string(range.step);
- if (range.tile != 1)
- preview += ':' + sdfg_property_to_string(range.tile);
- } else if (range.tile != 1) {
- preview += '::' + sdfg_property_to_string(range.tile);
- }
- }
- preview += ', ';
+ preview += sdfg_range_elem_to_string(range, settings) + ', ';
}
return preview.slice(0, -2) + ']';
} else if (prop.language !== undefined && prop.string_data !== undefined) {
@@ -115,7 +125,7 @@ function sdfg_property_to_string(prop) {
for (let subprop of prop) {
if (!first)
result += ', ';
- result += sdfg_property_to_string(subprop);
+ result += sdfg_property_to_string(subprop, settings);
first = false;
}
return result + ' ]';
diff --git a/diode/webclient/sdfv.css b/diode/webclient/sdfv.css
index 301d725803..5f4721d0eb 100644
--- a/diode/webclient/sdfv.css
+++ b/diode/webclient/sdfv.css
@@ -37,6 +37,10 @@ h1,h2,h3,h4,h5,h6{font-family:"Segoe UI",Arial,sans-serif;font-weight:400;margin
position: absolute;
background-color: #fdfdfd;
box-shadow: 0 4px 5px 3px rgba(0, 0, 0, 0.2);
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
}
.context_menu_option {
@@ -50,6 +54,14 @@ h1,h2,h3,h4,h5,h6{font-family:"Segoe UI",Arial,sans-serif;font-weight:400;margin
background: rgba(0, 0, 0, 0.1);
}
+.context_menu_option input {
+ position: absolute;
+ opacity: 0;
+ cursor: pointer;
+ height: 0;
+ width: 0;
+}
+
.tooltip {
background: rgba(0, 0, 0, 0.8);
color: white;
@@ -57,7 +69,53 @@ h1,h2,h3,h4,h5,h6{font-family:"Segoe UI",Arial,sans-serif;font-weight:400;margin
font-family: "Segoe UI", Arial, sans-serif;
padding: 2px 5px 2px 5px;
border-radius: 4px;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
user-select: none;
position: absolute;
display: none;
}
+
+/* Checkmarks for context menu items */
+.checkmark {
+ position: absolute;
+ right: 5px;
+ height: 20px;
+ width: 20px;
+ background-color: #eee;
+}
+
+.checkmark_checked {
+ position: absolute;
+ right: 5px;
+ height: 20px;
+ width: 20px;
+ background-color: #2196F3;
+}
+
+.context_menu_option:hover .checkmark {
+ background-color: #ccc;
+}
+
+.checkmark:after {
+ content: "";
+ position: absolute;
+ display: none;
+}
+
+.context_menu_option .checkmark_checked:after {
+ content: "";
+ position: absolute;
+ display: block;
+ left: 6px;
+ top: 2px;
+ width: 5px;
+ height: 10px;
+ border: solid white;
+ border-width: 0 3px 3px 0;
+ -webkit-transform: rotate(45deg);
+ -ms-transform: rotate(45deg);
+ transform: rotate(45deg);
+}
+
diff --git a/diode/webclient/sdfv.js b/diode/webclient/sdfv.js
index 9aabcc7975..25137570f4 100644
--- a/diode/webclient/sdfv.js
+++ b/diode/webclient/sdfv.js
@@ -160,7 +160,7 @@ function mouse_event(evtype, event, mousepos, elements, renderer, elem) {
for (let attr of Object.entries(elem.attributes())) {
if (attr[0] === "layout" || attr[0] === "sdfg" || attr[0].startsWith("_meta_")) continue;
html += "" + attr[0] + ": ";
- html += sdfg_property_to_string(attr[1], attr[0]) + "