Published
Edited
Apr 19, 2020
1 fork
1 star
Insert cell
Insert cell
Insert cell
canvas = {
const n = 80;
const height = Math.ceil(width * screen.height / screen.width);
const margin = 100
let w = width - margin * 2
let x0 = margin
let h = height - margin * 2
let y0 = margin
const context = DOM.context2d(width, height);
const particles = Array.from({length: n}, (d,i) => {
let angle = Math.PI * 2 * i / n
let speed = 1
let dx = 1//Math.random() < 0.5 ? -1 : 1;
let dy = 1//Math.random() < 0.5 ? -1 : 1;
let ivx = 1 + Math.random()
let ivy = 1 + Math.random()
return {
x: margin + Math.random() * w,
y: margin + Math.random() * h,
dx: dx,
dy: dy,
ivx: ivx,
ivy: ivy,
vx: (dx * ivx * speed) * Math.cos(angle),
vy: (dy * ivy * speed) * Math.sin(angle),

r: 20 * Math.random() + 10,
speed: speed,
angle: angle
}
});
const quadtree = d3.quadtree()
.x(d => d.x)
.y(d => d.y)
.addAll(particles);
context.canvas.style.background = "#fff";

context.strokeStyle = "red";
while (true) {
// context.fillStyle = "rgba(255,255,255,1)"
context.fillStyle = "rgba(255,255,255,0.07)"
context.fillRect(0,0, width, height)
context.fillStyle = "blue";
context.font = '14px san-serif';
context.strokeStyle = "rgba(25, 205, 58, 0.7)"
context.lineWidth = 2
let p;
for(var i = 0; i < particles.length; i++) {
p = particles[i]

// Constrain to surface
if(p.x > x0 + w) {
if( Math.cos(p.angle) >= 0) {
p.dx = -1
} else {
p.dx = 1
}
}
if(p.y > y0 + h) {
if( Math.sin(p.angle) >= 0) {
p.dy = -1
} else {
p.dy = 1
}
}
if(p.x < x0) {
if( Math.cos(p.angle) >= 0) {
p.dx = 1
} else {
p.dx = -1
}
}
if(p.y < y0) {
if( Math.sin(p.angle) >= 0) {
p.dy = 1
} else {
p.dy = -1
}
}

let nx1 = p.x - p.r,
nx2 = p.x + p.r,
ny1 = p.y - p.r,
ny2 = p.y + p.r;
let touching = false;
// visit each squares in the quadtree
// x1 y1 x2 y2 constitues the coordinates of the square
// we want to check if each square is a leaf node (has data prop)
quadtree.visit((visited, x1, y1, x2, y2) => {
if (visited.data && (visited.data !== p)) {
let x = p.x - visited.data.x,
y = p.y - visited.data.y,
l = Math.sqrt(x * x + y * y),
r = p.r + visited.data.r;
if (l < r) { // if quadtree leaf and input node collides
// Change direction while touching another Element
touching = true;
// get direction from other point to me
// which we have here as x and y
// add a fraction of direction to p.x and p.y
p.x += .05 * x
p.y += .05 * y
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
if(touching) {
// Change direction while touching another Element
p.angle += Math.PI * 0.01
// p.speed += 0.015
}
// p.speed -= 0.005
// let maxspeed = 5
// if(p.speed > maxspeed) p.speed = maxspeed
// if(p.speed < -maxspeed) p.speed = -maxspeed

p.vx = p.dx * p.ivx * p.speed * Math.cos(p.angle)
p.vy = p.dy * p.ivy * p.speed * Math.sin(p.angle)
// Move in a straight line
p.x += p.vx
p.y += p.vy
context.beginPath()
context.arc(p.x, p.y, p.r, 0, Math.PI*2)
context.stroke()
// context.fillText("p.x: " + p.x, p.x, p.y)
// context.fillText("p.y: " + p.y, p.x, p.y + 12)
// context.fillText("x0: " + (x0 + w), p.x - 70, p.y)
// context.fillText("y0: " + (y0 + h), p.x - 70, p.y + 12)
// context.fillText("cos: " + Math.floor(10 * Math.cos(p.angle)), p.x + 50, p.y)
// context.fillText("sin: " + Math.floor(10 * Math.sin(p.angle)), p.x + 50, p.y + 12)
// context.fillText("p.x: " + p.x, p.x + 30, p.y)
// context.fillText("p.y: " + p.y, p.x + 30, p.y + 12)
}
yield context.canvas;
}
}
Insert cell
Insert cell
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