| 1 | (function(){ |
| 2 | window.HPX = window.HPX || {}; |
| 3 | window.HPX['neural-net'] = function(el){ |
| 4 | const U = window.HPX._u; |
| 5 | const k = U.canvas(el), ctx = k.ctx; |
| 6 | const ac = U.accent(el,'#7c5cff'), ac2 = U.accent2(el,'#22d3ee'); |
| 7 | const layers = [4,6,6,3]; |
| 8 | let nodes = [], edges = [], pulses = []; |
| 9 | const layout = () => { |
| 10 | nodes = []; |
| 11 | const pad = 40; |
| 12 | const cw = k.w - pad*2, ch = k.h - pad*2; |
| 13 | for (let L=0; L<layers.length; L++){ |
| 14 | const x = pad + (cw * L / (layers.length-1)); |
| 15 | const n = layers[L]; |
| 16 | for (let i=0;i<n;i++){ |
| 17 | const y = pad + (ch * (i+0.5) / n); |
| 18 | nodes.push({x,y,L,i}); |
| 19 | } |
| 20 | } |
| 21 | edges = []; |
| 22 | for (let L=0; L<layers.length-1; L++){ |
| 23 | const a = nodes.filter(n=>n.L===L), b = nodes.filter(n=>n.L===L+1); |
| 24 | for (const x of a) for (const y of b) edges.push([nodes.indexOf(x),nodes.indexOf(y)]); |
| 25 | } |
| 26 | }; |
| 27 | layout(); |
| 28 | let lw=k.w, lh=k.h, last=0; |
| 29 | const stop = U.loop((t) => { |
| 30 | if (k.w!==lw||k.h!==lh){ layout(); lw=k.w; lh=k.h; } |
| 31 | ctx.clearRect(0,0,k.w,k.h); |
| 32 | ctx.strokeStyle = 'rgba(160,160,200,0.22)'; ctx.lineWidth=1; |
| 33 | for (const [i,j] of edges){ |
| 34 | const a=nodes[i], b=nodes[j]; |
| 35 | ctx.beginPath(); ctx.moveTo(a.x,a.y); ctx.lineTo(b.x,b.y); ctx.stroke(); |
| 36 | } |
| 37 | if (t - last > 0.25){ |
| 38 | last = t; |
| 39 | const starts = nodes.filter(n=>n.L===0); |
| 40 | const s = starts[(Math.random()*starts.length)|0]; |
| 41 | pulses.push({node:s, L:0, t:0}); |
| 42 | } |
| 43 | pulses = pulses.filter(p => p.L < layers.length-1); |
| 44 | for (const p of pulses){ |
| 45 | p.t += 0.03; |
| 46 | if (p.t >= 1){ |
| 47 | const next = nodes.filter(n=>n.L===p.L+1); |
| 48 | p.node2 = next[(Math.random()*next.length)|0]; |
| 49 | if (!p._started){ p._started = true; } |
| 50 | } |
| 51 | } |
| 52 | // animate progression |
| 53 | for (const p of pulses){ |
| 54 | if (!p.target){ |
| 55 | const next = nodes.filter(n=>n.L===p.L+1); |
| 56 | p.target = next[(Math.random()*next.length)|0]; |
| 57 | } |
| 58 | p.t += 0.04; |
| 59 | const a = p.node, b = p.target; |
| 60 | const x = a.x + (b.x-a.x)*Math.min(1,p.t); |
| 61 | const y = a.y + (b.y-a.y)*Math.min(1,p.t); |
| 62 | ctx.fillStyle = ac2; |
| 63 | ctx.beginPath(); ctx.arc(x,y,4,0,Math.PI*2); ctx.fill(); |
| 64 | if (p.t >= 1){ p.node = b; p.target=null; p.L++; p.t=0; } |
| 65 | } |
| 66 | for (const n of nodes){ |
| 67 | ctx.fillStyle = ac; |
| 68 | ctx.beginPath(); ctx.arc(n.x,n.y,6,0,Math.PI*2); ctx.fill(); |
| 69 | ctx.strokeStyle = ac2; ctx.lineWidth=1.5; |
| 70 | ctx.beginPath(); ctx.arc(n.x,n.y,8,0,Math.PI*2); ctx.stroke(); |
| 71 | } |
| 72 | }); |
| 73 | return { stop(){ stop(); k.destroy(); } }; |
| 74 | }; |
| 75 | })(); |
| 76 |