Published
Edited
Nov 17, 2020
8 stars
Insert cell
md`# Belle curve`
Insert cell
points =[{x:100, y:100}, {x:200, y:120},{x:400, y:121}, {x:500, y:200}, {x:401, y:401}, {x:0, y: 450}]
Insert cell
chart = {
const svg = d3.select(DOM.svg(width, 500));
svg.append("g").selectAll("circle")
.data(points)
.join("circle")
.attr("cx", d => d.x)
.attr("cy", d=> d.y)
.attr("r", 5);
//compute path
let computed = curvedPath(points, Math.PI/2, false)
svg.append("g")
.selectAll("circle")
.data(computed.circles)
.join("circle")
.attr("cx", d=>d.x)
.attr("cy", d=> d.y)
.attr("r", d=>d.r)
.attr('stroke', 'silver')
.attr('fill', 'none')
svg.append("path")
.attr('d', computed.path)
.attr('fill', 'none')
.attr('stroke', 'red')
.attr("stroke-width",2)
return svg.node();
}
Insert cell
curvedPath = function(points, startAngle){
// initialise the path with the first two points
let circles = [];
let path = ["M", points[0].x, points[0].y] // move to first point
let c1 = centerFromTwoPointsAndAngle(points[0].x, points[0].y, points[1].x, points[1].y, startAngle, true);
path = path.concat(["A", c1.r, c1.r, 0, 0, 1, points[1].x, points[1].y,])
let prevCircle = c1
circles.push(c1);
//now for each couple of points, find the arc
points.slice(2).forEach((d,i) => {
let p1 = points[i+1];
let p2 = d;
let circle = centerFromTwoPointsAndPrevCenter(p1.x, p1.y, p2.x, p2.y, prevCircle.x, prevCircle.y);
let direction = i%2
let largeCircle = direction == 0 ? circle.angleCW > 180 : circle.angleCCW > 180;
console.log(i+1, direction == 0 ? 'CW' : 'CCW', circle)
path = path.concat(["A", circle.r, circle.r, 0, largeCircle ? 1 : 0, direction, p2.x, p2.y]);
circles.push(circle);
prevCircle = circle;
})
return {circles: circles, path:path.join(" ")}
}
Insert cell
centerFromTwoPointsAndPrevCenter = function(x1, y1, x2, y2, xpc, ypc){
// slope and intercept for first line y = ax + c
// the first line passes through the previous center and p1 (x1,y1)
let a = (y1 - ypc) / (x1 - xpc);
let c = y1 - a * x1;
// slope and intecept for second line y = bx + d
// the second line passes through p1 (x1,y1) and p2 (x2,x2)
let b = (y2 - y1) / (x2 - x1);
let d = y1 - b * x1;
//find the perpendicular to second line passing from midpoint y = b1x + d1
// the line is perpendicular to the second one and passing through middle point between P1 and P2
let mx = (x2+x1)/2
let my = (y2+y1)/2
let b1 = -1/b;
let d1 = my - b1 * mx;
// find intercept
// https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection#Given_two_line_equations
let xc = (d1-c)/(a-b1);
let yc = a * xc + c;
let r = Math.sqrt(Math.pow(xc-x1,2)+Math.pow(yc-y1,2))
let alpha = Math.atan2(y1 - yc, x1 - xc)
let beta = Math.atan2(y2 - yc, x2 - xc)
alpha = alpha < 0 ? (2 * Math.PI) + alpha : alpha;
beta = beta < 0 ? (2 * Math.PI) + beta : beta;
let angleCW = alpha > beta ? alpha - beta : Math.PI*2 - (beta - alpha)
let angleCCW = Math.PI*2 - angleCW
return {
x:xc,
y:yc,
r:r,
angleStart: alpha * 180 / Math.PI,
angleEnd: beta * 180 / Math.PI,
angleCW:angleCW * 180 / Math.PI,
angleCCW:angleCCW * 180 / Math.PI,
middle:{x:mx, y:my},
line1:{x1:0, y1:c, x2:width, y2:a*width+c},
line2:{x1:0, y1:d, x2:width, y2:b*width+d},
line3:{x1:0, y1:d1, x2:width, y2:b1*width+d1},
}
}
Insert cell
function centerFromTwoPointsAndAngle(x1, y1, x2, y2, angle, isClockwise){
// from https://stackoverflow.com/questions/36211171/finding-center-of-a-circle-given-two-points-and-radius
const q = Math.sqrt(Math.pow((x2-x1),2) + Math.pow((y2-y1),2));

const r = q/(2 * Math.sin(angle/2));
const y3 = (y1+y2)/2;
const x3 = (x1+x2)/2;

const basex = Math.sqrt(Math.pow(r,2)-Math.pow((q/2),2))*(y1-y2)/q; //calculate once
const basey = Math.sqrt(Math.pow(r,2)-Math.pow((q/2),2))*(x2-x1)/q; //calculate once

const centerx1 = x3 + basex; //center x of circle 1
const centery1 = y3 + basey; //center y of circle 1
const centerx2 = x3 - basex; //center x of circle 2
const centery2 = y3 - basey; //center y of circle 2

if(isClockwise){
return {x: centerx1, y: centery1, r: r}
} else {
return {x: centerx2, y: centery2, r: r}
}
}
Insert cell
d3 = require("d3@6")
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