From fa3067a16f8ba06ee756eb2b2293be4cb728681d Mon Sep 17 00:00:00 2001 From: brian cully Date: Fri, 26 Dec 2025 12:04:14 -0500 Subject: add bouncing particles --- site/favicon.ico | Bin 0 -> 1258 bytes site/index.html | 25 ++++++++++ site/main.css | 11 +++++ site/main.mjs | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 176 insertions(+) create mode 100644 site/favicon.ico create mode 100644 site/index.html create mode 100644 site/main.css create mode 100644 site/main.mjs (limited to 'site') diff --git a/site/favicon.ico b/site/favicon.ico new file mode 100644 index 0000000..73de524 Binary files /dev/null and b/site/favicon.ico differ diff --git a/site/index.html b/site/index.html new file mode 100644 index 0000000..012e301 --- /dev/null +++ b/site/index.html @@ -0,0 +1,25 @@ + + + + + polyring + + + + + +

polyring

+

benchmarking maximal convex polygon finding

+ + + fps: n/a +
+ + + + + + + diff --git a/site/main.css b/site/main.css new file mode 100644 index 0000000..f875232 --- /dev/null +++ b/site/main.css @@ -0,0 +1,11 @@ +html { + box-sizing: border-box; +} +*, *::before, *::after { + box-sizing: inherit; +} + +canvas { + border: 1px solid orangered; + background-color: rgb(200 200 200); +} diff --git a/site/main.mjs b/site/main.mjs new file mode 100644 index 0000000..0c81b41 --- /dev/null +++ b/site/main.mjs @@ -0,0 +1,140 @@ +const NUM_POINTS = 5; + +// thanks vihart +const TAU = 2 * Math.PI; + +// no more than 1% of world size per tick. +const MAX_SPEED = 0.01; + +// size of rendered points in proportion to canvas. +const POINT_RADIUS = 0.01; + +class Point { + x = Math.random(); + y = Math.random(); + #heading = Math.random() * TAU; + speed = Math.random() * MAX_SPEED; + color = randColor(); + + #speedX; + get speedX() { + if (this.#speedX === undefined) { + this.#speedX = this.speed * Math.cos(this.heading); + } + return this.#speedX; + } + + #speedY; + get speedY() { + if (this.#speedY === undefined) { + this.#speedY = this.speed * Math.sin(this.heading); + } + return this.#speedY; + } + + get heading() { + return this.#heading; + } + + set heading(val) { + this.#heading = val; + this.#speedX = this.#speedY = undefined; + } +} + +const points = [...Array(NUM_POINTS)].map(_ => new Point()); + +function randColor() { + const [r, g, b] = [...Array(3)].map(_ => Math.random() * 255); + return `rgb(${r} ${g} ${b})` +} + +function renderPoints(ctx, points) { + points.forEach(p => { + ctx.fillStyle = p.color; + ctx.beginPath(); + ctx.arc(p.x, p.y, POINT_RADIUS, 0, TAU); + ctx.fill(); + + ctx.lineWidth = 0.005; + ctx.beginPath(); + ctx.moveTo(p.x, p.y); + ctx.lineTo(p.x + p.speedX * 3, p.y + p.speedY * 3); + ctx.stroke(); + }); +} + +function movePoints(points) { + points.forEach(p => { + p.x += p.speedX; + p.y += p.speedY; + }); +} + +function bouncePoints(points) { + let didBounce = false; + points.forEach(p => { + const x = p.x + p.speedX; + const y = p.y + p.speedY; + + if (x < 0) { + p.heading = Math.PI - p.heading; + didBounce = true; + } else if (x >= 1) { + p.heading = Math.PI - p.heading; + didBounce = true; + } else if (y < 0) { + p.heading = -1 * p.heading; + didBounce = true; + } else if (y >= 1) { + p.heading = -1 * p.heading; + didBounce = true; + } + }); + return didBounce; +} + +async function loaded() { + const canvas = document.querySelector('canvas'); + const ctx = canvas.getContext('2d'); + console.debug('canvas:', canvas, 'ctx', ctx, 'points:', points); + ctx.scale(canvas.width, canvas.height); + + const fps = document.querySelector('#fps'); + + const goButton = document.querySelector('button'); + let paused = true; + goButton.onclick = e => { + paused = !paused; + if (paused) { + e.target.textContent = 'go'; + } else { + e.target.textContent = 'pause'; + self.requestAnimationFrame(render); + } + }; + + let lastTime = document.timeline.currentTime; + function render(t) { + if (t > lastTime) { + fps.textContent = Math.floor(1_000 / (t - lastTime)); + lastTime = t; + } + + ctx.clearRect(0, 0, canvas.width, canvas.height); + renderPoints(ctx, points); + if (bouncePoints(points)) { + //goButton.onclick({ target: goButton }); + } + movePoints(points); + + if (!paused) { + self.requestAnimationFrame(render); + } + } + + //goButton.onclick({ target: goButton }); + render(document.timeline.currentTime); +} + +document.addEventListener('DOMContentLoaded', loaded); -- cgit v1.3