|
<!DOCTYPE html> |
|
<head> |
|
<meta charset="utf-8"> |
|
<title>Gradient Along Stroke by value</title> |
|
<script src="https://d3js.org/d3.v4.min.js"></script> |
|
<script src="//d3js.org/d3-scale-chromatic.v0.3.min.js"></script> |
|
|
|
<style> |
|
body { margin:0; padding: 10; position:fixed; top:0;right:0;bottom:0;left:0; } |
|
|
|
#gradient_path{ |
|
fill:none; |
|
stroke:black; |
|
stroke-width:7px; |
|
|
|
} |
|
</style> |
|
</head> |
|
|
|
<body> |
|
<script> |
|
var width = 960, height= 500; |
|
margin = {top:50, right:20, bottom:50, left:50} |
|
// Feel free to change or delete any of the code you see in this editor! |
|
var svg = d3.select("body").append("svg").attr("id", "gradient_path") |
|
.attr("width", width + margin.left + margin.right ) |
|
.attr("height", height + margin.top + margin.bottom ) |
|
.attr("transform", "scale(0.8)") |
|
|
|
|
|
var line_x, line_y; |
|
var line = d3.line() |
|
.x(function(d) { return line_x(d.x); }) |
|
.y(function(d) { return line_y(d.y); }) |
|
|
|
var gradient_color; |
|
|
|
// data load |
|
d3.csv("gradient_test.csv", function(data){ |
|
console.log(data); |
|
|
|
my_data = [] |
|
data.forEach(function(d){ |
|
my_data.push({ |
|
x: d.posX, |
|
y: d.posY, |
|
alpha: d.alpha |
|
}) |
|
}); |
|
|
|
// set domain of x, y |
|
line_x = d3.scaleLinear().range([0+margin.left, width+margin.right ]) |
|
.domain(d3.extent(my_data, function(d) { return d.x; })) |
|
line_y = d3.scaleLinear().range([height-margin.top, 0+margin.bottom]) |
|
.domain(d3.extent(my_data, function(d) { return d.y; })) |
|
|
|
// set domain of gradient by alpha value |
|
gradient_color = d3.scaleSequential(d3.interpolateRdYlBu) |
|
.domain(d3.extent(my_data, function(d) { return d.alpha})) |
|
|
|
// you can set the color as you want |
|
// gradient_color = d3.scaleLinear() |
|
// .domain(d3.extent(my_data, function(d) { return d.alpha})) |
|
// .range(['steelblue', 'red']); |
|
// gradient_color = d3.interpolateRainbow; |
|
|
|
|
|
|
|
|
|
svg.append("g").append("path") |
|
.data([my_data]) |
|
.attr("class", "line") |
|
.attr("d", line) |
|
|
|
|
|
// drawing gradient path. |
|
var gradient_path_width = 7; |
|
|
|
svg.append("g").selectAll("path") |
|
.data(quads(samples(line(my_data)))) |
|
.enter().append("path") |
|
.attr("class", "gradient_path") |
|
.attr("d", function(d) { return lineJoin(d[0], d[1], d[2], d[3], gradient_path_width)}) |
|
.style("fill", function(d) { return gradient_color(d.alpha)}) |
|
.style("stroke", function(d) { return gradient_color(d.alpha)}) |
|
|
|
|
|
}) |
|
|
|
|
|
// ********************** Gradient color utility ****************************** // |
|
// Sample the SVG path uniformly with the custom precision depending on path length of the line |
|
function samples(d) { |
|
|
|
var path = document.createElementNS(d3.namespaces.svg, "path"); |
|
path.setAttribute("d", d); |
|
|
|
var n = path.getTotalLength(), t = [0], i = 0; |
|
|
|
// calculate our custom precsion (to sample with as same size as with the line length) |
|
var custom_precision = n / my_data.length; |
|
|
|
while ((i += custom_precision) < n) { |
|
t.push(i); |
|
} |
|
t.push(n); |
|
|
|
return t.map(function(t) { |
|
var p = path.getPointAtLength(t), a = [p.x, p.y]; |
|
a.t = t / n; |
|
return a; |
|
}); |
|
} |
|
|
|
// Compute quads of adjacent points [p0, p1, p2, p3]. |
|
function quads(points) { |
|
return d3.range(points.length - 1).map(function(i) { |
|
var a = [points[i - 1], points[i], points[i + 1], points[i + 2]]; |
|
a.t = (points[i].t + points[i + 1].t) / 2; |
|
|
|
// set alpha value as random |
|
// a.alpha = Math.random()*10; |
|
|
|
// proxy of alpha value |
|
if(i < my_data.length){ |
|
a.alpha = my_data[i].alpha; |
|
}else{ |
|
a.alpha = 0; |
|
} |
|
|
|
return a; |
|
}); |
|
} |
|
|
|
// Compute stroke outline for segment p12. |
|
function lineJoin(p0, p1, p2, p3, width) { |
|
var u12 = perp(p1, p2), |
|
r = width / 2, |
|
a = [p1[0] + u12[0] * r, p1[1] + u12[1] * r], |
|
b = [p2[0] + u12[0] * r, p2[1] + u12[1] * r], |
|
c = [p2[0] - u12[0] * r, p2[1] - u12[1] * r], |
|
d = [p1[0] - u12[0] * r, p1[1] - u12[1] * r]; |
|
|
|
if (p0) { // clip ad and dc using average of u01 and u12 |
|
var u01 = perp(p0, p1), e = [p1[0] + u01[0] + u12[0], p1[1] + u01[1] + u12[1]]; |
|
a = lineIntersect(p1, e, a, b); |
|
d = lineIntersect(p1, e, d, c); |
|
} |
|
|
|
if (p3) { // clip ab and dc using average of u12 and u23 |
|
var u23 = perp(p2, p3), e = [p2[0] + u23[0] + u12[0], p2[1] + u23[1] + u12[1]]; |
|
b = lineIntersect(p2, e, a, b); |
|
c = lineIntersect(p2, e, d, c); |
|
} |
|
|
|
return "M" + a + "L" + b + " " + c + " " + d + "Z"; |
|
} |
|
|
|
// Compute intersection of two infinite lines ab and cd. |
|
function lineIntersect(a, b, c, d) { |
|
var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, |
|
y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, |
|
ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21); |
|
return [x1 + ua * x21, y1 + ua * y21]; |
|
} |
|
|
|
// Compute unit vector perpendicular to p01. |
|
function perp(p0, p1) { |
|
var u01x = p0[1] - p1[1], u01y = p1[0] - p0[0], |
|
u01d = Math.sqrt(u01x * u01x + u01y * u01y); |
|
return [u01x / u01d, u01y / u01d]; |
|
} |
|
|
|
|
|
|
|
</script> |
|
</body> |