Last active
December 22, 2015 15:43
-
-
Save sinkap/7f89de3e558856b81f10 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2015-2015 ARM Limited | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
var EventPlot = (function () { | |
/* EventPlot receives data that is hashed by the keys | |
* and each element in the data is sorted by start time. | |
* Since events on each lane are mutually exclusive, they | |
* they are also sorted by the end time. We use this information | |
* and binary search on the input data for filtering events | |
* This maintains filtering complexity to O[KLogN] | |
*/ | |
var GUIDER_WIDTH = 2; | |
infoProps = { | |
START_GUIDER_COLOR: "green", | |
END_GUIDER_COLOR: "red", | |
DELTA_COLOR: "blue", | |
GUIDER_WIDTH: 2, | |
TOP_MARGIN: 20, | |
HEIGHT: 30, | |
START_PREFIX: "A = ", | |
END_PREFIX: "B = ", | |
DELTA_PREFIX: "A - B = ", | |
XPAD: 10, | |
YPAD: 5, | |
BOX_BUFFER: 2, | |
BOX_WIDTH_RATIO: 0.6 | |
} | |
var search_data = function (data, key, value, left, right) { | |
var mid; | |
while (left < right) { | |
mid = Math.floor((left + right) / 2) | |
if (data[mid][key] > value) | |
right = mid; | |
else | |
left = mid + 1; | |
} | |
return left; | |
} | |
/* Return the information for the current process | |
* pointed by the mouse | |
*/ | |
var getCurrentInfo = function(ePlot, x0, y0) { | |
for (name in ePlot.items) { | |
var data = ePlot.items[name]; | |
var xMax = ePlot.zoomScale.domain()[1]; | |
var right = search_data(data, 0, xMax, 0, data.length - 1); | |
var left = search_data(data, 1, x0, 0, right); | |
if (data) { | |
var candidate = data[left]; | |
if (candidate[0] <= x0 && | |
candidate[1] >= x0 && | |
candidate[2] == y0) | |
return { | |
name: name, | |
info: candidate | |
}; | |
} | |
} | |
} | |
var generate = function (div_name, base) { | |
var margin, brush, x, ext, yMain, chart, main, | |
mainAxis, | |
itemRects, items, colourAxis, tip, lanes; | |
var json_file = base; | |
$.getJSON(json_file, function (d) { | |
items = d.data; | |
lanes = d.lanes; | |
var names = d.keys; | |
var showSummary = d.showSummary; | |
var div = $("#" + div_name); | |
margin = { | |
top: 15, | |
right: 15, | |
bottom: 15, | |
left: 70 | |
}, width = div.width() - margin.left - margin.right, | |
mainHeight = 50 * lanes.length - margin.top - margin.bottom; | |
x = d3.scale.linear() | |
.domain(d.xDomain) | |
.range([0, width]); | |
var zoomScale = d3.scale.linear() | |
.domain(d.xDomain) | |
.range([0, width]); | |
var xMin = x.domain()[0]; | |
var xMax = x.domain()[1]; | |
//Colour Ordinal scale. Uses Category20 Colors | |
colours = d3.scale.category20(); | |
colourAxis = d3.scale.ordinal() | |
.range(colours.range()) | |
.domain(names); | |
brushScale = d3.scale.linear() | |
.range([0, width]); | |
ext = d3.extent(lanes, function (d) { | |
return d.id; | |
}); | |
yMain = d3.scale.linear() | |
.domain([ext[0], ext[1] + | |
1 | |
]) | |
.range([0, mainHeight]); | |
var ePlot; | |
$("#" + div_name) | |
.append('<div class="pull-right">' + | |
'<button type="button" class="btn btn-sm btn-info" ' + | |
'onclick="EventPlot.create_help_dialog()">Help</button></div>') | |
var iDesc = drawInfo(div_name, margin, width); | |
chart = d3.select('#' + div_name) | |
.append('svg:svg') | |
.attr('width', width + margin.right + | |
margin.left) | |
.attr('height', mainHeight + margin.top + | |
margin.bottom + 5) | |
.attr('class', 'chart') | |
main = chart.append('g') | |
.attr('transform', 'translate(' + margin.left + | |
',' + margin.top + ')') | |
.attr('width', width) | |
.attr('height', mainHeight) | |
.attr('class', 'main') | |
main.append('g') | |
.selectAll('.laneLines') | |
.data(lanes) | |
.enter() | |
.append('line') | |
.attr('x1', 0) | |
.attr('y1', function (d) { | |
return d3.round(yMain(d.id)) + 0.5; | |
}) | |
.attr('x2', width) | |
.attr('y2', function (d) { | |
return d3.round(yMain(d.id)) + 0.5; | |
}) | |
.attr('stroke', function (d) { | |
return d.label === '' ? 'white' : | |
'lightgray' | |
}); | |
main.append('g') | |
.selectAll('.laneText') | |
.data(lanes) | |
.enter() | |
.append('text') | |
.attr('x', 0) | |
.text(function (d) { | |
return d.label; | |
}) | |
.attr('y', function (d) { | |
return yMain(d.id + .5); | |
}) | |
.attr('dy', '0.5ex') | |
.attr('text-anchor', 'end') | |
.attr('class', 'laneText'); | |
mainAxis = d3.svg.axis() | |
.scale(brushScale) | |
.orient('bottom'); | |
tip = d3.tip() | |
.attr('class', 'd3-tip') | |
.html(function (d) { | |
return "<span style='color:white'>" + | |
d.name + "</span>"; | |
}) | |
main.append('g') | |
.attr('transform', 'translate(0,' + | |
mainHeight + ')') | |
.attr('class', 'main axis') | |
.call(mainAxis); | |
var ePlot; | |
ePlot = { | |
div: div, | |
div_name: div_name, | |
margin: margin, | |
chart: chart, | |
mainHeight: mainHeight, | |
width: width, | |
x: x, | |
brushScale: brushScale, | |
ext: ext, | |
yMain: yMain, | |
main: main, | |
mainAxis: mainAxis, | |
items: items, | |
colourAxis: colourAxis, | |
tip: tip, | |
lanes: lanes, | |
names: names, | |
iDesc: iDesc, | |
}; | |
ePlot.zoomScale = zoomScale; | |
if (showSummary) | |
drawMini(ePlot); | |
var outgoing; | |
var zoomed = function () { | |
if (zoomScale.domain()[0] < xMin) { | |
zoom.translate([zoom.translate()[ | |
0] - zoomScale( | |
xMin) + | |
zoomScale.range()[0], | |
zoom.translate()[ | |
1] | |
]); | |
} else if (zoomScale.domain()[1] > | |
xMax) { | |
zoom.translate([zoom.translate()[ | |
0] - zoomScale( | |
xMax) + | |
zoomScale.range()[1], | |
zoom.translate()[ | |
1] | |
]); | |
} | |
outgoing = main.selectAll(".mItem") | |
.attr("visibility", "hidden"); | |
drawMain(ePlot, zoomScale.domain()[0], | |
zoomScale.domain()[1]); | |
if (showSummary) { | |
brush.extent(zoomScale.domain()); | |
ePlot.mini.select(".brush") | |
.call( | |
brush); | |
} | |
brushScale.domain(zoomScale.domain()); | |
ePlot.main.select('.main.axis') | |
.call(ePlot.mainAxis) | |
updateInfo(ePlot); | |
}; | |
var rightClickCtrlAltHandler = function(x0, y0) { | |
x0 = ePlot.zoomScale.invert(x0); | |
y0 = Math.floor(ePlot.yMain.invert(y0)); | |
var current = getCurrentInfo(ePlot, x0, y0); | |
if (current) { | |
ePlot.iDesc.currentProc.text(current.name) | |
ePlot.iDesc.currentInfo.text( | |
current.info[0].toFixed(6) | |
+ " to " + | |
current.info[1].toFixed(6) + | |
" (" + (current.info[1] - current.info[0]) | |
.toFixed(6) + ")") | |
removeContextRect(ePlot); | |
ePlot.contextRect = drawContextRect(ePlot, current.info[0], current.info[1], current.info[2], true) | |
ePlot.iDesc.currentDisp.attr("stroke", ePlot.colourAxis(current.name)); | |
} | |
} | |
var contextMenuHandler = function() { | |
var e = d3.event; | |
var x0 = d3.mouse(this)[0] - ePlot.margin.left; | |
var y0 = d3.mouse(this)[1] - ePlot.margin.top; | |
if (e.ctrlKey && e.altKey) | |
rightClickCtrlAltHandler(x0, y0); | |
else if (e.ctrlKey) { | |
if (ePlot.endGuider) | |
ePlot.endGuider = ePlot.endGuider.remove(); | |
ePlot.endGuider = drawVerticalLine(ePlot, x0, | |
infoProps.END_GUIDER_COLOR, "B"); | |
ePlot.endGuider._x_pos = ePlot.zoomScale.invert(x0); | |
iDesc.endText.text(infoProps.END_PREFIX + ePlot.endGuider._x_pos.toFixed(6)) | |
} else { | |
if (ePlot.startGuider) | |
ePlot.startGuider = ePlot.startGuider.remove(); | |
ePlot.startGuider = drawVerticalLine(ePlot, x0, | |
infoProps.START_GUIDER_COLOR, "A"); | |
ePlot.startGuider._x_pos = ePlot.zoomScale.invert(x0); | |
iDesc.startText.text(infoProps.START_PREFIX + ePlot.startGuider._x_pos.toFixed(6)) | |
} | |
if (ePlot.endGuider && ePlot.startGuider) | |
iDesc.deltaText.text(infoProps.DELTA_PREFIX + | |
(ePlot.endGuider._x_pos - ePlot.startGuider._x_pos) | |
.toFixed(6) | |
) | |
d3.event.preventDefault(); | |
} | |
chart.on("contextmenu", contextMenuHandler); | |
if (showSummary) { | |
var _brushed_event = function () { | |
main.selectAll("path") | |
.remove(); | |
var brush_xmin = brush.extent()[0]; | |
var brush_xmax = brush.extent()[1]; | |
var t = zoom.translate(), | |
new_domain = brush.extent(), | |
scale; | |
/* | |
* scale = x.range()[1] - x.range[0] | |
* -------------------------- | |
* x(x.domain()[1] - x.domain()[0]) | |
* | |
* _ _ | |
* new_domain[0] = x.invert | x.range()[0] - z.translate()[0] | | |
* | ------------------- | | |
* |_ z.scale() _| | |
* | |
* | |
* | |
* translate[0] = x.range()[0] - x(new_domain[0])) * zoom.scale() | |
*/ | |
scale = (width) / x(x.domain()[0] + | |
new_domain[1] - | |
new_domain[0]); | |
zoom.scale(scale); | |
t[0] = x.range()[0] - (x(new_domain[ | |
0]) * scale); | |
zoom.translate(t); | |
brushScale.domain(brush.extent()) | |
drawMain(ePlot, brush_xmin, | |
brush_xmax); | |
ePlot.main.select('.main.axis') | |
.call(ePlot.mainAxis) | |
updateInfo(ePlot); | |
}; | |
brush = d3.svg.brush() | |
.x(x) | |
.extent(x.domain()) | |
.on("brush", _brushed_event); | |
ePlot.mini.append('g') | |
.attr('class', 'brush') | |
.call(brush) | |
.selectAll('rect') | |
.attr('y', 1) | |
.attr('height', ePlot.miniHeight - 1); | |
} | |
var zoom = d3.behavior.zoom() | |
.x(zoomScale) | |
.on( | |
"zoom", zoomed) | |
.on("zoomend", function () { | |
if (outgoing) | |
outgoing.remove() | |
}) | |
.scaleExtent([1, 4096]); | |
chart.call(zoom); | |
drawMain(ePlot, xMin, xMax); | |
ePlot.main.select('.main.axis') | |
.call(ePlot.mainAxis) | |
var resize = function() { | |
var width = div.width() - margin.left | |
- margin.right; | |
/* Update scale ranges */ | |
x.range([0, width]); | |
zoomScale.range([0, width]); | |
brushScale.range([0, width]); | |
ePlot.width = width; | |
resize_main(ePlot); | |
resize_info(ePlot); | |
resize_mini(ePlot); | |
zoomed(); | |
} | |
d3.select(window) | |
.on("resize." + ePlot.div_name, resize) | |
return ePlot; | |
}); | |
}; | |
var resize_mini = function(ePlot) { | |
d3.select(ePlot.mini.node().parentNode) | |
.attr("width", ePlot.div.width()); | |
ePlot.iDesc.info_svg | |
.attr("width", ePlot.div.width()); | |
ePlot.mini.selectAll("line") | |
.attr("x2", ePlot.width); | |
ePlot.mini.call(ePlot.miniAxis); | |
ePlot.mini.selectAll(".miniItem").remove(); | |
drawMiniPaths(ePlot); | |
} | |
var resize_main = function(ePlot) { | |
d3.select(ePlot.main.node().parentNode) | |
.attr("width", ePlot.div.width()); | |
ePlot.main.selectAll("line") | |
.attr("x2", ePlot.width); | |
} | |
var resize_info = function(ePlot) { | |
var width_box_one = infoProps.BOX_WIDTH_RATIO * ePlot.width; | |
var width_box_two = ePlot.width - width_box_one; | |
ePlot.iDesc.info | |
.attr("width", width); | |
ePlot.iDesc.guiderInfo | |
.attr("width", width_box_one - infoProps.BOX_BUFFER); | |
ePlot.iDesc.currentDisp | |
.attr("x", width_box_one + infoProps.BOX_BUFFER); | |
ePlot.iDesc.currentDisp | |
.attr("width", width_box_two - infoProps.BOX_BUFFER); | |
ePlot.iDesc.deltaText | |
.attr("x", (width_box_one / 2) - infoProps.XPAD) | |
ePlot.iDesc.endText | |
.attr("x", width_box_one - infoProps.XPAD) | |
ePlot.iDesc.currentProc | |
.attr("x", width_box_one + infoProps.XPAD + infoProps.BOX_BUFFER) | |
ePlot.iDesc.currentInfo | |
.attr("x", ePlot.width - infoProps.XPAD) | |
} | |
var drawInfo = function (div_name, margin, width) { | |
var infoHeight = 30; | |
var _top = 20; | |
var LINE_WIDTH = 2 | |
var iDesc = {}; | |
var width_box_one = infoProps.BOX_WIDTH_RATIO * width; | |
var width_box_two = width - width_box_one | |
iDesc.info_svg = d3.select("#" + div_name) | |
.append( | |
"svg:svg") | |
.attr('width', width + margin.right + | |
margin.left) | |
.attr('height', infoHeight + infoProps.TOP_MARGIN + LINE_WIDTH) | |
.attr('class', 'info') | |
iDesc.info = iDesc.info_svg.append("g") | |
.attr("transform", "translate(" + margin.left + | |
"," + infoProps.TOP_MARGIN + ")") | |
.attr('width', width) | |
.attr("class", "main") | |
.attr('height', infoProps.HEIGHT) | |
iDesc.guiderInfo = iDesc.info.append("rect") | |
.attr("x", 0) | |
.attr("y", 0) | |
.attr("width", width_box_one - infoProps.BOX_BUFFER) | |
.attr("height", infoHeight) | |
.attr("stroke", "lightgray") | |
.attr("fill", "none") | |
.attr("stroke-width", 1); | |
iDesc.currentDisp = iDesc.info.append("rect") | |
.attr("x", width_box_one + infoProps.BOX_BUFFER) | |
.attr("y", 0) | |
.attr("width", width_box_two - infoProps.BOX_BUFFER) | |
.attr("height", infoHeight) | |
.attr("stroke", "lightgray") | |
.attr("fill", "none") | |
.attr("stroke-width", 1); | |
iDesc.startText = iDesc.info.append("text") | |
.text("") | |
.attr("x", infoProps.XPAD) | |
.attr("y", infoProps.HEIGHT / 2 + infoProps.YPAD) | |
.attr("fill", infoProps.START_GUIDER_COLOR); | |
iDesc.deltaText = iDesc.info.append("text") | |
.text("") | |
.attr("x", (width_box_one / 2) - infoProps.XPAD) | |
.attr("y", infoProps.HEIGHT / 2 + infoProps.YPAD) | |
.attr("fill", infoProps.DELTA_COLOR); | |
iDesc.endText = iDesc.info.append("text") | |
.text("") | |
.attr("x", width_box_one - infoProps.XPAD) | |
.attr("text-anchor", "end") | |
.attr("y", infoProps.HEIGHT / 2 + infoProps.YPAD) | |
.attr("fill", infoProps.END_GUIDER_COLOR); | |
iDesc.currentProc = iDesc.info.append("text") | |
.text("") | |
.attr("x", width_box_one + infoProps.XPAD + infoProps.BOX_BUFFER) | |
.attr("text-anchor", "start") | |
.attr("y", infoProps.HEIGHT / 2 + infoProps.YPAD) | |
iDesc.currentInfo = iDesc.info.append("text") | |
.text("") | |
.attr("x", width - infoProps.XPAD) | |
.attr("text-anchor", "end") | |
.attr("y", infoProps.HEIGHT / 2 + infoProps.YPAD) | |
return iDesc; | |
} | |
var drawVerticalLine = function (ePlot, x, color, text) { | |
var line = ePlot.main.append("g") | |
line.append("line") | |
.style("stroke", color) | |
.style("stroke-width", GUIDER_WIDTH) | |
.attr("x1", x) | |
.attr("x2", x) | |
.attr("y1", 0) | |
.attr("y2", ePlot.mainHeight) | |
line.append("text") | |
.text(text) | |
.attr("y", -1) | |
.attr("x", x) | |
.attr("text-anchor", "middle") | |
.attr("fill", color) | |
return line; | |
}; | |
var removeContextRect = function(ePlot) { | |
if (ePlot.contextRect && ePlot.contextRect.rect) | |
ePlot.contextRect.rect.remove(); | |
} | |
var drawContextRect = function (ePlot, x0, x1, y, animate) { | |
var xMin = ePlot.zoomScale.domain()[0]; | |
var xMax = ePlot.zoomScale.domain()[1]; | |
var bounds = [Math.max(x0, xMin), Math.min(x1, | |
xMax)] | |
if (bounds[0] >= bounds[1]) | |
return { | |
rect: false, | |
x0: x0, | |
x1: x1, | |
y: y, | |
} | |
var rect = ePlot.main.selectAll(".contextRect").data([""]) | |
if (animate) | |
rect.enter().append("rect") | |
.attr("x", ePlot.zoomScale(bounds[0])) | |
.attr("y", ePlot.yMain(y)) | |
.attr("height", ePlot.yMain(1)) | |
.attr("class", "contextRect") | |
.attr("width", 0) | |
.transition() | |
.attr("width", ePlot.zoomScale(bounds[1]) - ePlot.zoomScale(bounds[0])) | |
else | |
rect.enter().append("rect") | |
.attr("x", ePlot.zoomScale(bounds[0])) | |
.attr("y", ePlot.yMain(y)) | |
.attr("class", "contextRect") | |
.attr("height", ePlot.yMain(1)) | |
.attr("width", ePlot.zoomScale(bounds[1]) - ePlot.zoomScale(bounds[0])) | |
return { | |
rect: rect, | |
x0: x0, | |
x1: x1, | |
y: y, | |
} | |
} | |
var checkGuiderRange = function (ePlot, xpos) { | |
if (xpos >= ePlot.zoomScale.domain()[0] && | |
xpos <= ePlot.zoomScale.domain()[1]) | |
return true; | |
else | |
return false; | |
} | |
var updateInfo = function (ePlot) { | |
if (ePlot.endGuider) { | |
var xpos = ePlot.endGuider._x_pos; | |
ePlot.endGuider.remove(); | |
if (checkGuiderRange(ePlot, xpos)) { | |
ePlot.endGuider = drawVerticalLine(ePlot, ePlot.zoomScale(xpos), | |
infoProps.END_GUIDER_COLOR, "B"); | |
ePlot.endGuider._x_pos = xpos; | |
} | |
} | |
if (ePlot.startGuider) { | |
var xpos = ePlot.startGuider._x_pos; | |
ePlot.startGuider.remove(); | |
if (checkGuiderRange(ePlot, xpos)) { | |
ePlot.startGuider = drawVerticalLine(ePlot, ePlot.zoomScale(xpos), | |
infoProps.START_GUIDER_COLOR, "A"); | |
ePlot.startGuider._x_pos = xpos | |
} | |
} | |
if (ePlot.contextRect) { | |
removeContextRect(ePlot); | |
ePlot.contextRect = drawContextRect(ePlot, ePlot.contextRect.x0, | |
ePlot.contextRect.x1, | |
ePlot.contextRect.y, | |
false); | |
} | |
} | |
var drawMiniPaths = function(ePlot) { | |
ePlot.mini.append('g') | |
.selectAll('miniItems') | |
.data(getPaths(ePlot, ePlot.x, ePlot.yMini)) | |
.enter() | |
.append('path') | |
.attr('class', function (d) { | |
return 'miniItem' | |
}) | |
.attr('d', function (d) { | |
return d.path; | |
}) | |
.attr("stroke", function (d) { | |
return d.color | |
}) | |
.attr("class", "miniItem"); | |
} | |
var drawMini = function (ePlot) { | |
var miniHeight = ePlot.lanes.length * 12 + 50; | |
var miniAxis = d3.svg.axis() | |
.scale(ePlot.x) | |
.orient('bottom'); | |
var yMini = d3.scale.linear() | |
.domain([ePlot.ext[0], ePlot.ext[1] + | |
1 | |
]) | |
.range([0, miniHeight]); | |
ePlot.yMini = yMini; | |
ePlot.miniAxis = miniAxis; | |
ePlot.miniHeight = miniHeight; | |
var summary = d3.select("#" + ePlot.div_name) | |
.append( | |
"svg:svg") | |
.attr('width', ePlot.width + ePlot.margin.right + | |
ePlot.margin.left) | |
.attr('height', miniHeight + ePlot.margin.bottom + | |
ePlot.margin.top) | |
.attr('class', 'chart') | |
var mini = summary.append('g') | |
.attr("transform", "translate(" + ePlot.margin.left + | |
"," + ePlot.margin.top + ")") | |
.attr('width', ePlot.width) | |
.attr('height', ePlot.miniHeight) | |
.attr('class', 'mini'); | |
mini.append('g') | |
.selectAll('.laneLines') | |
.data(ePlot.lanes) | |
.enter() | |
.append('line') | |
.attr('x1', 0) | |
.attr('y1', function (d) { | |
return d3.round(ePlot.yMini(d.id)) + 0.5; | |
}) | |
.attr('x2', ePlot.width) | |
.attr('y2', function (d) { | |
return d3.round(ePlot.yMini(d.id)) + 0.5; | |
}) | |
.attr('stroke', function (d) { | |
return d.label === '' ? 'white' : | |
'lightgray' | |
}); | |
mini.append('g') | |
.attr('transform', 'translate(0,' + | |
ePlot.miniHeight + ')') | |
.attr('class', 'axis') | |
.call(ePlot.miniAxis); | |
ePlot.mini = mini | |
drawMiniPaths(ePlot); | |
mini.append('g') | |
.selectAll('.laneText') | |
.data(ePlot.lanes) | |
.enter() | |
.append('text') | |
.text(function (d) { | |
return d.label; | |
}) | |
.attr('x', -10) | |
.attr('y', function (d) { | |
return ePlot.yMini(d.id + .5); | |
}) | |
.attr('dy', '0.5ex') | |
.attr('text-anchor', 'end') | |
.attr('class', 'laneText'); | |
return mini; | |
}; | |
var drawMain = function (ePlot, xMin, xMax) { | |
var rects, labels; | |
var dMin = 10000; | |
var paths = getPaths(ePlot, ePlot.zoomScale, ePlot.yMain); | |
ePlot.brushScale.domain([xMin, xMax]); | |
if (paths.length == 0) | |
return; | |
ePlot.main | |
.selectAll('mainItems') | |
.data(paths) | |
.enter() | |
.append('path') | |
.attr("shape-rendering", "crispEdges") | |
.attr('d', function (d) { | |
return d.path; | |
}) | |
.attr("class", "mItem") | |
.attr("stroke-width", function(d) { | |
return 0.8 * ePlot.yMain(1); | |
}) | |
.attr("stroke", function (d) { | |
return d.color | |
}) | |
.call(ePlot.tip) | |
.on("mouseover", ePlot.tip.show) | |
.on('mouseout', ePlot.tip.hide) | |
.on('mousemove', function () { | |
var xDisp = parseFloat(ePlot.tip.style("width")) / | |
2.0 | |
ePlot.tip.style("left", (d3.event.pageX - xDisp) + | |
"px") | |
.style("top", Math.max(0, d3.event.pageY - | |
47) + "px"); | |
}) | |
}; | |
function _handle_equality(d, xMin, xMax, x, y) { | |
var offset = 0.5 * y(1) + 0.5 | |
var bounds = [Math.max(d[0], xMin), Math.min(d[1], | |
xMax)] | |
if (bounds[0] < bounds[1]) | |
return 'M' + ' ' + x(bounds[0]) + ' ' + (y(d[2]) + offset) + ' H ' + x(bounds[1]); | |
else | |
return ''; | |
}; | |
function _process(path, d, xMin, xMax, x, y, offset) { | |
var start = d[0]; | |
if (start < xMin) | |
start = xMin; | |
var end = d[1]; | |
if (end > xMax) | |
end = xMax; | |
start = x(start); | |
end = x(end); | |
if ((end - start) < 0.01) | |
return path; | |
else if ((end - start) < 1) | |
end = start + 1; | |
path += 'M' + ' ' + start + ' ' + (y(d[2]) + offset) + ' H ' + end; | |
return path; | |
} | |
var _get_path = function(data, xMin, xMax, offset, x, y, stride) { | |
var path = '' | |
var max_rects = 2000; | |
var right = search_data(data, 0, xMax, 0, data.length - | |
1) | |
var left = search_data(data, 1, xMin, 0, right) | |
//Handle Equality | |
if (left == right) | |
return _handle_equality(data[left], xMin, xMax, x, y); | |
data = data.slice(left, right + 1); | |
var stride_length = 1; | |
if (stride) | |
stride_length = Math.max(Math.ceil(data.length / max_rects), 1); | |
for (var i = 0; i < data.length; i+= stride_length) | |
path = _process(path, data[i], xMin, xMax, x, y, offset); | |
return path; | |
} | |
var getPaths = function (ePlot, x, y, stride) { | |
var keys = ePlot.names; | |
var items = ePlot.items; | |
var colourAxis = ePlot.colourAxis; | |
var xMin = x.domain()[0]; | |
var xMax = x.domain()[1]; | |
var paths = {}, | |
d, offset = 0.5 * y(1) + 0.5, | |
result = []; | |
for (var i in keys) { | |
var name = keys[i]; | |
var path = _get_path(items[name], xMin, xMax, offset, x, y, stride) | |
/* This is critical. Adding paths for non | |
* existent processes in the window* can be | |
* very expensive as there is one SVG per process | |
* and SVG rendering is expensive | |
*/ | |
if (!path || path == "") | |
continue | |
result.push({ | |
color: colourAxis(name), | |
path: path, | |
name: name | |
}); | |
} | |
return result; | |
} | |
var create_dialog_body = function (body, title) { | |
var element = $("<div/>") | |
.addClass("modal fade") | |
.attr("role", "dialog") | |
.attr("tabindex", -1) | |
element.append( | |
$("<div/>") | |
.addClass("modal-dialog") | |
.attr("role", "document") | |
.append( | |
$("<div/>") | |
.addClass("modal-content") | |
.append( | |
$("<div/>") | |
.addClass("modal-header") | |
.append( | |
$("<button>") | |
.addClass("close") | |
.attr("data-dismiss", | |
"modal") | |
.append($("<span/>") | |
.html("×")) | |
) | |
.append($("<h4/>") | |
.addClass("modal-title") | |
.text(title) | |
) | |
.append($("<div/>") | |
.addClass("modal-body") | |
.append(body) | |
) | |
.append( | |
$("<div/>") | |
.addClass("modal-footer") | |
.append( | |
$("<button>") | |
.addClass("btn btn-default") | |
.attr("data-dismiss", "modal") | |
.text("Close") | |
) | |
) | |
) | |
) | |
) | |
return element.modal(); | |
} | |
var create_help_dialog = function () { | |
var HELP_IMAGE = "https://rawgit.com/ARM-software/trappy/master/trappy/plotter/css/EventPlot_help.jpg" | |
var element = $('<div/>'); | |
// The documentation | |
var doc = $('<div/>') | |
.addClass('alert alert-info'); | |
doc.append( | |
'EventPlot is a multi-lane timeline plot ' + | |
'which supports interative zooming and timing calculation' | |
); | |
element.append(doc); | |
var zoom = $("<div/>") | |
.addClass("media-left"); | |
var addLabel = function (txt, cls) { | |
return '<span class="label label-' + cls + '" + ">' + | |
txt + '</span>' | |
} | |
var addListItem = function (txt) { | |
return '<li class="list-group-item">' + txt + | |
'</li>' | |
} | |
var addPlus = function () { | |
return " + " | |
} | |
var addBadge = function (txt) { | |
return '<span class="label label-default" style="border-radius: 10px">' + | |
txt + '</span>' | |
} | |
element.append( | |
'<img style="width: 100%;" class="media-object" src="' + | |
HELP_IMAGE + '"/>' | |
) | |
element.append('<ul class="list-group">') | |
element.append(addListItem('Scroll in the main area ' + | |
addBadge("1") + " to zoom interactively")) | |
element.append(addListItem( | |
'Click and drag in the main area ' + addBadge( | |
"1") + " to pan the zoom")) | |
element.append(addListItem( | |
'The summary of the plot is shown in ' + | |
addBadge("2"))) | |
element.append(addListItem('Adjust the size of window ' + | |
addBadge("4") + | |
" set the X-Limits of the chart")) | |
element.append(addListItem(addLabel("Right-Click", | |
"default") + | |
" to place marker " + addLabel("A", "success"))) | |
element.append(addListItem(addLabel("Ctrl", "primary") + | |
" + " + addLabel("Right-Click", "default") + | |
" to place marker " + addLabel("B", "danger"))) | |
element.append(addListItem( | |
"The marker positions and delta will be shown in " + | |
addBadge("3"))) | |
element.append( | |
addListItem(addLabel("Ctrl", "primary") + addPlus() + | |
addLabel("Alt", "primary") + addPlus() + | |
addLabel("Right-Click", "default") + | |
" on the rectange (eg. " + addBadge("6") + | |
" ) to show info in " + addBadge("5"))) | |
element.append('</ul>') | |
var dialog = create_dialog_body(element, "Help: EventPlot"); | |
dialog.show(); | |
} | |
return { | |
generate: generate, | |
create_help_dialog: create_help_dialog | |
}; | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment