// Stable scales are measured across all captured tokens for each comparable bank. function normalization(){ const cacheKey=[trace.version,mode,selected,head,$('groups').value,expanded].join(':'); if(normCache.has(cacheKey))return normCache.get(cacheKey); const result={banks:new Map(),dense:1e-8}; const add=g=>{for(const n of g.nodes)result.banks.set(F.bank(n),Math.max(result.banks.get(F.bank(n))||1e-8,Math.abs(n.v)));for(const e of g.edges)if(e.kind==='dense')result.dense=Math.max(result.dense,Math.abs(e.v));}; if(trace.version===4&&['network','projection'].includes(mode)){ for(let t=0;t[F.key(n),i]));nodeByPosition=new Map(nodes.map(n=>[n.p,n]));adjacency=new Map(nodes.map(n=>[F.key(n),[]])); const work=edges.map((e,i)=>{ const a=nodeByPosition.get(e.a),b=nodeByPosition.get(e.b),ia=a?nodeIndex.get(F.key(a)):-2,ib=b?nodeIndex.get(F.key(b)):-2; if(a)adjacency.get(F.key(a)).push(e);if(b&&b!==a)adjacency.get(F.key(b)).push(e); const curve=F.curve(e),arc=F.arc(e,curve),steps=e.kind==='frame'?1:(geometryTier?(e.arc?16:10):(e.arc?8:4)); const col=F.palette(e,norms.dense),key=(e.source||a&&F.key(a)||e.a.join(','))+'>'+(e.target||b&&F.key(b)||e.b.join(','))+':'+e.kind; size+=steps*2*26;return {e,curve,arc,steps,col,key,ia,ib,i}; }); const line=new Float32Array(size);let offset=0;lastNodeColors=new Map();lastEdgeColors=new Map(); function vertex(arr,w,t,col,old){ const values=[...w.e.a,...w.e.b,...col,t,7/w.arc[3],w.curve[0],w.curve[1],w.ia,w.ib,w.curve[2],w.curve[3],...old,...w.arc.slice(0,3),F.blockEdge(w.e)?1:0]; if(arr===line){line.set(values,offset);offset+=26;}else arr.push(...values); } for(const w of work){ const old=previousEdgeColors.get(w.key)||w.col;lastEdgeColors.set(w.key,w.col); for(let j=0;je.kind==='dense').length;pickDirty=true; canvas.dataset.style='filament-v2';canvas.dataset.lineVertices=String(counts.line); canvas.dataset.blockAlpha=JSON.stringify({history:.0012,blockOperations:.0025,local:.055}); } function setSelection(n){ const next=n?nodeIndex.get(F.key(n)):-1; if(next!==selectionId&&next>=0){selectionId=next;selectionFade=0;} selectionGoal=n?1:0; canvas.dataset.selection=n?F.key(n):'';canvas.dataset.pinned=focused?'true':'false'; } function inspectNode(n){ if(!n)return; const connected=adjacency.get(F.key(n))||[]; $('inspectTitle').textContent=n.title; $('inspect').textContent=(n.structural?'Structural operation junction.':'Captured token value: '+n.v.toPrecision(7))+'\n'+connected.length+' saved incident links. '+(focused?'Pinned · Esc releases selection.':'Hover · click to pin.')+'\n'+connected.slice().sort((a,b)=>Math.abs(b.v)-Math.abs(a.v)).slice(0,4).map(e=>e.info).join('\n'); } function focusCamera(n,distance=45){if(!n)return;cameraGoal={target:[...n.p],distance};} function updateScreenNodes(){ if(!matrix)return;screenNodes=[]; for(const n of nodes){const p=project(n.p);if(p[2]>0&&p[0]>=0&&p[0]<=innerWidth&&p[1]>=0&&p[1]<=innerHeight)screenNodes.push({n,p});} pickDirty=false;const probe=screenNodes.filter(q=>q.n.id?.includes('/selected y')).reduce((a,b)=>!a||Math.hypot(b.p[0]-innerWidth/2,b.p[1]-innerHeight/2)