UNPKG

onibrowser

Version:
206 lines (187 loc) 5.42 kB
<!DOCTYPE html> <meta charset=utf-8> <meta name=viewport content=width=device-width,initial-scale=1,user-scalable=no> <title> BrowserGap &mdash; Point field demo </title> <style> :root, body { height: 200vh; width: 100%; margin: 0; } canvas { position: fixed; top: 0; width: 100%; height: 100vh; user-select: none; background: linear-gradient(to right top, navy, dodgerblue); } aside { position: relative; z-index: 2; padding: 2em; color: white; font-size: larger; font-family: Verdana, Helvetica, Arial, system-ui, sans; pointer-events: none; } </style> <canvas> <script> const frameReady = () => new Promise(res => window.requestAnimationFrame(res)) const sleep = ms => new Promise(res => setTimeout(res, ms)); const ALTERP = 0.618; const DTHRESHOLD = 0.618; const FRAMEDURATION = 40; const F = Math.floor(1000/FRAMEDURATION); const UPDATEP = 2; const XAVAL = 7; const YAVAL = 3; const SIZE = [3,3]; const N = 71; const NN = 1; const MAXNN = Math.ceil(Math.sqrt(N)); const MAXD = Math.min(window.innerWidth**2, window.innerHeight**2)/9; const C = document.currentScript.parentElement; const S = C.getContext('2d'); const width = window.innerWidth; const height = window.innerHeight; let frameCount = 0; let nnds = +1; let nn = Math.round(Math.sqrt(MAXNN * NN)); let allPairsCache; C.width = width; C.height = height; S.fillStyle = 'white'; S.strokeStyle = 'white'; C.addEventListener('click', () => { nn += nnds; if ( nn > MAXNN ) { nnds *= -1; } if ( nn < NN ) { nnds *= -1; } }); drawLoop(); async function drawLoop() { const Points = createPoints(N); while(true) { await sleep(FRAMEDURATION); await frameReady(); S.clearRect(0,0,width,height); update(Points); draw(Points); drawTrianglesOnClosest(Points, frameCount); frameCount++; } } function update(P) { P.forEach(updateA); } function draw(P) { P.forEach(({x,y}) => { //S.fillRect(x,y,...SIZE) S.beginPath(); S.arc(x,y,SIZE[0],0,Math.PI*2); S.closePath(); S.fill(); }); } function drawTrianglesOnClosest(Points, frameNo) { const found = new Map(); if ( ! allPairsCache ) { allPairsCache = allPairs(Points) //.map(distance); } allPairsCache .map(frameNo % UPDATEP == 0 ? ranDistance: X => X) //.map(ranDistance) .sort(([{}, {}, d1], [{}, {}, d2]) => d1 - d2) .filter(([P1,P2,d]) => { const ok = (!found.has(P1) || found.get(P1) <= nn) && (!found.has(P2) || found.get(P2) <= nn); if ( ok ) { found.set(P1, (found.get(P1) || 0)+1); found.set(P2, (found.get(P2) || 0)+1); } return ok && d < MAXD; }) .forEach(([P1,P2]) => { S.beginPath(); S.moveTo(P1.x, P1.y); S.lineTo(P2.x, P2.y); S.stroke(); S.closePath(); }); /** .reduce((T, p, i, P) => { const last = T[T.length-1]; if ( last.length == 3 ) { T.push([p]); } else { last.push(p); } }, []); **/ } function updateA(A) { const randomX = (Math.random() * XAVAL) - XAVAL/2; const randomY = (Math.random() * YAVAL) - YAVAL/2; A.x += randomX; A.y += randomY; return A; } function createPoints(n) { const P = []; for( let i = 0; i < n; i++ ) { const Point = { x: random(width), y: random(height), V: { x: random(width/20), y: random(height/20) }, A: updateA({x:XAVAL,y:YAVAL}) }; P.unshift(Point); } return P; } function random(max) { return Math.random() * max; } function allPairs(A) { const P = []; A.forEach((a,i) => { for( let j = i + 1; j < A.length; j++ ) { const [{x:x1,y:y1},{x:X1,y:Y1}] = [a,A[j]]; P.push([a,A[j],0]); } }); return P; } function distance(P) { const [{x:x1,y:y1},{x:X1,y:Y1}] = P; const d = ((X1-x1)**2+(Y1-y1)**2); P[2] = d; return P; } function ranDistance(P) { const [{x:x1,y:y1},{x:X1,y:Y1}] = P; const d = ((X1-x1)**2+(Y1-y1)**2); if ( Math.random() < ALTERP ) { if ( Math.abs(P[2] - d)/d > DTHRESHOLD ) { P[2] = d; } } return P; } </script> </canvas> <aside> <h1>Point field test</h1> <p>Could also add colors to each node / point, and sum colors with neighbour to determine line color.</p> <p>Or even make a linear gradient with direction equal to line direction to move between node colors.</p> </aside>