Need to convert to Canvas
forked from widged's block: Rapid implementation of a ternary plot with d3js
xxxxxxxxxx
<html lang="en">
<head>
<meta charset="UTF-8">
<title>D3 Ternary Plot</title>
<style>
.border {
fill: none;
stroke-width: 2;
stroke: #363636;
}
line.axis {
stroke-width: 2;
stroke: #363636;
}
line.tick {
stroke-width: 1;
stroke: #c3c3c3;
}
text.tick-text {
font-family: "sans-serif";
font-size: 10px;
fill: #000;
}
</style>
</head>
<body>
<div id="plot">
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
(function() {
console.clear()
var width = 600;
var height = triangleHeight(width)
var margin = 20;
var density = 50
var rotateHSL = 0
var scaleMax = 100
var f = scaleMax / 20
var svg = d3.select('#plot').append('svg')
.attr("width", 800)
.attr("height", 800);
var background = svg.append("g")
.attr("id", "chart-background")
var axes = svg.append("g")
.attr("id", "chart-axes")
var maxDistanceToCentre = Math.ceil(2 * (height/3))
var centre = {
"x": (width/2) + margin,
"y": margin + maxDistanceToCentre
}
var corners = {
"left": {},
"top": {},
"right": {}
}
corners.left.x = margin
corners.left.y = height + margin
corners.top.x = (width/2) + margin
corners.top.y = margin
corners.right.x = width + margin
corners.right.y = height + margin
svg.append('defs')
.append('clipPath')
.attr('id', "clip")
.append('path')
.attr("class", "border")
.attr('d', "M " + corners.left.x + " " + corners.left.y + ", "
+ "L " + corners.top.x + " " + corners.top.y + ", "
+ "L " + corners.right.x + " " + corners.right.y + ", "
+ "z"
)
var data = []
for (var i = 0; i <= density; i++ ) {
let density2 = density - i
for (var j = 0; j <= density2; j++ ) {
let k = density2 - j
data.push(coord((i * f), (j * f), (k * f)))
}
}
var ticks = [0,20,40,60,80,100]
var n = ticks.length;
ticks.forEach(function(v) {
var coord1 = coord(v, 0, 100-v);
var coord2 = coord(v, 100-v, 0);
var coord3 = coord(0, 100-v, v);
var coord4 = coord(100-v, 0, v);
if(v !== 0 && v !== 100) {
axes.append("line")
.attr("x1", coord1.x)
.attr("y1", coord1.y)
.attr("x2", coord2.x)
.attr("y2", coord2.y)
.classed('tick', true);
axes.append("line")
.attr("x1", coord2.x)
.attr("y1", coord2.y)
.attr("x2", coord3.x)
.attr("y2", coord3.y)
.classed('tick', true);
axes.append("line")
.attr("x1", coord3.x)
.attr("y1", coord3.y)
.attr("x2", coord4.x)
.attr("y2", coord4.y)
.classed('tick', true);
}
axes.append("text")
.attr("x", coord1.x - 15)
.attr("y", coord1.y )
.text( function (d) { return v; })
.classed('tick-text tick-a', true);
axes.append("text")
.attr("x", coord2.x - 6)
.attr("y", coord2.y + 10 )
.text( function (d) { return (100- v); })
.classed('tick-text tick-b', true);
axes.append("text")
.attr("x", coord3.x + 6)
.attr("y", coord3.y )
.text( function (d) { return v; })
.classed('tick-text tick-c', true);
})
var circles = background.selectAll("circle")
.data(data);
circles.enter()
.append("circle")
.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; })
.style("fill", function(d){ return color(d) })
.attr("clip-path", "url(#clip)")
.attr("r", ((width / density)))
.attr("title", function(d){
return "" + d.h + ", " + d.l + ", " + d.s
});
function color(d) {
let x = Math.abs(d.x - centre.x)
let y = Math.abs(d.y - centre.y)
if (d.y < centre.y && d.x > centre.x) {
d.angle = angleTan(x,y) * (180 / Math.PI)
}
if (d.y <= centre.y && d.x <= centre.x) {
d.angle = 360 - (angleTan(x,y) * (180 / Math.PI))
}
if (d.y > centre.y && d.x < centre.x) {
d.angle = 180 + (angleTan(x,y) * (180 / Math.PI))
}
if (d.y >= centre.y && d.x >= centre.x) {
d.angle = 180 - (angleTan(x,y) * (180 / Math.PI))
}
if (d.angle <= 60 || d.angle >= 300 ) {
x = Math.abs(d.x - corners.top.x)
y = Math.abs(d.y - corners.top.y)
d.distance = distanceRatio(x, y)
} else if (d.angle >= 60 && d.angle <= 180 ) {
x = Math.abs(d.x - corners.right.x)
y = Math.abs(d.y - corners.right.y)
d.distance = distanceRatio(x, y)
} else if (d.angle >= 180 && d.angle <= 300 ) {
x = Math.abs(d.x - corners.left.x)
y = Math.abs(d.y - corners.left.y)
d.distance = distanceRatio(x, y)
}
d.hAngle = Math.floor(d.angle + rotateHSL)
d.sat = 0.6 - (d.distance / 2)
d.lum = 0.2 + (d.distance * 0.8)
let hslColor = d3.hsl(d.hAngle, d.sat, d.lum)
return hslColor
}
function angleTan(opposite, adjacent) {
return Math.atan(opposite/adjacent);
}
function triangleHypotenuse(sideA, sideB) {
return Math.sqrt(Math.pow(sideA, 2) + Math.pow(sideB, 2))
};
function distanceRatio(x, y){
return triangleHypotenuse(x, y) / maxDistanceToCentre
}
function coord(a, b, c){
var sum
var pos = {};
pos.a = a
pos.b = b
pos.c = c
sum = a + b + c;
if(sum !== 0) {
a /= sum;
b /= sum;
c /= sum;
pos.x = corners.left.x * a + corners.right.x * b + corners.top.x * c;
pos.y = corners.left.y * a + corners.right.y * b + corners.top.y * c;
}
return pos;
}
function scale(/* point */ p, factor) {
return [p[0] * factor, p[1] * factor];
}
function triangleHeight(width){
return Math.sqrt((width * width) - (width/2 * width/2));
}
})()
</script>
</body>
</html>
https://d3js.org/d3.v4.min.js