/**
* Manages displaying everything with d3 library
* @param {number} update_rate - the update rate of the renderloop
* @param {VelocityField} velocityField - the related VelocityField
* @constructor
*/
var D3Manager = function (update_rate, velocityField) {
var UPDATE_RATE = update_rate;
var velocityField = velocityField;
//get svg element to draw to, imagine it like a canvas -
//it has NOTHING to do with the HTML5 canvas, though!
var svgCanvas = d3.select("#canvas").append("svg")
.attr("width", velocityField.widthOfField)
.attr("height", velocityField.heightOfField)
.style("border", "1px solid black");
/**
* sets the background image
* @param {String} imgPath - path to image file
* @param {number} width - final image width
* @param {height} height - final image height
*/
this.setBackgroundImg = function (imgPath, width, height) {
svgCanvas.append("svg:image")
.attr("xlink:href", imgPath)
//.attr("x", "60")
//.attr("y", "60")
.attr("width", width)
.attr("height", height);
}
/**
* renders arrows and arrowHeads
* d3 update pattern explained here: http://quintonlouisaiken.com/d3-general-update-pattern/
* and here: https://bost.ocks.org/mike/join/
* and here is an example with lines which helped me : http://bl.ocks.org/jonsadka/482005612916b3f5e408
* @param {Array.<Arrow>} arrows - the arrows which should be rendered
* @param {Array.<ArrowHead>} arrowHeads - coresponding Arrowheads
*/
this.render = function (arrows, arrowHeads) {
var lineFunction = d3.svg.area()
.x(function (d) {return d.x;})
.y(function (d) {return d.y;})
.y0(function (d) {return (d.y + 5) ;})
.y1(function (d) {return (d.y - 5) ;})
.interpolate("basis");
var circleFunction = d3.svg.area()
.x(function (d) {return d.x + d.xOffset;}) //redo the x scale for a circle, would be to small with small velocity
.y(function (d) {return d.y;})
.y0(function (d) {return (d.cy + d.yOffset);}) //upper y-bound of the area
.y1(function (d) {return (d.cy - d.yOffset);}) //lower y-bound of the area
.interpolate("basis");
var arrowData = arrowsToLineData(arrows);
var d3arrows = svgCanvas.selectAll("path").data(arrowData);
// transition from previous paths to new paths - update the existing ones
d3arrows.transition().duration(UPDATE_RATE)
.attr("d", function (d, i){
if(arrowData[i][0].isCirc == 0) { return lineFunction(d)}
else { return circleFunction(d,i)}
;})
.attr("stroke", "blue")
.attr("stroke-width", function (d, i) {return arrowData[i][0].st;} )
.attr("fill", "yellow")
.attr("opacity", function (d, i) {return arrowData[i][0].op;});
// draw any new arrows if neccessary
d3arrows.enter()
.append("path")
.attr("d", function (d, i){
if(arrowData[i][0].isCirc == 0) {return lineFunction(d)}
else {return circleFunction(d,i)}
;})
.attr("stroke", "blue")
.attr("stroke-width", function (d, i) {return arrowData[i][0].st;})
.attr("fill", "yellow")
.attr("opacity", function (d, i) {return arrowData[i][0].op;});
d3arrows.exit().remove();
//Draw the arrowheads
var triangleData = arrowHeadsToTriangleData(arrowHeads); //TODO: rotate polygons according to the angles
var d3arrowHeads = svgCanvas.selectAll("polygon").data(triangleData);
d3arrowHeads.transition().duration(UPDATE_RATE)
.attr("points", function (d, i) {
if(arrowData[i][0].isCirc == 0) {
return '-16 -16 , -16 16 , 16 0'}
else { return '0 0, 0 0, 0 0' }
;})
.attr("transform", function (d, i) {
//TODO: ROTATION SHOULD BE triangleData[i][0].rotation BUT IS NaN? WTF?
//console.log("TODO: FIX ROTATION IN D3 MANAGER!")
return "translate(" + (triangleData[i][0].mX) + "," + triangleData[i][0].mY + ") rotate(" + triangleData[i][0].angle + ")";
})
.attr("stroke", "blue")
.attr("stroke-width", function (d, i) {return arrowData[i][0].st;})
.attr("fill", "yellow")
.attr("opacity", function (d, i) {return arrowData[i][0].op;});
// draw new arrowHeads if neccessary
d3arrowHeads.enter()
.append("polygon")
.attr("class", "aHeads")
.attr("points", function (d, i) {
if(arrowData[i][0].isCirc == 0) {
return '-16 -16 , -16 16 , 16 0'}
else { return '0 0, 0 0, 0 0' }
;})
.attr("transform", function (d, i) {
//console.log("TODO: FIX ROTATION IN D3 MANAGER!")
//console.log(triangleData[i][0].angle)
return "translate(" + (triangleData[i][0].mX) + "," + triangleData[i][0].mY + ") rotate(" + triangleData[i][0].angle + ")";
})
.attr("stroke", "blue")
.attr("stroke-width", function (d, i) {return arrowData[i][0].st;})
.attr("fill", "yellow")
.attr("opacity", function (d, i) {return arrowData[i][0].op;});
d3arrowHeads.exit().remove();
/*
//DEBUG draw handle Points
var pointData = arrowsToPointData(arrows);
var d3arrowPoints = svgCanvas.selectAll("circle").data(pointData);
d3arrowPoints.transition().duration(UPDATE_RATE)
.attr("cx", function(d) {
return d[0];
})
.attr("cy", function(d) {
return d[1];
})
.attr("r", "5px")
.attr("fill", "red");
d3arrowPoints.enter().append("circle")
.attr("cx", function(d) {
return d[0];
})
.attr("cy", function(d) {
return d[1];
})
.attr("r", "5px")
.attr("fill", "red");
d3arrowPoints.exit().remove();
//DEBUG distanceMap lines
svgCanvas.append("line") // attach a line
.style("stroke", "black") // colour the line
.attr("x1", 100) // x position of the first end of the line
.attr("y1", 0) // y position of the first end of the line
.attr("x2", 100) // x position of the second end of the line
.attr("y2", 800); // y position of the second end of the line
svgCanvas.append("line") // attach a line
.style("stroke", "black") // colour the line
.attr("x1", 200) // x position of the first end of the line
.attr("y1", 0) // y position of the first end of the line
.attr("x2", 200) // x position of the second end of the line
.attr("y2", 800); // y position of the second end of the line
svgCanvas.append("line") // attach a line
.style("stroke", "black") // colour the line
.attr("x1", 300) // x position of the first end of the line
.attr("y1", 0) // y position of the first end of the line
.attr("x2", 300) // x position of the second end of the line
.attr("y2", 800); // y position of the second end of the line
svgCanvas.append("line") // attach a line
.style("stroke", "black") // colour the line
.attr("x1", 400) // x position of the first end of the line
.attr("y1", 0) // y position of the first end of the line
.attr("x2", 400) // x position of the second end of the line
.attr("y2", 800); // y position of the second end of the line
svgCanvas.append("line") // attach a line
.style("stroke", "black") // colour the line
.attr("x1", 500) // x position of the first end of the line
.attr("y1", 0) // y position of the first end of the line
.attr("x2", 500) // x position of the second end of the line
.attr("y2", 800); // y position of the second end of the line
svgCanvas.append("line") // attach a line
.style("stroke", "black") // colour the line
.attr("x1", 600) // x position of the first end of the line
.attr("y1", 0) // y position of the first end of the line
.attr("x2", 600) // x position of the second end of the line
.attr("y2", 800); // y position of the second end of the line
svgCanvas.append("line") // attach a line
.style("stroke", "black") // colour the line
.attr("x1", 700) // x position of the first end of the line
.attr("y1", 0) // y position of the first end of the line
.attr("x2", 700) // x position of the second end of the line
.attr("y2", 800); // y position of the second end of the line
svgCanvas.append("line") // attach a line
.style("stroke", "black") // colour the line
.attr("x1", 800) // x position of the first end of the line
.attr("y1", 0) // y position of the first end of the line
.attr("x2", 800) // x position of the second end of the line
.attr("y2", 800); // y position of the second end of the line
svgCanvas.append("line") // attach a line
.style("stroke", "black") // colour the line
.attr("x1", 900) // x position of the first end of the line
.attr("y1", 0) // y position of the first end of the line
.attr("x2", 900) // x position of the second end of the line
.attr("y2", 800); // y position of the second end of the line
svgCanvas.append("line") // attach a line
.style("stroke", "black") // colour the line
.attr("x1", 1000) // x position of the first end of the line
.attr("y1", 0) // y position of the first end of the line
.attr("x2", 1000) // x position of the second end of the line
.attr("y2", 800); // y position of the second end of the line
//horizontal lines
svgCanvas.append("line") // attach a line
.style("stroke", "black") // colour the line
.attr("x1", 0) // x position of the first end of the line
.attr("y1", 0) // y position of the first end of the line
.attr("x2", 1000) // x position of the second end of the line
.attr("y2", 0); // y position of the second end of the line
svgCanvas.append("line") // attach a line
.style("stroke", "black") // colour the line
.attr("x1", 0) // x position of the first end of the line
.attr("y1", 100) // y position of the first end of the line
.attr("x2", 1000) // x position of the second end of the line
.attr("y2", 100); // y position of the second end of the line
svgCanvas.append("line") // attach a line
.style("stroke", "black") // colour the line
.attr("x1", 0) // x position of the first end of the line
.attr("y1", 200) // y position of the first end of the line
.attr("x2", 1000) // x position of the second end of the line
.attr("y2", 200); // y position of the second end of the line
svgCanvas.append("line") // attach a line
.style("stroke", "black") // colour the line
.attr("x1", 0) // x position of the first end of the line
.attr("y1", 300) // y position of the first end of the line
.attr("x2", 1000) // x position of the second end of the line
.attr("y2", 300); // y position of the second end of the line
svgCanvas.append("line") // attach a line
.style("stroke", "black") // colour the line
.attr("x1", 0) // x position of the first end of the line
.attr("y1", 400) // y position of the first end of the line
.attr("x2", 1000) // x position of the second end of the line
.attr("y2", 400); // y position of the second end of the line
svgCanvas.append("line") // attach a line
.style("stroke", "black") // colour the line
.attr("x1", 0) // x position of the first end of the line
.attr("y1", 500) // y position of the first end of the line
.attr("x2", 1000) // x position of the second end of the line
.attr("y2", 500); // y position of the second end of the line
svgCanvas.append("line") // attach a line
.style("stroke", "black") // colour the line
.attr("x1", 0) // x position of the first end of the line
.attr("y1", 600) // y position of the first end of the line
.attr("x2", 1000) // x position of the second end of the line
.attr("y2", 600); // y position of the second end of the line
svgCanvas.append("line") // attach a line
.style("stroke", "black") // colour the line
.attr("x1", 0) // x position of the first end of the line
.attr("y1", 700) // y position of the first end of the line
.attr("x2", 1000) // x position of the second end of the line
.attr("y2", 700); // y position of the second end of the line
svgCanvas.append("line") // attach a line
.style("stroke", "black") // colour the line
.attr("x1", 0) // x position of the first end of the line
.attr("y1", 800) // y position of the first end of the line
.attr("x2", 1000) // x position of the second end of the line
.attr("y2", 800); // y position of the second end of the line
*/
}
/**
* map all positional data from the arrows array to an array of coordinates for the SVG to render paths
* @param {Array.<Arrow>} arrows - the arrows
* @returns {Array} restructured arrows for d3
*/
var arrowsToLineData = function(arrows) {
var dataArrows = arrows.map(function(arrow){
var rObj =
[ {"x":arrow.currp0x, "y":arrow.currp0y, "yOffset":arrow.oy1, "xOffset":arrow.ox1, "cy":arrow.yCirc, "isCirc":arrow.isCirc, "op":arrow.opacity, "st":arrow.strokeWidth}, //erstelle 2 verschiedene arrays, ja nachdem, was im arrow drin ist.
{"x":arrow.currp1x, "y":arrow.currp1y, "yOffset":arrow.oy2, "xOffset":arrow.ox2, "cy":arrow.yCirc},
{"x":arrow.currX, "y":arrow.currY, "yOffset":arrow.oy3, "xOffset":arrow.ox3, "cy":arrow.yCirc},
{"x":arrow.currp2x, "y":arrow.currp2y, "yOffset":arrow.oy4, "xOffset":arrow.ox4, "cy":arrow.yCirc},
{"x":arrow.currp3x, "y":arrow.currp3y, "yOffset":arrow.oy5, "xOffset":arrow.ox5, "cy":arrow.yCirc},
];
return rObj;
});
return dataArrows;
}
/**
* restructure ArrowHeads for d3
* @param {Array.<ArrowHead>} arrowHeads - the ArrowHeads
* @returns {Array} restructured Arrowsheads for d3
*/
var arrowHeadsToTriangleData = function(arrowHeads) {
var data = arrowHeads.map(function(arrowHead){
var rObj =
[
{"mX":arrowHead.posX , "mY": arrowHead.posY, "angle": arrowHead.rotation}
];
return rObj;
});
return data;
}
//
var arrowsToPointData = function(arrows) {
var points = [];
arrows.forEach(function(arrow) {
points.push([arrow.currp0x, arrow.currp0y]);
points.push([arrow.currp0x, arrow.currp0y]);
points.push([arrow.currp1x, arrow.currp1y]);
points.push([arrow.currX, arrow.currY]);
points.push([arrow.currp2x, arrow.currp2y]);
points.push([arrow.currp3x, arrow.currp3y])
})
return points;
}
}