| 1 | (function(){ |
| 2 | window.HPX = window.HPX || {}; |
| 3 | window.HPX['knowledge-graph'] = function(el){ |
| 4 | const U = window.HPX._u; |
| 5 | const k = U.canvas(el), ctx = k.ctx; |
| 6 | const pal = U.palette(el); |
| 7 | const tx = U.text(el, '#e7e7ef'); |
| 8 | const labels = ['AI','ML','LLM','Graph','Node','Edge','Claude','GPT','RAG','Vector', |
| 9 | 'Embed','Neural','Agent','Tool','Memory','Logic','Data','Train','Infer','Token', |
| 10 | 'Prompt','Chain','Plan','Skill','Cloud','Edge','GPU','Code','Task','Flow']; |
| 11 | const N = 28; |
| 12 | const nodes = Array.from({length:N}, (_,i) => ({ |
| 13 | x: U.rand(40, 300), y: U.rand(40, 200), |
| 14 | vx: 0, vy: 0, label: labels[i%labels.length], |
| 15 | c: pal[i%pal.length] |
| 16 | })); |
| 17 | const edges = []; |
| 18 | const made = new Set(); |
| 19 | while (edges.length < 50){ |
| 20 | const a = (Math.random()*N)|0, b = (Math.random()*N)|0; |
| 21 | if (a===b) continue; |
| 22 | const key = a<b ? a+'-'+b : b+'-'+a; |
| 23 | if (made.has(key)) continue; |
| 24 | made.add(key); edges.push([a,b]); |
| 25 | } |
| 26 | const stop = U.loop(() => { |
| 27 | // physics |
| 28 | for (let i=0;i<N;i++){ |
| 29 | for (let j=i+1;j<N;j++){ |
| 30 | const a=nodes[i], b=nodes[j]; |
| 31 | const dx=b.x-a.x, dy=b.y-a.y; |
| 32 | let d2=dx*dx+dy*dy; if (d2<1) d2=1; |
| 33 | const d=Math.sqrt(d2); |
| 34 | const f=1600/d2; |
| 35 | const fx=(dx/d)*f, fy=(dy/d)*f; |
| 36 | a.vx-=fx; a.vy-=fy; b.vx+=fx; b.vy+=fy; |
| 37 | } |
| 38 | } |
| 39 | for (const [i,j] of edges){ |
| 40 | const a=nodes[i], b=nodes[j]; |
| 41 | const dx=b.x-a.x, dy=b.y-a.y, d=Math.hypot(dx,dy)||1; |
| 42 | const f=(d-90)*0.008; |
| 43 | const fx=(dx/d)*f, fy=(dy/d)*f; |
| 44 | a.vx+=fx; a.vy+=fy; b.vx-=fx; b.vy-=fy; |
| 45 | } |
| 46 | const cx=k.w/2, cy=k.h/2; |
| 47 | for (const n of nodes){ |
| 48 | n.vx += (cx-n.x)*0.002; |
| 49 | n.vy += (cy-n.y)*0.002; |
| 50 | n.vx *= 0.85; n.vy *= 0.85; |
| 51 | n.x += n.vx; n.y += n.vy; |
| 52 | } |
| 53 | ctx.clearRect(0,0,k.w,k.h); |
| 54 | ctx.strokeStyle = 'rgba(180,180,220,0.25)'; ctx.lineWidth=1; |
| 55 | for (const [i,j] of edges){ |
| 56 | const a=nodes[i], b=nodes[j]; |
| 57 | ctx.beginPath(); ctx.moveTo(a.x,a.y); ctx.lineTo(b.x,b.y); ctx.stroke(); |
| 58 | } |
| 59 | ctx.font='11px system-ui,sans-serif'; ctx.textAlign='center'; ctx.textBaseline='middle'; |
| 60 | for (const n of nodes){ |
| 61 | ctx.fillStyle = n.c; |
| 62 | ctx.beginPath(); ctx.arc(n.x,n.y,7,0,Math.PI*2); ctx.fill(); |
| 63 | ctx.fillStyle = tx; |
| 64 | ctx.fillText(n.label, n.x, n.y-14); |
| 65 | } |
| 66 | }); |
| 67 | return { stop(){ stop(); k.destroy(); } }; |
| 68 | }; |
| 69 | })(); |
| 70 |