Public
Edited
Jan 9, 2023
2 forks
4 stars
Insert cell
Insert cell
Insert cell
Insert cell
WIDTH = Math.min(600, width)
Insert cell
HEIGHT = WIDTH
Insert cell
DURATION_MS = 6000
Insert cell
UNIT = WIDTH/6
Insert cell
A = [UNIT * 2, UNIT * 5]
Insert cell
B = [UNIT * 3, UNIT * 5.5]
Insert cell
α = -1
Insert cell
extent = 1000
Insert cell
Insert cell
d = [A[0] - B[0], A[1] - B[1]]
Insert cell
δ = Math.atan2(-d[0], d[1])
Insert cell
Insert cell
t = {
while (true) {
yield Math.sin(Date.now() / DURATION_MS * Math.PI);
}
}
Insert cell
ρ = Math.tan(t*1.3)
Insert cell
ρ_min = Math.tan(-1.3)
Insert cell
ρ_max = Math.tan(1.3)
Insert cell
l_min = (d[0]* ρ_min - d[1]) / α
Insert cell
l_mid = -d[1] / α
Insert cell
l_max = (d[0]* ρ_max - d[1]) / α
Insert cell
// Draw a segment of a parabola from (x0,y0) ending at (x2,y2),
// where the parabola we’re drawing a segment of is the unique
// parabola with a vertical axis that passes through all three
// points (x0,y0), (x1,y1) and (x2,y2).

function parabolaThrough(ct, x0,y0, x1,y1, x2,y2) {
// Lagrange interpolation & differentiate, to get the gradients of the tangents at (x0,y0) and (x2,y2)
const g0 = y0/(x0 - x1) + y0/(x0 - x2) + ((x0 - x2)*y1)/((-x0 + x1)*(x1 - x2)) + ((x0 - x1)*y2)/((-x0 + x2)*(x2 - x1));
const g2 = ((x2 - x1)*y0)/((x0 - x1)*(x0 - x2)) + ((x2 - x0)*y1)/((-x0 + x1)*(x1 - x2)) + y2/(x2 - x0) + y2/(x2 - x1);

// Tangent lines are:
// y - y0 = g0 (x - x0)
// y - y2 = g2 (x - x2)
// Subtracting:
// y2 - y0 = g0 (x - x0) - g2 (x - x2)
// => y2 - y0 + g0 x0 - g2 x2 = (g0 - g2) x
const x = (y2 - y0 + g0*x0 - g2*x2) / (g0 - g2);
const y = g0*(x - x0) + y0;

ct.moveTo(x0,y0);
ct.quadraticCurveTo(x,y, x2,y2);
}
Insert cell
{
const ct = canvas.ct;
const { sin, cos } = Math;

ct.clearRect(0, 0, WIDTH, HEIGHT);

// Find the point where the lines intersect
const l = (d[0]* ρ - d[1]) / α;
const X = [
B[0] - l,
B[1] - l*(ρ+α),
];

// Draw the parabola
ct.beginPath();
parabolaThrough(
ct,
B[0] - l_min, B[1] - l_min*(ρ_min+α),
B[0] - l_mid, B[1] - l_mid*α,
X[0], X[1], //B[0] - l_max, B[1] - l_max*(ρ_max+α)
);

ct.lineWidth = 2;
ct.strokeStyle = "#0c0";
//ct.stroke();

ct.lineTo(X[0], 0);
ct.fillStyle = "rebeccapurple";
ct.fill();

// Draw the lines
const a_distance = Math.sqrt(1 + ρ*ρ);
const b_distance = Math.sqrt(1 + (ρ+α)*(ρ+α));
ct.beginPath();
ct.moveTo(A[0] - extent / a_distance, A[1] - extent * ρ / a_distance);
ct.lineTo(A[0] + extent / a_distance, A[1] + extent * ρ / a_distance);

ct.moveTo(B[0] - extent / a_distance, B[1] - extent * (ρ+α) / a_distance);
ct.lineTo(B[0] + extent / a_distance, B[1] + extent * (ρ+α) / a_distance);

ct.lineWidth = 1;
ct.strokeStyle = "#999";
ct.stroke();

// Draw the intersection point
ct.beginPath();
ct.arc(X[0], X[1], 2, 0, 7);
ct.fillStyle = "#900";
ct.fill();
// Draw the chord
ct.beginPath();
ct.moveTo(A[0], A[1]);
ct.lineTo(B[0], B[1]);

ct.lineWidth = 1;
ct.strokeStyle = "blue";
ct.stroke();

// Endpoints of chord
ct.beginPath();
ct.arc(A[0], A[1], 5, 0, 7);
ct.arc(B[0], B[1], 5, 0, 7);
ct.fillStyle = "#333";
ct.fill();

return this || html`(<i>the drawing function</i>)`;
}
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