Skip to content

Instantly share code, notes, and snippets.

@veltman
Created November 22, 2016 17:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save veltman/28032482374490d8e73e03f9bcfd2a19 to your computer and use it in GitHub Desktop.
Save veltman/28032482374490d8e73e03f9bcfd2a19 to your computer and use it in GitHub Desktop.
Sand spiral

Playing around with Anders Hoff's sand spline technique. Actually drawing enough iterations for a spiral this long takes pretty much forever because of all the getPointAtLength() calls, there's probably a way around that.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<style>
svg {
display: none;
}
canvas {
width: 960px;
height: 500px;
}
</style>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg"></svg>
<canvas width="1920" height="1000"></canvas>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var path = d3.select("svg").append("path"),
node = path.node(),
points = spiralPoints(),
context = document.querySelector("canvas").getContext("2d"),
iterations = 0;
path.datum(points);
context.translate(960, 500);
context.fillStyle = "rgba(224, 41, 117, 0.05)";
var spline = d3.line()
.x(function(d){ return d.x; })
.y(function(d){ return d.y; })
.curve(d3.curveBasis);
d3.timer(draw);
function draw() {
var scale,
length,
p,
x,
y
// Reset
if (++iterations > 30) {
context.clearRect(-960, -500, 1920, 1000);
points.forEach(function(point){
point.x = point.original[0];
point.y = point.original[1];
});
iterations = 1;
}
scale = points.map(function(d){
return 0.25 + Math.random() * 0.75;
}).sort(function(a, b){
return a - b;
});
points.forEach(function(point, i){
var noise = scale[i] * 2 * (Math.random() * 2 - 1);
point.x += noise * Math.cos(point.angle);
point.y += noise * Math.sin(point.angle);
});
path.attr("d", spline);
length = node.getTotalLength();
context.beginPath();
for (var i = 0; i < 3500; i++) {
p = node.getPointAtLength(length * i / 3500); // yuck
context.moveTo(p.x * 2, p.y * 2);
context.arc(p.x * 2, p.y * 2, 2, 0, 2 * Math.PI);
}
context.fill();
}
function spiralPoints() {
var maxAngle = Math.PI * 2 * 8;
var polar = d3.range(0, maxAngle, 0.1).map(function(angle, i){
var r = 5 * angle;
return [
r * Math.cos(angle),
r * Math.sin(angle),
];
});
path.attr("d", "M" + polar.join("L"));
var length = node.getTotalLength();
return d3.range(300).map(function(d, i){
var p = node.getPointAtLength(length * d / 300);
return {
original: [p.x, p.y],
x: p.x,
y: p.y,
angle: (p.x || p.y) ? Math.atan(p.y / p.x) : 0
}
});
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment