1// https://observablehq.com/@bayre/deconstructed-sankey-diagram@1608 2import define1 from "./[email protected]"; 3 4function _1(md){return( 5md`# Deconstructed sankey diagram 6 7This is a journey through the source code for [d3-sankey](https://github.com/d3/d3-sankey). In what follows I've traded in require for copy-paste in a bid to better understand how the sankey constructor works, and where the opportunities lie for [its modification](/@bayre/spatially-situated-sankey). 8 9You're almost certainly better off reading the official documentation. 10 11_see also @tomshanley [1](https://observablehq.com/@tomshanley/sankey-circular-deconstructed) [2](https://observablehq.com/@tomshanley/vertical-sankey-with-elegant-links-to-entry-and-exit-nodes)_` 12)} 13 14function _shape(radio){return( 15radio({ options: ["chunky", "smooth"], value: "chunky" }) 16)} 17 18function _chart(d3,width,height,sankey,data,color,format,DOM,shape,sankeyLinkHorizontal) 19{ 20 const svg = d3.create("svg") 21 .attr("viewBox", [0, 0, width, height]); 22 23 const {nodes, links} = sankey(data); 24 25 svg.append("g") 26 .attr("stroke", "#000") 27 .selectAll("rect") 28 .data(nodes) 29 .join("rect") 30 .attr("x", d => d.x0) 31 .attr("y", d => d.y0) 32 .attr("height", d => d.y1 - d.y0) 33 .attr("width", d => d.x1 - d.x0) 34 .attr("fill", color) 35 .append("title") 36 .text(d => `${d.name}\n${format(d.value)}`); 37 38 const link = svg.append("g") 39 .attr("fill", "none") 40 .attr("stroke-opacity", 0.5) 41 .selectAll("g") 42 .data(links) 43 .join("g") 44 .style("mix-blend-mode", "multiply"); 45 46 const gradient = link.append("linearGradient") 47 .attr("id", d => (d.uid = DOM.uid("link")).id) 48 .attr("gradientUnits", "userSpaceOnUse") 49 .attr("x1", d => d.source.x1) 50 .attr("x2", d => d.target.x0); 51 52 gradient.append("stop") 53 .attr("offset", "0%") 54 .attr("stop-color", d => color(d.source)); 55 56 gradient.append("stop") 57 .attr("offset", "100%") 58 .attr("stop-color", d => color(d.target)); 59 60 link.append("path") 61 .attr("d", shape === "chunky" ? d => `M${d.source.x0},${d.y0} L${d.target.x0},${d.y1}Z` : sankeyLinkHorizontal()) 62 .attr("stroke", d => d.uid) 63 .attr("stroke-width", d => Math.max(1, d.width)); 64 65 link.append("title") 66 .text(d => `${d.source.name} â ${d.target.name}\n${format(d.value)}`); 67 68 svg.append("g") 69 .attr("font-family", "sans-serif") 70 .attr("font-size", 10) 71 .selectAll("text") 72 .data(nodes) 73 .join("text") 74 .attr("x", d => d.x0 < width / 2 ? d.x1 + 6 : d.x0 - 6) 75 .attr("y", d => (d.y1 + d.y0) / 2) 76 .attr("dy", "0.35em") 77 .attr("text-anchor", d => d.x0 < width / 2 ? "start" : "end") 78 .text(d => d.name); 79 80 return svg.node(); 81} 82 83 84function _4(md){return( 85md`---` 86)} 87 88async function _data(FileAttachment) 89{ 90 const links = await FileAttachment("energy.csv").csv({typed: true}); 91 const nodes = Array.from(new Set(links.flatMap(l => [l.source, l.target])), name => ({name, category: name.replace(/ .*/, "")})); 92 return {nodes, links, units: "TWh"}; 93} 94 95 96function _6(md){return( 97md`The [sankey constructor](https://github.com/d3/d3-sankey/blob/bb7233ea0004669878d922e55fe83084ef3cb555/src/sankey.js#L54) provides a series of default values and methods that are used to generate a function that looks something like this:` 98)} 99 100function _sankey(clone,computeNodeLinks,computeNodeValues,computeNodeDepths,computeNodeHeights,computeNodeBreadths,computeLinkBreadths){return( 101function sankey({nodes, links}) { 102 const graph = clone({nodes, links}); 103 computeNodeLinks(graph); 104 computeNodeValues(graph); 105 computeNodeDepths(graph); 106 computeNodeHeights(graph); 107 computeNodeBreadths(graph); 108 computeLinkBreadths(graph); 109 return graph; 110} 111)} 112 113function _8(md){return( 114md`In order to get this working in Observable I have pulled out the default values into cells and done away with the methods entirely.` 115)} 116 117function _x0(){return( 1181 119)} 120 121function _x1(width){return( 122width - 1 123)} 124 125function _y0(){return( 1265 127)} 128 129function _y1(height){return( 130height - 5 131)} 132 133function _dx(){return( 13415 135)} 136 137function _dy(){return( 13810 139)} 140 141function _py(){return( 142null 143)} 144 145function _id(){return( 146d => d.name 147)} 148 149function _sort(){return( 150undefined 151)} 152 153function _linkSort(){return( 154undefined 155)} 156 157function _align() 158{ 159 return function justify(node, n) { 160 return node.sourceLinks.length ? node.depth : n - 1; 161 } 162} 163 164 165function _20(md){return( 166md`With this in place, it's time to work through the six steps in our sankey function. 167 168First up, we identify the incoming (target) and outgoing (source) links for each node.` 169)} 170 171function _computeNodeLinks(id,find,linkSort){return( 172function computeNodeLinks({nodes, links}) { 173 for (const [i, node] of nodes.entries()) { 174 node.index = i; 175 node.sourceLinks = []; 176 node.targetLinks = []; 177 } 178 const nodeById = new Map(nodes.map((d, i) => [id(d, i, nodes), d])); 179 for (const [i, link] of links.entries()) { 180 link.index = i; 181 let {source, target} = link; 182 if (typeof source !== "object") source = link.source = find(nodeById, source); 183 if (typeof target !== "object") target = link.target = find(nodeById, target); 184 source.sourceLinks.push(link); 185 target.targetLinks.push(link); 186 } 187 if (linkSort != null) { 188 for (const {sourceLinks, targetLinks} of nodes) { 189 sourceLinks.sort(linkSort); 190 targetLinks.sort(linkSort); 191 } 192 } 193} 194)} 195 196function _find(){return( 197function find(nodeById, id) { 198 const node = nodeById.get(id); 199 if (!node) throw new Error("missing: " + id); 200 return node; 201} 202)} 203 204function _step1(clone,data,computeNodeLinks) 205{ 206 const graph = clone(data); 207 computeNodeLinks(graph); 208 return graph; 209} 210 211 212function _24(md){return( 213md`Then we assign a value to each node. Unless a \`fixedValue\` is provided, this is calculated as the maximum of the sums of source and target links.` 214)} 215 216function _computeNodeValues(d3,value){return( 217function computeNodeValues({nodes}) { 218 for (const node of nodes) { 219 node.value = node.fixedValue === undefined 220 ? Math.max(d3.sum(node.sourceLinks, value), d3.sum(node.targetLinks, value)) 221 : node.fixedValue; 222 } 223} 224)} 225 226function _value(){return( 227function value(d) { 228 return d.value; 229} 230)} 231 232function _step2(clone,data,computeNodeLinks,computeNodeValues) 233{ 234 const graph = clone(data); 235 computeNodeLinks(graph); 236 computeNodeValues(graph); 237 return graph; 238} 239 240 241function _28(md){return( 242md`In steps 3 and 4 we compute the graph topology. [Here](https://stackoverflow.com/a/2603707)'s a good explanation of what is meant b
242y depth and height in this context.` 243)} 244 245function _computeNodeDepths(){return( 246function computeNodeDepths({nodes}) { 247 const n = nodes.length; 248 let current = new Set(nodes); 249 let next = new Set; 250 let x = 0; 251 while (current.size) { 252 for (const node of current) { 253 node.depth = x; 254 for (const {target} of node.sourceLinks) { 255 next.add(target); 256 } 257 } 258 if (++x > n) throw new Error("circular link"); 259 current = next; 260 next = new Set; 261 } 262} 263)} 264 265function _computeNodeHeights(){return( 266function computeNodeHeights({nodes}) { 267 const n = nodes.length; 268 let current = new Set(nodes); 269 let next = new Set; 270 let x = 0; 271 while (current.size) { 272 for (const node of current) { 273 node.height = x; 274 for (const {source} of node.targetLinks) { 275 next.add(source); 276 } 277 } 278 if (++x > n) throw new Error("circular link"); 279 current = next; 280 next = new Set; 281 } 282} 283)} 284 285function _step4(clone,data,computeNodeLinks,computeNodeValues,computeNodeDepths,computeNodeHeights) 286{ 287 const graph = clone(data); 288 computeNodeLinks(graph); 289 computeNodeValues(graph); 290 computeNodeDepths(graph); // step3 291 computeNodeHeights(graph); 292 return graph; 293} 294 295 296function _32(md){return( 297md`Now for the geometry (or breadth) of the nodes. 298 299The main function below has a few dependencies, so we'll work through each of those in turn.` 300)} 301 302function _computeNodeBreadths(computeNodeLayers,dy,y1,y0,d3,py,$0,initializeNodeBreadths,iterations,relaxRightToLeft,relaxLeftToRight){return( 303function computeNodeBreadths(graph) { 304 const columns = computeNodeLayers(graph); 305 let lpy = Math.min(dy, (y1 - y0) / (d3.max(columns, c => c.length) - 1)); 306 if (lpy > py) $0.value = lpy; // small modification to allow for py to be redefined 307 initializeNodeBreadths(columns); 308 for (let i = 0; i < iterations; ++i) { 309 const alpha = Math.pow(0.99, i); 310 const beta = Math.max(1 - alpha, (i + 1) / iterations); 311 relaxRightToLeft(columns, alpha, beta); 312 relaxLeftToRight(columns, alpha, beta); 313 } 314} 315)} 316 317function _34(md){return( 318md`\`computeNodeLayers\` determines which column a given node should be in and assigns \`x0\`, \`x1\` accordingly.` 319)} 320 321function _computeNodeLayers(d3,x1,x0,dx,align,sort){return( 322function computeNodeLayers({nodes}) { 323 const x = d3.max(nodes, d => d.depth) + 1; 324 const kx = (x1 - x0 - dx) / (x - 1); 325 const columns = new Array(x); 326 for (const node of nodes) { 327 const i = Math.max(0, Math.min(x - 1, Math.floor(align.call(null, node, x)))); 328 node.layer = i; 329 node.x0 = x0 + i * kx; 330 node.x1 = node.x0 + dx; 331 if (columns[i]) columns[i].push(node); 332 else columns[i] = [node]; 333 } 334 if (sort) for (const column of columns) { 335 column.sort(sort); 336 } 337 return columns; 338} 339)} 340 341function _columns(clone,step4,computeNodeLayers) 342{ 343 const graph = clone(step4); 344 const columns = computeNodeLayers(graph); 345 return columns; 346} 347 348 349function _37(md){return( 350md`\`initializeNodeBreadths\` is tasked with determining the \`y\` coordinates. It does so by calculating a scale factor \`ky\` based on the available vertical space.` 351)} 352 353function _initializeNodeBreadths(d3,y1,y0,py,value,reorderLinks){return( 354function initializeNodeBreadths(columns) { 355 const ky = d3.min(columns, c => (y1 - y0 - (c.length - 1) * py) / d3.sum(c, value)); 356 for (const nodes of columns) { 357 let y = y0; 358 for (const node of nodes) { 359 node.y0 = y; 360 node.y1 = y + node.value * ky; 361 y = node.y1 + py; 362 for (const link of node.sourceLinks) { 363 link.width = link.value * ky; 364 } 365 } 366 y = (y1 - y + py) / (nodes.length + 1); 367 for (let i = 0; i < nodes.length; ++i) { 368 const node = nodes[i]; 369 node.y0 += y * (i + 1); 370 node.y1 += y * (i + 1); 371 } 372 reorderLinks(nodes); 373 } 374} 375)} 376 377function _39(md){return( 378md`Once we've determined node breadths we can sort incoming and outgoing links accordingly (unless a link sorting function is specified).` 379)} 380 381function _reorderLinks(linkSort,ascendingTargetBreadth,ascendingSourceBreadth){return( 382function reorderLinks(nodes) { 383 if (linkSort === undefined) { 384 for (const {sourceLinks, targetLinks} of nodes) { 385 sourceLinks.sort(ascendingTargetBreadth); 386 targetLinks.sort(ascendingSourceBreadth); 387 } 388 } 389} 390)} 391 392function _ascendingBreadth(){return( 393function ascendingBreadth(a, b) { 394 return a.y0 - b.y0; 395} 396)} 397 398function _ascendingSourceBreadth(ascendingBreadth){return( 399function ascendingSourceBreadth(a, b) { 400 return ascendingBreadth(a.source, b.source) || a.index - b.index; 401} 402)} 403 404function _ascendingTargetBreadth(ascendingBreadth){return( 405function ascendingTargetBreadth(a, b) { 406 return ascendingBreadth(a.target, b.target) || a.index - b.index; 407} 408)} 409 410function _44(md){return( 411md`But we're not yet finished with [step 5](#computeNodeBreadths). We've determined the initial positions for each node, but they're currently suboptimal. The loop at the end of \`computeNodeBreadths\` uses iterative relaxation to improve the \`y\` positions. 412 413Try setting iterations to \`0\` to see the effect.` 414)} 415 416function _iterations(){return( 4176 418)} 419 420function _46(md){return( 421md`For each iteration nodes are relaxed right-to-left and left-to-right. But what does this mean? 422 423While the function names might suggest they relate to the horizontal plane, they are in fact used to update \`y\` coordinates. \`relaxRightToLeft\` repositions nodes based on their source links and \`relaxLeftToRight\` handles the target links. 424 425Each function attemtps to reduce tension by moving nodes towards their optimum position. Meanwhile \`resolveCollisions\` ensures that nodes aren't occluded during the process.` 426)} 427 428function _relaxRightToLeft(sourceTop,reorderNodeLinks,sort,ascendingBreadth,resolveCollisions){return( 429function relaxRightToLeft(columns, alpha, beta) { 430 for (let n = columns.length, i = n - 2; i >= 0; --i) { 431 const column = columns[i]; 432 for (const source of column) { 433 let y = 0; 434 let w = 0; 435 for (const {target, value} of source.sourceLinks) { 436 let v = value * (target.layer - source.layer); 437 y += sourceTop(source, target) * v; 438 w += v; 439 } 440 if (!(w > 0)) continue; 441 let dy = (y / w - source.y0) * alpha; 442 source.y0 += dy; 443 source.y1 += dy; 444 reorderNodeLinks(source); 445 } 446 if (sort === undefined) column.sort(ascendingBreadth); 447 resolveCollisions(column, beta); 448 } 449} 450)} 451 452function _sourceTop(py){return( 453function sourceTop(source, target) { 454 let y = target.y0 - (target.targetLinks.length - 1) * py / 2; 455 for (const {source: node, width} of target.targetLinks) { 456 if (node === source) break; 457 y += width + py; 458 } 459 for (const {target: node, width} of source.sourceLinks) { 460 if (node === target) break; 461 y -= width; 462 } 463 return y; 464} 465)} 466 467function _relaxLeftToRight(targetTop,reorderNodeLinks,sort,ascendingBreadth,resolveCollisions){return( 468function relaxLeftToRight(columns, alpha, beta) { 469 for (let i = 1, n = columns.length; i < n; ++i) { 470 const column = columns[i]; 471 for (const target of column) { 472 let y = 0; 473 let w = 0; 474 for (const {source, value} of target.targetLinks) { 475 let v = value * (target.layer - source.layer); 476 y += targetTop(source, target) * v; 477 w += v; 478 } 479 if (!(w > 0)) continue; 480 let dy = (y / w - target.y0) * alpha; 481 target.y0 += dy; 482 target.y1 += dy; 483 reorderNodeLinks(target); 484 } 485 if (sort === undefined) column.sort(ascendingBreadth); 486 resolveCollisions(column, beta); 487 } 488} 489)} 490 491function _targetTop(py){return( 492function targetTop(source, target) { 493 let y = source.y0 - (source.sourceLinks.length - 1) * py / 2; 494 for (const {target: node, width} of source.sourceLinks) { 495 if (node === target) break; 496 y += width + py; 497 } 498 for (const {source: node, width} of target.targetLinks) { 499 if (node === source) break; 500 y -= width; 501 } 502 return y; 503} 504)} 505 506function _reorderNodeLinks(linkSort,ascendingTargetBreadth,ascendingSourceBreadth){return( 507function reorderNodeLinks({sourceLinks, targetLinks}) { 508 if (linkSort === undefined) { 509 for (const {source: {sourceLinks}} of targetLinks) { 510 sourceLinks.sort(ascendingTargetBreadth); 511 } 512 for (const {target: {targetLinks}} of sourceLinks) { 513 targetLinks.sort(ascendingSourceBreadth); 514 } 515 } 516} 517)} 518 519function _resolveCollisions(resolveCollisionsBottomToTop,py,resolveCollisionsTo
519pToBottom,y1,y0){return( 520function resolveCollisions(nodes, alpha) { 521 const i = nodes.length >> 1; 522 const subject = nodes[i]; 523 resolveCollisionsBottomToTop(nodes, subject.y0 - py, i - 1, alpha); 524 resolveCollisionsTopToBottom(nodes, subject.y1 + py, i + 1, alpha); 525 resolveCollisionsBottomToTop(nodes, y1, nodes.length - 1, alpha); 526 resolveCollisionsTopToBottom(nodes, y0, 0, alpha); 527} 528)} 529 530function _resolveCollisionsTopToBottom(py){return( 531function resolveCollisionsTopToBottom(nodes, y, i, alpha) { 532 for (; i < nodes.length; ++i) { 533 const node = nodes[i]; 534 const dy = (y - node.y0) * alpha; 535 if (dy > 1e-6) node.y0 += dy, node.y1 += dy; 536 y = node.y1 + py; 537 } 538} 539)} 540 541function _resolveCollisionsBottomToTop(py){return( 542function resolveCollisionsBottomToTop(nodes, y, i, alpha) { 543 for (; i >= 0; --i) { 544 const node = nodes[i]; 545 const dy = (node.y1 - y) * alpha; 546 if (dy > 1e-6) node.y0 -= dy, node.y1 -= dy; 547 y = node.y0 - py; 548 } 549} 550)} 551 552function _step5(clone,data,computeNodeLinks,computeNodeValues,computeNodeDepths,computeNodeHeights,computeNodeBreadths) 553{ 554 const graph = clone(data); 555 computeNodeLinks(graph); 556 computeNodeValues(graph); 557 computeNodeDepths(graph); 558 computeNodeHeights(graph); 559 computeNodeBreadths(graph); 560 return graph; 561} 562 563 564function _56(md){return( 565md`Finally, we derive link breadths from node breadths.` 566)} 567 568function _computeLinkBreadths(){return( 569function computeLinkBreadths({nodes}) { 570 for (const node of nodes) { 571 let y0 = node.y0; 572 let y1 = y0; 573 for (const link of node.sourceLinks) { 574 link.y0 = y0 + link.width / 2; 575 y0 += link.width; 576 } 577 for (const link of node.targetLinks) { 578 link.y1 = y1 + link.width / 2; 579 y1 += link.width; 580 } 581 } 582} 583)} 584 585function _step6(clone,data,computeNodeLinks,computeNodeValues,computeNodeDepths,computeNodeHeights,computeNodeBreadths) 586{ 587 const graph = clone(data); 588 computeNodeLinks(graph); 589 computeNodeValues(graph); 590 computeNodeDepths(graph); 591 computeNodeHeights(graph); 592 computeNodeBreadths(graph); 593 computeNodeLinks(graph); 594 return graph; 595} 596 597 598function _59(md){return( 599md`And draw the sankey links using [d3.linkHorizontal](https://github.com/d3/d3-shape#linkHorizontal).` 600)} 601 602function _sankeyLinkHorizontal(d3) 603{ 604 function horizontalSource(d) { 605 return [d.source.x1, d.y0]; 606 } 607 608 function horizontalTarget(d) { 609 return [d.target.x0, d.y1]; 610 } 611 612 return function() { 613 return d3.linkHorizontal() 614 .source(horizontalSource) 615 .target(horizontalTarget); 616 } 617} 618 619 620function _clone(){return( 621function clone({nodes, links}) { 622 return { nodes: nodes.map(d => Object.assign({}, d)), links: links.map(d => Object.assign({}, d)) }; 623} 624)} 625 626function _62(md){return( 627md`_original chart cells_` 628)} 629 630function _format(d3,data) 631{ 632 const format = d3.format(",.0f"); 633 return data.units ? d => `${format(d)} ${data.units}` : format; 634} 635 636 637function _color(d3) 638{ 639 const color = d3.scaleOrdinal(d3.schemeCategory10); 640 return d => color(d.category === undefined ? d.name : d.category); 641} 642 643 644function _width(){return( 645954 646)} 647 648function _height(){return( 649600 650)} 651 652function _d3(require){return( 653require("d3@6") 654)} 655 656export default function define(runtime, observer) { 657 const main = runtime.module(); 658 function toString() { return this.url; } 659 const fileAttachments = new Map([ 660 ["energy.csv", {url: new URL("./files/d6774e9422bd72369f195a30d3a6b33ff9d41676cff4d89c93511e1a458efb3cfd16cbb7ce3fecdd8dd2466121e10c9bfe57fd73c7520bf358d352a92b898614.csv", import.meta.url), mimeType: "text/csv", toString}] 661 ]); 662 main.builtin("FileAttachment", runtime.fileAttachments(name => fileAttachments.get(name))); 663 main.variable(observer()).define(["md"], _1); 664 main.variable(observer("viewof shape")).define("viewof shape", ["radio"], _shape); 665 main.variable(observer("shape")).define("shape", ["Generators", "viewof shape"], (G, _) => G.input(_)); 666 main.variable(observer("chart")).define("chart", ["d3","width","height","sankey","data","color","format","DOM","shape","sankeyLinkHorizontal"], _chart); 667 main.variable(observer()).define(["md"], _4); 668 main.variable(observer("data")).define("data", ["FileAttachment"], _data); 669 main.variable(observer()).define(["md"], _6); 670 main.variable(observer("sankey")).define("sankey", ["clone","computeNodeLinks","computeNodeValues","computeNodeDepths","computeNodeHeights","computeNodeBreadths","computeLinkBreadths"], _sankey); 671 main.variable(observer()).define(["md"], _8); 672 main.variable(observer("x0")).define("x0", _x0); 673 main.variable(observer("x1")).define("x1", ["width"], _x1); 674 main.variable(observer("y0")).define("y0", _y0); 675 main.variable(observer("y1")).define("y1", ["height"], _y1); 676 main.variable(observer("dx")).define("dx", _dx); 677 main.variable(observer("dy")).define("dy", _dy); 678 main.define("initial py", _py); 679 main.variable(observer("mutable py")).define("mutable py", ["Mutable", "initial py"], (M, _) => new M(_)); 680 main.variable(observer("py")).define("py", ["mutable py"], _ => _.generator); 681 main.variable(observer("id")).define("id", _id); 682 main.variable(observer("sort")).define("sort", _sort); 683 main.variable(observer("linkSort")).define("linkSort", _linkSort); 684 main.variable(observer("align")).define("align", _align); 685 main.variable(observer()).define(["md"], _20); 686 main.variable(observer("computeNodeLinks")).define("computeNodeLinks", ["id","find","linkSort"], _computeNodeLinks); 687 main.variable(observer("find")).define("find", _find); 688 main.variable(observer("step1")).define("step1", ["clone","data","computeNodeLinks"], _step1); 689 main.variable(observer()).define(["md"], _24); 690 main.variable(observer("computeNodeValues")).define("computeNodeValues", ["d3","value"], _computeNodeValues); 691 main.variable(observer("value")).define("value", _value); 692 main.variable(observer("step2")).define("step2", ["clone","data","computeNodeLinks","computeNodeValues"], _step2); 693 main.variable(observer()).define(["md"], _28); 694 main.variable(observer("computeNodeDepths")).define("computeNodeDepths", _computeNodeDepths); 695 main.variable(observer("computeNodeHeights")).define("computeNodeHeights", _computeNodeHeights); 696 main.variable(observer("step4")).define("step4", ["clone","data","computeNodeLinks","computeNodeValues","computeNodeDepths","computeNodeHeights"], _step4); 697 main.variable(observer()).define(["md"], _32); 698 main.variable(observer("computeNodeBreadths")).define("computeNodeBreadths", ["computeNodeLayers","dy","y1","y0","d3","py","mutable py","initializeNodeBreadths","iterations","relaxRightToLeft","relaxLeftToRight"], _computeNodeBreadths); 699 main.variable(observer()).define(["md"], _34); 700 main.variable(observer("computeNodeLayers")).define("computeNodeLayers", ["d3","x1","x0","dx","align","sort"], _computeNodeLayers); 701 main.variable(observer("columns")).define("columns", ["clone","step4","computeNodeLayers"], _columns); 702 main.variable(observer()).define(["md"], _37); 703 main.variable(observer("initializeNodeBreadths")).define("initializeNodeBreadths", ["d3","y1","y0","py","value","reorderLinks"], _initializeNodeBreadths); 704 main.variable(observer()).define(["md"], _39); 705 main.variable(observer("reorderLinks")).define("reorderLinks", ["linkSort","ascendingTargetBreadth","ascendingSourceBreadth"], _reorderLinks); 706 main.variable(observer("ascendingBreadth")).define("ascendingBreadth", _ascendingBreadth); 707 main.variable(observer("ascendingSourceBreadth")).define("ascendingSourceBreadth", ["ascendingBreadth"], _ascendingSourceBreadth); 708 main.variable(observer("ascendingTargetBreadth")).define("ascendingTargetBreadth", ["ascendingBreadth"], _ascendingTargetBreadth); 709 main.variable(observer()).define(["md"], _44); 710 main.variable(observer("iterations")).define("iterations", _iterations); 711 main.variable(observer()).define(["md"], _46); 712 main.variable(observer("relaxRightToLeft")).define("relaxRightToLeft", ["sourceTop","reorderNodeLinks","sort","ascendingBreadth","resolve
712Collisions"], _relaxRightToLeft); 713 main.variable(observer("sourceTop")).define("sourceTop", ["py"], _sourceTop); 714 main.variable(observer("relaxLeftToRight")).define("relaxLeftToRight", ["targetTop","reorderNodeLinks","sort","ascendingBreadth","resolveCollisions"], _relaxLeftToRight); 715 main.variable(observer("targetTop")).define("targetTop", ["py"], _targetTop); 716 main.variable(observer("reorderNodeLinks")).define("reorderNodeLinks", ["linkSort","ascendingTargetBreadth","ascendingSourceBreadth"], _reorderNodeLinks); 717 main.variable(observer("resolveCollisions")).define("resolveCollisions", ["resolveCollisionsBottomToTop","py","resolveCollisionsTopToBottom","y1","y0"], _resolveCollisions); 718 main.variable(observer("resolveCollisionsTopToBottom")).define("resolveCollisionsTopToBottom", ["py"], _resolveCollisionsTopToBottom); 719 main.variable(observer("resolveCollisionsBottomToTop")).define("resolveCollisionsBottomToTop", ["py"], _resolveCollisionsBottomToTop); 720 main.variable(observer("step5")).define("step5", ["clone","data","computeNodeLinks","computeNodeValues","computeNodeDepths","computeNodeHeights","computeNodeBreadths"], _step5); 721 main.variable(observer()).define(["md"], _56); 722 main.variable(observer("computeLinkBreadths")).define("computeLinkBreadths", _computeLinkBreadths); 723 main.variable(observer("step6")).define("step6", ["clone","data","computeNodeLinks","computeNodeValues","computeNodeDepths","computeNodeHeights","computeNodeBreadths"], _step6); 724 main.variable(observer()).define(["md"], _59); 725 main.variable(observer("sankeyLinkHorizontal")).define("sankeyLinkHorizontal", ["d3"], _sankeyLinkHorizontal); 726 main.variable(observer("clone")).define("clone", _clone); 727 main.variable(observer()).define(["md"], _62); 728 main.variable(observer("format")).define("format", ["d3","data"], _format); 729 main.variable(observer("color")).define("color", ["d3"], _color); 730 main.variable(observer("width")).define("width", _width); 731 main.variable(observer("height")).define("height", _height); 732 main.variable(observer("d3")).define("d3", ["require"], _d3); 733 const child1 = runtime.module(define1); 734 main.import("radio", child1); 735 return main; 736}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.