D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
bmershon
Full window
Github gist
Apollonian Gasket
<!DOCTYPE html> <meta charset="utf-8"> <svg width="960" height="960"></svg> <script src="https://d3js.org/d3.v4.min.js" charset="utf-8"></script> <script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script> <script src="apollonius-circle.js" charset="utf-8"></script> <script src="subsets.js" charset="utf-8"></script> <script> var svg = d3.select("svg"), width = +svg.attr("width"), height = +svg.attr("height"); var N = 8; var generations = []; var threeCircles = d3.range(3).map((d, i) => { return { x: width / 2 + width / 4 * Math.sin((i + 1) * 2 * Math.PI / 3 + Math.PI), y: height / 2 + height / 4 * Math.cos((i + 1) * 2 * Math.PI / 3 + Math.PI), r: 0, generation: 0, } }); var initialRadius = distance(threeCircles[0].x, threeCircles[0].y, threeCircles[1].x, threeCircles[1].y) / 2; threeCircles.forEach((d, i) => { d.r = initialRadius; }); var bigCircle = enclosingCircle.apply(null, threeCircles); bigCircle.generation = -1; generations.push(bigCircle); Array.prototype.push.apply(generations, threeCircles); var color = d3.scaleSequential(d3.interpolateInferno) .domain([1, N]); iterate(N); function iterate(n) { let i = 0, triplets = [threeCircles]; newTriplets = []; triplets = triplets.concat(subsets(threeCircles, 2).map((subset) => { return [bigCircle].concat(subset); })); while (++i <= n) { triplets.forEach((t) => { let solution; // Ensure the circle with the largest radius is the first of the triplet. // The largest radius circle may be the enclosing circle; the solutions // for the Apollonius circles (up to 8 of them) depends on orientation. t.sort((a, b) => -(a.r - b.r)); // Choose which of the 8 possible Apollonius circles to solve for. if (t.indexOf(bigCircle) === -1) { solution = inscribedCircle.apply(null, t); } else { solution = borderCircle.apply(null, t); } solution.generation = i; generations.push(solution); // Each enclosing circle generates 3 more triplets which will each produce // a circle in the next generation. subsets(t, 2).forEach((subset) => { newTriplets.push([solution].concat(subset)); }); }); triplets = newTriplets; newTriplets = []; } var circle = svg.selectAll(".circle") .data(generations) .enter().append("g") .attr("class", "circle") .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); circle.append("circle") .attr("r", function(d) { return d.r; }) .style("fill", function(d, i) { return d.generation < 1 ? 'none' : color(d.generation); }); } function distance(x0, y0, x1, y1) { return Math.sqrt(Math.pow(x0 - x1, 2) + Math.pow(y0 - y1, 2)); } function circleArea(c) { return Math.PI * c.r * c.r; } function enclosingCircle(c1, c2, c3) { return apolloniusCircle(c1.x, c1.y, +c1.r, c2.x, c2.y, +c2.r, c3.x, c3.y, +c3.r); } function borderCircle(c1, c2, c3) { return apolloniusCircle(c1.x, c1.y, +c1.r, c2.x, c2.y, -c2.r, c3.x, c3.y, -c3.r); } function inscribedCircle(c1, c2, c3) { return apolloniusCircle(c1.x, c1.y, -c1.r, c2.x, c2.y, -c2.r, c3.x, c3.y, -c3.r); } </script>
https://d3js.org/d3.v4.min.js
https://d3js.org/d3-scale-chromatic.v1.min.js