|
<!DOCTYPE html> |
|
|
|
<head> |
|
<meta charset="utf-8"> |
|
<script src="https://d3js.org/d3.v4.min.js"></script> |
|
<script src="triangleEquations.js"></script> |
|
<style> |
|
body { |
|
margin: 0; |
|
position: fixed; |
|
top: 0; |
|
right: 0; |
|
bottom: 0; |
|
left: 0; |
|
background: DarkSlateBlue; |
|
} |
|
|
|
g.tick > line { |
|
stroke: lightgrey; |
|
stroke-dasharray: 2,2; |
|
} |
|
|
|
.axis-label { |
|
fill: lightgrey; |
|
} |
|
|
|
</style> |
|
</head> |
|
|
|
<body> |
|
<script> |
|
|
|
const data = [0, 4, 1, 3, 4, 6, 3, 8, 11, 0, 2, 3, 3, 1, 8, 3, 0]; |
|
const noOfCharts = 24; |
|
const dataLength = data.length; |
|
|
|
const domain = [0, d3.max(data)]; |
|
|
|
let colour = d3.scaleSequential(d3.interpolatePlasma) |
|
.domain([100,0]) |
|
|
|
let xTickValues = []; |
|
for (t = 0; t < dataLength; t++) { |
|
xTickValues[t] = t; |
|
}; |
|
|
|
const gradientColours = [ |
|
{ "offset": 0}, |
|
{ "offset": 21}, |
|
{ "offset": 60}, |
|
{ "offset": 100} |
|
]; |
|
|
|
const xDiagonal = 451; |
|
const zDiagonal = 500; |
|
const yHeight = 102; |
|
|
|
const xAngleDegrees = 30; |
|
const xzAngleDegrees = 90 + xAngleDegrees; |
|
const zAngleDegrees = 180 - xzAngleDegrees - xAngleDegrees; |
|
|
|
const xzAngle = xzAngleDegrees * (Math.PI/180); |
|
const xAngle = xAngleDegrees * (Math.PI/180); //between xDiagonal and horizontal |
|
const zAngle = zAngleDegrees * (Math.PI/180); |
|
|
|
let xWidth = adjacentCos(xAngle, xDiagonal); |
|
let zWidth = adjacentCos(zAngle, zDiagonal); |
|
|
|
let xHeight = triangleSide(xWidth, xDiagonal); |
|
let zHeight = triangleSide(zWidth, zDiagonal); |
|
|
|
const margin = { "top": 50, "right": 50, "bottom": 50, "left": 50 }; |
|
const containerHeight = xHeight + zHeight + yHeight; |
|
const containerWidth = xWidth + zWidth; |
|
|
|
let xScale = d3.scaleLinear() |
|
.domain([0, (dataLength - 1)]) |
|
.range([0, xWidth]); |
|
|
|
let yScale = d3.scaleLinear() |
|
.domain([0, d3.max(data)]) |
|
.range([yHeight, 0]); |
|
|
|
let zScale = d3.scaleLinear() |
|
.domain([0, (noOfCharts-1)]) |
|
.range([0, zWidth]) |
|
|
|
let area = d3.area() |
|
.x(function (d, i) { |
|
return xScale(i); |
|
}) |
|
.y0(function (d, i) { |
|
return yArea(0, i); |
|
}) |
|
.y1(function (d, i) { |
|
return yArea(d, i); |
|
}); |
|
|
|
let svg = d3.select("body") |
|
.append("svg") |
|
.attr("width", containerWidth + margin.left + margin.right) |
|
.attr("height", containerHeight + margin.top + margin.bottom); |
|
|
|
let defs = svg.append("defs"); |
|
|
|
let axes = svg.append("g") |
|
.attr("transform", "translate(" + margin.left + "," +margin.top + ")") |
|
.attr("class", "axes") |
|
.call(drawXAxis); |
|
|
|
let charts = svg.append("g") |
|
.attr("transform", "translate(" + margin.left + "," +margin.top + ")") |
|
.attr("class", "charts"); |
|
|
|
for (var series = 0; series < noOfCharts; series++) { |
|
|
|
let chartData = []; |
|
data.forEach(function(d,i){ |
|
let change = Math.ceil(Math.random() * 3); |
|
chartData[i] = data[i] + change; |
|
}); |
|
|
|
let g = charts.append("g") |
|
.attr("transform", "translate(" + areaOffsetX(series) + "," + areaOffsetY(series) + ")"); |
|
|
|
g.append("text") |
|
.attr("class", "axis-label") |
|
.text(series) |
|
.attr("x", -8) |
|
.attr("y", yHeight + 8) |
|
.style("text-anchor", "end") |
|
|
|
let areaChart = g.append("path") |
|
.datum(chartData) |
|
.style("fill", "url(#gradient" + series + ")") |
|
.attr("stroke", "DarkSlateBlue") |
|
.attr("stroke-linejoin", "round") |
|
.attr("stroke-linecap", "round") |
|
.attr("stroke-width", 1.5) |
|
.style("fill-opacity", 0.9) |
|
.attr("d", area); |
|
|
|
let gradient = defs.append("linearGradient") |
|
.attr("id", "gradient" + series) |
|
.attr("x1", 0) |
|
.attr("y1", 0) |
|
.attr("x2", 0) |
|
.attr("y2", yHeight) |
|
.attr("gradientUnits", "userSpaceOnUse") |
|
.attr("gradientTransform","rotate(-"+ xAngleDegrees +",0,0)") |
|
|
|
gradient.selectAll("stop") |
|
.data(gradientColours) |
|
.enter() |
|
.append("stop") |
|
.attr("offset", function (d) { return d.offset + "%"; }) |
|
.attr("stop-color", function (d) { return colour(d.offset); }); |
|
|
|
}; //end of for loop |
|
|
|
function areaOffsetX(i) { |
|
return i * (zWidth / (noOfCharts - 1)) |
|
}; |
|
|
|
function areaOffsetY(i) { |
|
let defaultY = containerHeight - zHeight - yHeight; |
|
let offset = i * (zHeight / (noOfCharts - 1)); |
|
return defaultY + offset; |
|
}; |
|
|
|
function yArea(d, i) { |
|
let n = xHeight * (i / dataLength) |
|
return yScale(d) - n; |
|
}; |
|
|
|
function xCoord(x, y, z) { |
|
let x1 = xScale(x); |
|
let z1 = zScale(z); |
|
return seriesWidth + xScale(x) - zScale(z) |
|
}; |
|
|
|
function yCoord(x, y, z) { |
|
let x1 = xHeight * (x / dataLength); |
|
let y1 = chartHeight - yScale(y); |
|
let z1 = zHeight * (z / noOfCharts); |
|
return height - (y1 + x1 + z1) |
|
}; |
|
|
|
function drawXAxis(sel) { |
|
|
|
let xAxis = sel.append("g") |
|
.attr("id", "x-axis") |
|
.attr("transform", "translate(" + areaOffsetX(0) + "," + areaOffsetY(0) + ")"); |
|
|
|
let xTicks = xAxis.selectAll(".ticks") |
|
.data(xTickValues) |
|
.enter() |
|
.append("g") |
|
.attr("class", "tick") |
|
.attr("transform", function (d) { |
|
let x = xScale(d); |
|
let y = yArea(0, d); |
|
return "translate(" + x + "," + y + ")" |
|
}); |
|
|
|
let tickLength = zDiagonal + 25; |
|
let tickText = tickLength + 5; |
|
|
|
xTicks.append("line") |
|
.attr("x1", 0) |
|
.attr("y1", 0) |
|
.attr("x2", adjacentCos(zAngle, tickLength)) |
|
.attr("y2", oppositeSin(zAngle, tickLength)); |
|
|
|
xTicks.append("text") |
|
.attr("class", "axis-label") |
|
.text(function(d){ return d; }) |
|
.attr("x", adjacentCos(zAngle, tickText)) |
|
.attr("y", oppositeSin(zAngle, tickText)); |
|
}; |
|
|
|
</script> |
|
</body> |