Published
Edited
Nov 19, 2018
Fork of Hexagon Spin
4 forks
3 stars
Insert cell
Insert cell
canvas = {
const context = DOM.context2d(width, height);
let sprites = [];
for (let i = 4; i <= 33; ++i) {
sprites.push(sprite(0, 0, i));
}
while (true) {
context.save();
context.fillStyle = "#222";
context.fillRect(0, 0, width, height);
context.translate(width / 2, height / 2);
sprites.forEach((sprite) => {
updateSprite(sprite, 16.6667);
drawSprite(context, sprite);
});
context.restore();
yield context.canvas;
}
}
Insert cell
Insert cell
function drawSprite(context, sprite) {
var p = sprite.controlPoints;
var s = sprite.connectionPoints;
var n = sprite.arms * 2;

computePoints(sprite);

context.save();

context.fillStyle = sprite.fillStyle;
context.strokeStyle = sprite.strokeStyle;

context.translate(sprite.position[0], sprite.position[1]);

context.rotate(sprite.angle);
context.beginPath();

context.moveTo(s[2 * n - 1][0], s[2 * n - 1][1]);

for (let i = 0; i < n; i++) {
const orig = s[2*i];
const dest = s[(2*i+1) % (2 * n)]
context.quadraticCurveTo(p[i][0], p[i][1], orig[0], orig[1]);
context.lineTo(dest[0], dest[1]);
}

context.closePath();
context.stroke();
context.fill();

context.restore();
}
Insert cell
function sprite(x, y, n) {
return {
position: [x, y],
angle: Math.random() * Math.PI / n,
velocity: [0, 0],
angularVelocity: Math.random() * 0.001 * Math.PI / n,
innerRadius: Math.random() * 100,
outerRadius: 100 + Math.random() * 100,
fillStyle: '#0000',
strokeStyle: d3.interpolateRainbow(Math.random()),
arms: n,
controlPoints: [],
connectionPoints: [],
straightProportion: 0,
straightOffset: 0.5,
pulseRate: 0.5 - Math.random(),
outerPulse: 0.5 - Math.random()
}
}
Insert cell
function updateSprite(sprite, dt) {
sprite.position[0] += sprite.velocity[0] * dt;
sprite.position[1] += sprite.velocity[1] * dt;
sprite.angle += sprite.angularVelocity * dt;
if (sprite.position[0] + sprite.outerRadius / 2 > width / 2 ||
sprite.position[0] - sprite.outerRadius / 2 < -width / 2) {
sprite.velocity[0] *= -1;
}
if (sprite.position[1] + sprite.outerRadius / 2 > height / 2 ||
sprite.position[1] - + sprite.outerRadius / 2 < -height / 2) {
sprite.velocity[1] *= -1;
}
if (sprite.angle > Math.PI * 2) {
sprite.angluarVelocity *= -1;
}

if (sprite.innerRadius > height / 2 || sprite.innerRadius < 10) {
sprite.pulseRate *= -1;
}
if (sprite.outerRadius < sprite.innerRadius || sprite.outerRadius > height / 2) {
sprite.outerPulse *= -1;
}

sprite.innerRadius += sprite.pulseRate;
sprite.outerRadius += sprite.outerPulse;
}
Insert cell
function computePoints(sprite) {
var r1 = sprite.outerRadius;
var r2 = sprite.innerRadius;
var n = sprite.arms;
var p = sprite.controlPoints;
var s = sprite.connectionPoints;
var l = sprite.straightProportion;
var l1 = sprite.straightOffset;
var l3 = 1 - (l + l1);

// control points define a simple n/2-pointed star
for (var i = 0; i < 2 * n; i++) {
var theta = i / n * Math.PI;
var rho = ((i % 2) == 0) ? r1 : r2;
p[i] = [rho * Math.cos(theta), rho * Math.sin(theta)];
}
// there are 2 connection points for each control point
for (var i = 0; i < 2 * n; i++) {
var p0 = p[i];
var p1 = p[(i+1) % (2 * n)];

if ((i % 2) == 0) {
var even = [p[i][0] + l1 * (p1[0] - p0[0]), p[i][1] + l1 * (p1[1] - p0[1])];
var odd = [p[i][0] + (1 - l3) * (p1[0] - p0[0]), p[i][1] + (1 - l3) * (p1[1] - p0[1])];
}
else {
var even = [p[i][0] + l3 * (p1[0] - p0[0]), p[i][1] + l3 * (p1[1] - p0[1])];
var odd = [p[i][0] + (1 - l1) * (p1[0] - p0[0]), p[i][1] + (1 - l1) * (p1[1] - p0[1])];
}
s[2 * i] = even;
s[2 * i + 1] = odd;
}
}
Insert cell
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