in data_annotation_platform/app/static/js/makeChart.js [77:369]
function baseChart(
selector,
data,
clickFunction,
annotations,
annotationFunction,
divWidth,
divHeight
) {
if (divWidth === null || typeof divWidth === 'undefined')
divWidth = 1200;
if (divHeight === null || typeof divHeight === 'undefined')
divHeight = 480;
// preprocess the data
data = preprocessData(data);
var svg = d3.select(selector)
.on("touchstart", noZoom)
.on("touchmove", noZoom)
.append("svg")
.attr("width", divWidth)
.attr("height", divHeight)
.attr("viewBox", "-25 0 " + divWidth + " " + divHeight);
var margin = {top: 20, right: 20, bottom: 50, left: 50};
var width = +svg.attr("width") - margin.left - margin.right;
var height = +svg.attr("height") - margin.top - margin.bottom;
var [xAxis, yAxis, xScale, xScaleOrig, yScale, yScaleOrig, yDomainMin, yDomainMax] = scaleAndAxis(
data,
width,
height);
var lineObjects = [];
for (let r=0; r<data.length; r++) {
var lineObj = new d3.line()
.x(function(d) { return xScale(d.X); })
.y(function(d) { return yScale(d.Y); });
lineObjects.push(lineObj);
}
var zoomX = d3.zoom()
.scaleExtent([1, 100])
.translateExtent([[0, 0], [width, height]])
.extent([[0, 0], [width, height]])
.wheelDelta(() => -d3.event.deltaY * 0.002)
.on("zoom", zoomTransformX);
var zoomY = d3.zoom()
.scaleExtent([1, 100])
.translateExtent([[0, 0], [width, height]])
.extent([[0, 0], [width, height]])
.wheelDelta(() => -d3.event.deltaY * 0.004)
.on("zoom", zoomTransformY);
var currentZoom = zoomX; // Default: X-axis zooming
function zoomTransformX() {
const transform = d3.event.transform;
const mouseX = d3.mouse(svg.node())[0]; // Get mouse X position relative to the SVG
const mouseDomainX = xScale.invert(mouseX); // Convert mouse X to domain value
// Transform only the x-axis
xScale.domain(transform.rescaleX(xScaleOrig).domain());
// Adjust the domain to center around the mouse position
const newMouseDomainX = xScale.invert(mouseX);
const domainShift = mouseDomainX - newMouseDomainX;
xScale.domain(xScale.domain().map(d => d + domainShift));
for (let r = 0; r < data.length; r++) {
svg.select(".line-" + r).attr("d", lineObjects[r]);
// Transform the circles
pointSets[r].data(data[r])
.attr("cx", d => xScale(d.X))
.attr("cy", d => yScale(d.Y));
}
// Transform annotation lines (if any)
annoLines = gView.selectAll("line");
annoLines._groups[0].forEach(l => {
l.setAttribute("x1", xScale(l.getAttribute("cp_idx")));
l.setAttribute("x2", xScale(l.getAttribute("cp_idx")));
});
svg.select(".axis--x").call(xAxis);
}
function zoomTransformY() {
const transform = d3.event.transform;
const mouseY = d3.mouse(svg.node())[1]; // Get mouse Y position relative to the SVG
const mouseDomainY = yScale.invert(mouseY); // Convert mouse Y to domain value
// Rescale the Y axis using yScaleOrig
yScale.domain(transform.rescaleY(yScaleOrig).domain());
// Adjust the domain to center around the mouse position
const newMouseDomainY = yScale.invert(mouseY);
const domainShift = mouseDomainY - newMouseDomainY;
yScale.domain(yScale.domain().map(d => d + domainShift));
for (let r = 0; r < data.length; r++) {
svg.select(".line-" + r).attr("d", lineObjects[r]);
// Transform the circles
pointSets[r].data(data[r])
.attr("cx", d => xScale(d.X))
.attr("cy", d => yScale(d.Y));
}
// Transform the annotation lines (if any)
annoLines = gView.selectAll("line");
annoLines._groups[0].forEach(function(l) {
l.setAttribute("y1", yScale(l.getAttribute("cp_idx")));
l.setAttribute("y2", yScale(l.getAttribute("cp_idx")));
});
// Update Y axis
svg.select(".axis--y").call(yAxis);
}
// Define drag behavior for panning
var drag = d3.drag()
.on("drag", function () {
// Calculate the change in X and Y based on drag
var dx = d3.event.dx * (xScale.domain()[1] - xScale.domain()[0]) / width;
var dy = d3.event.dy * (yScale.domain()[1] - yScale.domain()[0]) / height;
// Update X and Y domains
xScale.domain([xScale.domain()[0] - dx, xScale.domain()[1] - dx]);
yScale.domain([yScale.domain()[0] + dy, yScale.domain()[1] + dy]);
// Update the axes
svg.select(".axis--x").call(xAxis);
svg.select(".axis--y").call(yAxis);
// Update lines and points
for (let r = 0; r < data.length; r++) {
svg.select(".line-" + r).attr("d", lineObjects[r]);
pointSets[r].data(data[r])
.attr("cx", d => xScale(d.X))
.attr("cy", d => yScale(d.Y));
}
// Update annotation lines (if any)
annoLines = gView.selectAll("line");
annoLines._groups[0].forEach(function (l) {
l.setAttribute("x1", xScale(l.getAttribute("cp_idx")));
l.setAttribute("x2", xScale(l.getAttribute("cp_idx")));
l.setAttribute("y1", yScale(l.getAttribute("cp_idx")));
l.setAttribute("y2", yScale(l.getAttribute("cp_idx")));
});
});
var zero = xScale(0);
// clip path
svg.append("defs")
.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width - 18)
.attr("height", height)
.attr("transform", "translate(" + zero + ",0)");
// y axis
svg.append("g")
.attr("class", "axis axis--y")
.attr("transform", "translate(" + zero + ",0)") // Use margin.left instead of zero
.call(yAxis);
// x axis
svg.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0, " + height + ")")
.call(xAxis);
// x axis label
svg.append("text")
.attr("text-anchor", "middle")
.attr("class", "axis-label")
.attr("transform", "translate(" + (width - 20) + "," + (height + 50) + ")")
.text("Time");
// wrapper for zoom
var gZoom = svg.append("g")
.call(currentZoom)
.call(drag);
gZoom.call(currentZoom);
// Add event listener for zoom toggle
// document.getElementById("zoomToggle").addEventListener("change", function() {
// if (this.checked) {
// // Y-axis zoom
// currentZoom = zoomY;
// document.getElementById("zoomLabel").innerText = "Change zoom mode (current mode is Y-Axis Zoom)";
// } else {
// // X-axis zoom
// currentZoom = zoomX;
// document.getElementById("zoomLabel").innerText = "Change zoom mode (current mode is X-Axis Zoom)";
// }
// // Apply the current zoom behavior
// gZoom.call(currentZoom);
// });
const zoomToggle = document.getElementById("zoomToggle");
if (zoomToggle) {
zoomToggle.addEventListener("change", function () {
if (this.checked) {
currentZoom = zoomY;
document.getElementById("zoomLabel").innerText =
"Change zoom mode (current mode is Y-Axis Zoom)";
} else {
currentZoom = zoomX;
document.getElementById("zoomLabel").innerText =
"Change zoom mode (current mode is X-Axis Zoom)";
}
gZoom.call(currentZoom);
});
} else {
console.warn('zoomToggle element not found, skipping event listener.');
}
// rectangle for the graph area
gZoom.append("rect")
.attr("width", width)
.attr("height", height);
// view for the graph
var gView = gZoom.append("g")
.attr("class", "view");
// add the line(s) to the view
for (let r=0; r<data.length; r++) {
gView.append("path")
.datum(data[r])
.attr("class", "line line-"+r)
.attr("d", lineObjects[r]);
}
var pointSets = [];
for (let r=0; r<data.length; r++) {
var wrap = gView.append("g");
var points = wrap.selectAll("circle")
.data(data[r])
.enter()
.append("circle")
.attr("cx", function(d) { return xScale(d.X); })
.attr("cy", function(d) { return yScale(d.Y); })
.attr("data_X", function(d) { return d.X; })
.attr("data_Y", function(d) { return d.Y; })
.attr("r", 5)
.on("click", function(d, i) {
d.element = this;
return clickFunction(d, i);
});
pointSets.push(points);
}
// handle the annotations
annotations.forEach(function(a) {
for (i=0; i<points._groups[0].length; i++) {
p = points._groups[0][i];
if (p.getAttribute("data_X") != a.index)
continue;
var elem = d3.select(p);
annotationFunction(a, elem, gView, xScale, yScale, yDomainMin, yDomainMax);
}
});
}