Download graph.js from audio-cpp/Audio-Model-Architecture-Atlas: direct link, hf CLI and curl.
- Browser
- Download file 4.69 kB
-
https://huggingface.co/spaces/audio-cpp/Audio-Model-Architecture-Atlas/resolve/main/graph.js
- Command line
-
hf download hf://spaces/audio-cpp/Audio-Model-Architecture-Atlas/graph.js
-
curl -L -o graph.js https://huggingface.co/spaces/audio-cpp/Audio-Model-Architecture-Atlas/resolve/main/graph.js
4.69 kB
| /* SVG rendering for authored DAGs. Dagre handles branching and shared nodes. */ | |
| window.AtlasGraph = (() => { | |
| let serial = 0; | |
| const NS = 'http://www.w3.org/2000/svg'; | |
| const colors = {ar:['#315fc1','#e9effc'],flow:['#a63975','#fae9f3'],encoder:['#007e70','#e3f4ef'],codec:['#956609','#faf0d6'],head:['#557b31','#edf4e4'],dsp:['#58777d','#eef3f4'],input:['#75888b','#fff'],output:['#75888b','#fff'],model:['#263e43','#f3f7f5']}; | |
| const roles = {ar:'AR BACKBONE',flow:'GENERATION',encoder:'ENCODER',codec:'CODEC / VOCODER',head:'PREDICTION',dsp:'FRONTEND / DSP',input:'INPUT',output:'OUTPUT',model:'MODEL'}; | |
| function element(name,attrs={},text){const n=document.createElementNS(NS,name);for(const [k,v] of Object.entries(attrs))n.setAttribute(k,v);if(text!==undefined)n.textContent=text;return n;} | |
| const measure=document.createElement('canvas').getContext('2d'); | |
| function wrap(text,width,font){measure.font=font;const out=[];let line='';for(const word of text.split(/\s+/)){const next=line?line+' '+word:word;if(line&&measure.measureText(next).width>width){out.push(line);line=word;}else line=next;}if(line)out.push(line);return out;} | |
| function render(container,nodes,edges,{onNode,active,compact=false,residuals=[]}={}){ | |
| const graph=new dagre.graphlib.Graph({multigraph:true}); | |
| graph.setGraph({rankdir:'TB',ranksep:compact?28:66,nodesep:compact?25:34,marginx:28,marginy:25});graph.setDefaultEdgeLabel(()=>({})); | |
| for(const node of nodes){const io=['input','output'].includes(node.role);const width=compact?205:230; | |
| const lines=wrap(node.label,width-28,`700 ${compact?12:14}px Noto`); | |
| graph.setNode(node.id,{...node,width,height:(io?27:40)+lines.length*(compact?18:20),lines}); | |
| } | |
| edges.forEach((edge,i)=>graph.setEdge(edge.from,edge.to,{...edge,width:edge.label?135:0,height:edge.label?16:0},String(i))); | |
| dagre.layout(graph); | |
| const {width,height}=graph.graph(),id='arrow-'+serial++; | |
| const svg=element('svg',{xmlns:NS,viewBox:`0 0 ${width} ${height}`,width,height,role:'group','aria-label':'Architecture diagram'}); | |
| const defs=element('defs'),marker=element('marker',{id,markerWidth:7,markerHeight:7,refX:6,refY:3.5,orient:'auto'}); | |
| marker.append(element('path',{d:'M0 0 L7 3.5 L0 7',fill:'none',stroke:'#90a6a9'}));defs.append(marker);svg.append(defs); | |
| for(const e of graph.edges()){const data=graph.edge(e),points=data.points; | |
| const path=element('path',{d:points.map((p,i)=>(i?'L':'M')+p.x+' '+p.y).join(' '),fill:'none',stroke:data.dim?'#c3cecf':'#92aaa9','stroke-width':data.highlight?2:1.3,'stroke-dasharray':data.optional?'5 4':'none','marker-end':`url(#${id})`,opacity:data.dim?.24:1}); | |
| path.append(element('title',{},data.label||'Data flow'));svg.append(path); | |
| if(data.label){const label=element('text',{x:data.x,y:data.y+4,'text-anchor':'middle',fill:'#637b7e','font-size':10,'paint-order':'stroke',stroke:'white','stroke-width':4,'stroke-linejoin':'round'},data.label);svg.append(label);} | |
| } | |
| for(const [a,b] of residuals){const start=graph.node(a),end=graph.node(b);const x=Math.min(start.x-start.width/2,end.x-end.width/2)-15; | |
| svg.append(element('path',{d:`M${start.x-start.width/2} ${start.y-start.height/2} H${x} V${end.y} H${end.x-end.width/2}`,stroke:'#789698',fill:'none','stroke-dasharray':'3 2'})); | |
| } | |
| for(const nodeId of graph.nodes()){const n=graph.node(nodeId),[ink,fill]=colors[n.role]||colors.dsp; | |
| const g=element('g',{class:'node'+(n.dim?' dim':''),'data-node':n.id,transform:`translate(${n.x-n.width/2},${n.y-n.height/2})`}); | |
| if(n.action){g.setAttribute('tabindex','0');g.setAttribute('role','button');g.setAttribute('aria-label',n.actionLabel||n.label);if(n.pressed!==undefined)g.setAttribute('aria-pressed',n.pressed);} | |
| const io=['input','output'].includes(n.role); | |
| g.append(element('rect',{width:n.width,height:n.height,rx:io?2:5,fill,stroke:ink,'stroke-opacity':io?.3:.5,'stroke-width':n.id===active?2:1,'stroke-dasharray':n.optional?'5 4':'none'})); | |
| g.append(element('text',{x:n.width/2,y:16,'text-anchor':'middle','font-size':8,fill:ink},n.sublabel??roles[n.role]??'')); | |
| n.lines.forEach((l,i)=>g.append(element('text',{x:n.width/2,y:io?33+i*18:38+i*(compact?18:20),'text-anchor':'middle','font-weight':io?400:700,'font-size':compact?12:14,fill:ink},l))); | |
| g.append(element('title',{},n.description||n.label)); | |
| if(n.action){const invoke=()=>onNode(n);g.addEventListener('click',invoke);g.addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();invoke();}});} | |
| svg.append(g); | |
| } | |
| container.replaceChildren(svg);return {width,height,svg}; | |
| } | |
| return {render,colors}; | |
| })(); | |