Published
Edited
Jan 8, 2022
8 stars
Insert cell
Insert cell
{
const height = 500;
const data = mutable circles;

const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.attr("stroke-width", 1.5);

const circle = svg.append("g")
.attr("fill", "none")
.attr("stroke", "black")
.attr("pointer-events", "all")
.selectAll("circle")
.data(data)
.join("circle")
.attr("r", ({r}) => r)
.call(d3.drag().on("drag", dragged));

const line = svg.append("line")
.attr("stroke", "red")
.attr("pointer-events", "none");

const point = svg.append("circle")
.attr("fill", "blue")
.attr("pointer-events", "none")
.attr("r", 3.5);

const orthocircle = svg.append("circle")
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-dasharray", "4,4")
.attr("pointer-events", "none");

const tangentline = svg.append("g")
.attr("stroke", "blue")
.selectAll("line")
.data(new Array(4))
.join("line");

function update() {
const [a, b] = data;
const [cx, cy] = radical(a, b);
const k = (width + height) / Math.hypot(cx, cy);
const [vx, vy] = [(b.x - cx) * k, (b.y - cy) * k];
const o = {x: cx, y: cy, r: Math.sqrt(Math.abs((cx - a.x) ** 2 + (cy - a.y) ** 2 - a.r ** 2))};

circle
.attr("cx", ({x}) => x)
.attr("cy", ({y}) => y);

point
.attr("cx", cx)
.attr("cy", cy);

orthocircle
.attr("cx", o.x)
.attr("cy", o.y)
.attr("r", o.r);

line
.attr("x1", cx - vy)
.attr("y1", cy + vx)
.attr("x2", cx + vy)
.attr("y2", cy - vx);

tangentline
.data([...intersections(o, a), ...intersections(o, b)])
.attr("x1", cx)
.attr("y1", cy)
.attr("x2", ([x]) => x)
.attr("y2", ([, y]) => y)
}

function dragged(event, d) {
d.x = event.x, d.y = event.y;
mutable circles = data;
update();
}

return svg.call(update).node();
}
Insert cell
function radical(a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
const l = Math.hypot(dx, dy);
const k = (l - (a.r * a.r - b.r * b.r) / l) / (2 * l);
return [b.x + dx * k, b.y + dy * k];
}
Insert cell
function intersections(a, b) {
const dx = b.x - a.x;
const dy = b.y - a.y;
const l = Math.hypot(dx, dy);
const x = (l * l - b.r * b.r + a.r * a.r) / (2 * l);
const y = Math.sqrt(a.r * a.r - x * x);
const kx = dx / l;
const ky = dy / l;
return [
[a.x + kx * x - ky * y, a.y + ky * x + kx * y],
[a.x + kx * x + ky * y, a.y + ky * x - kx * y]
];
}
Insert cell
mutable circles = [
{x: Math.min(640, width) / 3, y: 260, r: 50},
{x: Math.min(640, width) * 2 / 3, y: 220, r: 120}
]
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more