Skip to content

Instantly share code, notes, and snippets.

@sarah37
Last active March 17, 2019 18:05
Show Gist options
  • Select an option

  • Save sarah37/d42fdff394497f4faa815630e28967df to your computer and use it in GitHub Desktop.

Select an option

Save sarah37/d42fdff394497f4faa815630e28967df to your computer and use it in GitHub Desktop.
Sierpinski III
height: 960
redirect: https://observablehq.com/@sarah37/sierpinski-triangles

Sierpinski triangle with d3 and svg, constructed using the Sierpiński arrowhead curve. I used Mike Bostock's pathTween function from here to get the transitions to work.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Sierpinski</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script src="https://d3js.org/d3.v4.js"></script>
</head>
<body>
<div id="svgDiv"></div>
<script type="text/javascript" src="sierpinski.js"></script>
</body>
</html>
var w = 960;
var h = 960;
var b = 20;
var svg = d3.select("#svgDiv")
.append("svg")
.attr("width", w)
.attr("height", h);
var points = [[b, h/2 + (Math.sqrt(3)*w/2 - b)/2], [w-b, h/2 + (Math.sqrt(3)*w/2 - b)/2]];
svg.append("path").attr("d", function() {return makePath(points)})
// set iterations
var iter = 8;
var i = 0;
var even = true; // the direction of the bend is toggled at every iteration
iterate(points)
function iterate(points) {
setTimeout(function() {
var newPoints = [points[0]];
for (var j = 0; j < points.length - 1; j++) {
newPoints.push(...bend(points[j], points[j+1]))
}
svg.select("path")
.transition()
.duration(2000)
.attrTween("d", pathTween(makePath(newPoints), 2));
i++
if (i < iter) {iterate(newPoints)}
}, 2500)
}
function halfway(a,b) {
return [(a[0]+b[0]) / 2, (a[1]+b[1]) / 2]
}
function bend(a,d) {
even = !even;
var L = Math.sqrt(Math.pow(d[0]-a[0], 2) + Math.pow(d[1]-a[1], 2))
var alpha = Math.atan( (d[1]-a[1]) / (d[0]-a[0]) )
if (d[0]-a[0] < 0) {alpha += Math.PI}
if (even) {
var b1 = a[0] + (L/2) * Math.cos(alpha + Math.PI/3)
var b2 = a[1] + (L/2) * Math.sin(alpha + Math.PI/3)
var c1 = a[0] + 0.5 * Math.sqrt(3) * L * Math.cos(alpha + Math.PI/6)
var c2 = a[1] + 0.5 * Math.sqrt(3) * L * Math.sin(alpha + Math.PI/6)
}
else {
var b1 = a[0] + (L/2) * Math.cos(alpha - Math.PI/3)
var b2 = a[1] + (L/2) * Math.sin(alpha - Math.PI/3)
var c1 = a[0] + 0.5 * Math.sqrt(3) * L * Math.cos(alpha - Math.PI/6)
var c2 = a[1] + 0.5 * Math.sqrt(3) * L * Math.sin(alpha - Math.PI/6)
}
return [[b1, b2], [c1, c2], d]
}
function makePath(points) {
var p = "M" + points[0][0] + "," + points[0][1];
for (var i = 1; i < points.length; i++) {
p += "L" + points[i][0] + "," + points[i][1];
}
return p
}
// from https://bl.ocks.org/mbostock/3916621
function pathTween(d1, precision) {
return function() {
var path0 = this,
path1 = path0.cloneNode(),
n0 = path0.getTotalLength(),
n1 = (path1.setAttribute("d", d1), path1).getTotalLength();
// Uniform sampling of distance based on specified precision.
var distances = [0], i = 0, dt = precision / Math.max(n0, n1);
while ((i += dt) < 1) distances.push(i);
distances.push(1);
// Compute point-interpolators at each distance.
var points = distances.map(function(t) {
var p0 = path0.getPointAtLength(t * n0),
p1 = path1.getPointAtLength(t * n1);
return d3.interpolate([p0.x, p0.y], [p1.x, p1.y]);
});
return function(t) {
return t < 1 ? "M" + points.map(function(p) { return p(t); }).join("L") : d1;
};
};
}
body, html {
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
background-color: #000;
}
#counter {
position: absolute;
left: 10px;
top: 10px;
font-family: sans-serif;
}
path {
stroke: #fff;
stroke-width: 2px;
stroke-linecap: round;
stroke-linejoin: round;
fill: none;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment