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.
Last active
March 17, 2019 18:05
-
-
Save sarah37/d42fdff394497f4faa815630e28967df to your computer and use it in GitHub Desktop.
Sierpinski III
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| height: 960 | |
| redirect: https://observablehq.com/@sarah37/sierpinski-triangles |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; | |
| }; | |
| }; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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