xxxxxxxxxx
<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