@lucastho/d3-sankey-circular-ng 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ // https://github.com/lucastho/d3-sankey-circular-ng v0.1.0 Copyright 2026 Lucas Tho
2
+ !function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports,require("d3-array")):"function"==typeof define&&define.amd?define(["exports","d3-array"],n):n((t=t||self).d3=t.d3||{},t.d3)}(this,function(t,n){"use strict";function o(t){return t.target.depth}function e(t,n){return t.sourceLinks.length?t.depth:n-1}function r(t){return function(){return t}}function i(t,n){return s(t.source,n.source)||t.index-n.index}function c(t,n){return s(t.target,n.target)||t.index-n.index}function s(t,n){return t.y0-n.y0}function u(t){return t.value}function f(t){return t.index}function a(t){return t.nodes}function l(t){return t.links}function h(t,n){const o=t.get(n);if(!o)throw new Error("missing: "+n);return o}function y({nodes:t}){for(const n of t){let t=n.y0,o=t;for(const o of n.sourceLinks)o.y0=t+o.width/2,t+=o.width;for(const t of n.targetLinks)t.y1=o+t.width/2,o+=t.width}}const d=4,g=1.5,x=.15,k=3,p=.6,m=2.5,L=2,M=.5,w=1.5;function $(t,{dx:n,dy:o,x0:e,x1:r}){const i=function(t){return Math.max(t,d)}(o),c=[],s=[];for(const n of t.links)n.circular&&("top"===n.circularLinkType?c:s).push(n);function u(t){let n=t.length?i:0;return t.forEach((t,o)=>{t.circularLaneIndex=o,n+=t.width+(o>0?i:0)}),n}c.sort((t,n)=>n.width-t.width),s.sort((t,n)=>n.width-t.width);const f=Math.max(0,...t.links.filter(t=>t.circular).map(t=>t.width/2)),a=Math.min(Math.max(n*g,f+n),(r-e)*x);return{top:u(c),bottom:u(s),gutter:a,topStack:c,bottomStack:s,gap:i}}function b(t,n,o,e,r){const{gap:i}=o,c="top"===n?e:r;let s=i;t.forEach(t=>{const e=t.width,r="top"===n?c-s-e/2:c+s+e/2;s+=e+i;const u=t.source.x1,f=t.target.x0,a=t.y0,l=t.y1,h=Math.max(k*e,i),y=Math.max(h,o.gutter*p),d=u+y,g=f-y,x=t.source===t.target,$="top"===n?x?t.source.y0:Math.min(a,l):x?t.source.y1:Math.max(a,l),b=m*e+i;let S;"top"===n?(S=Math.min(r,$-b),S=Math.min(S,Math.min(a,l)-b)):(S=Math.max(r,$+b),S=Math.max(S,Math.max(a,l)+b));const v=[{x:u,y:a},{x:d,y:a},{x:d,y:S},{x:g,y:S},{x:g,y:l},{x:f,y:l}],E=Math.abs(S-a),P=Math.abs(S-l),j=Math.abs(g-d),z=Math.max(L,Math.min(o.gutter*M,e*w+i,E/2,P/2,j/2,y/2));t.circularPathData={points:v,radius:z,type:n,selfLoop:x,laneY:S,sourceX:u,targetX:f,sourceY:a,targetY:l}})}t.sankey=function(){let t,o,d,g=0,x=0,k=1,p=1,m=24,L=8,M=f,w=e,S=a,v=l,E=6;function P(){const t={nodes:S.apply(null,arguments),links:v.apply(null,arguments)};if(function({nodes:t,links:n}){for(const[n,o]of t.entries())o.index=n,o.sourceLinks=[],o.targetLinks=[];const o=new Map(t.map((n,o)=>[M(n,o,t),n]));for(const[t,e]of n.entries()){e.index=t;let{source:n,target:r}=e;"object"!=typeof n&&(n=e.source=h(o,n)),"object"!=typeof r&&(r=e.target=h(o,r)),n.sourceLinks.push(e),r.targetLinks.push(e)}if(null!=d)for(const{sourceLinks:n,targetLinks:o}of t)n.sort(d),o.sort(d)}(t),function({nodes:t,links:n}){for(const t of n)t.circular=!1;const o=new Set,e=new Set;function r(t){if(!o.has(t)){e.add(t);for(const n of t.sourceLinks)n.target===t?n.circular=!0:e.has(n.target)?n.circular=!0:o.has(n.target)||r(n.target);e.delete(t),o.add(t)}}for(const n of t)o.has(n)||r(n)}(t),function({nodes:t}){for(const o of t)o.value=void 0===o.fixedValue?Math.max(n.sum(o.sourceLinks,u),n.sum(o.targetLinks,u)):o.fixedValue}(t),function({nodes:t}){const n=t.length;let o=new Set(t),e=new Set,r=0;for(;o.size;){for(const t of o){t.depth=r;for(const n of t.sourceLinks)n.circular||e.add(n.target)}if(++r>n)throw new Error("circular link");o=e,e=new Set}}(t),function({nodes:t}){const n=t.length;let o=new Set(t),e=new Set,r=0;for(;o.size;){for(const t of o){t.height=r;for(const n of t.targetLinks)n.circular||e.add(n.source)}if(++r>n)throw new Error("circular link");o=e,e=new Set}}(t),!t.links.some(t=>t.circular))return j(t),y(t),t;j(t),y(t),function({links:t}){const n=(x+p)/2;for(const o of t){if(!o.circular)continue;const t=(o.source.y0+o.source.y1+o.target.y0+o.target.y1)/4;o.circularLinkType=t<n?"top":"bottom"}}(t);const o=$(t,{dx:m,dy:L,x0:g,x1:k});t.circularReservation=o;const e=g,r=k,i=x,c=p;try{g=e+o.gutter,k=r-o.gutter,x=i+o.top,p=c-o.bottom,j(t),y(t)}finally{g=e,k=r,x=i,p=c}return function(t,n){const o=Math.min(...t.nodes.map(t=>t.y0)),e=Math.max(...t.nodes.map(t=>t.y1));b(n.topStack,"top",n,o,e),b(n.bottomStack,"bottom",n,o,e)}(t,o),t}function j(e){const r=function({nodes:t}){const e=n.max(t,t=>t.depth)+1,r=(k-g-m)/(e-1),i=new Array(e);for(const n of t){const t=Math.max(0,Math.min(e-1,Math.floor(w.call(null,n,e))));n.layer=t,n.x0=g+t*r,n.x1=n.x0+m,i[t]?i[t].push(n):i[t]=[n]}if(o)for(const t of i)t.sort(o);return i}(e);t=Math.min(L,(p-x)/(n.max(r,t=>t.length)-1)),function(o){const e=n.min(o,o=>(p-x-(o.length-1)*t)/n.sum(o,u));for(const n of o){let o=x;for(const r of n){r.y0=o,r.y1=o+r.value*e,o=r.y1+t;for(const t of r.sourceLinks)t.width=t.value*e}o=(p-o+t)/(n.length+1);for(let t=0;t<n.length;++t){const e=n[t];e.y0+=o*(t+1),e.y1+=o*(t+1)}R(n)}}(r);for(let t=0;t<E;++t){const n=Math.pow(.99,t),o=Math.max(1-n,(t+1)/E);C(r,n,o),z(r,n,o)}}function z(t,n,e){for(let r=1,i=t.length;r<i;++r){const i=t[r];for(const t of i){let o=0,e=0;for(const{source:n,value:r}of t.targetLinks){let i=r*(t.layer-n.layer);o+=T(n,t)*i,e+=i}if(!(e>0))continue;let r=(o/e-t.y0)*n;t.y0+=r,t.y1+=r,I(t)}void 0===o&&i.sort(s),D(i,e)}}function C(t,n,e){for(let r=t.length-2;r>=0;--r){const i=t[r];for(const t of i){let o=0,e=0;for(const{target:n,value:r}of t.sourceLinks){let i=r*(n.layer-t.layer);o+=V(t,n)*i,e+=i}if(!(e>0))continue;let r=(o/e-t.y0)*n;t.y0+=r,t.y1+=r,I(t)}void 0===o&&i.sort(s),D(i,e)}}function D(n,o){const e=n.length>>1,r=n[e];A(n,r.y0-t,e-1,o),Y(n,r.y1+t,e+1,o),A(n,p,n.length-1,o),Y(n,x,0,o)}function Y(n,o,e,r){for(;e<n.length;++e){const i=n[e],c=(o-i.y0)*r;c>1e-6&&(i.y0+=c,i.y1+=c),o=i.y1+t}}function A(n,o,e,r){for(;e>=0;--e){const i=n[e],c=(i.y1-o)*r;c>1e-6&&(i.y0-=c,i.y1-=c),o=i.y0-t}}function I({sourceLinks:t,targetLinks:n}){if(void 0===d){for(const{source:{sourceLinks:t}}of n)t.sort(c);for(const{target:{targetLinks:n}}of t)n.sort(i)}}function R(t){if(void 0===d)for(const{sourceLinks:n,targetLinks:o}of t)n.sort(c),o.sort(i)}function T(n,o){let e=n.y0-(n.sourceLinks.length-1)*t/2;for(const{target:r,width:i}of n.sourceLinks){if(r===o)break;e+=i+t}for(const{source:t,width:r}of o.targetLinks){if(t===n)break;e-=r}return e}function V(n,o){let e=o.y0-(o.targetLinks.length-1)*t/2;for(const{source:r,width:i}of o.targetLinks){if(r===n)break;e+=i+t}for(const{target:t,width:r}of n.sourceLinks){if(t===o)break;e-=r}return e}return P.nodeId=function(t){return arguments.length?(M="function"==typeof t?t:r(t),P):M},P.nodeAlign=function(t){return arguments.length?(w="function"==typeof t?t:r(t),P):w},P.nodeSort=function(t){return arguments.length?(o=t,P):o},P.nodeWidth=function(t){return arguments.length?(m=+t,P):m},P.nodePadding=function(n){return arguments.length?(L=t=+n,P):L},P.nodes=function(t){return arguments.length?(S="function"==typeof t?t:r(t),P):S},P.links=function(t){return arguments.length?(v="function"==typeof t?t:r(t),P):v},P.linkSort=function(t){return arguments.length?(d=t,P):d},P.size=function(t){return arguments.length?(g=x=0,k=+t[0],p=+t[1],P):[k-g,p-x]},P.extent=function(t){return arguments.length?(g=+t[0][0],k=+t[1][0],x=+t[0][1],p=+t[1][1],P):[[g,x],[k,p]]},P.iterations=function(t){return arguments.length?(E=+t,P):E},P},t.sankeyCenter=function(t){return t.targetLinks.length?t.depth:t.sourceLinks.length?n.min(t.sourceLinks,o)-1:0},t.sankeyJustify=e,t.sankeyLeft=function(t){return t.depth},t.sankeyLinkCircular=function(){let t=!1;function n(t){return t.circular?function(t){const n=t.circularPathData;return function(t,n){if(t.length<2)return"";if(2===t.length)return`M${t[0].x},${t[0].y}L${t[1].x},${t[1].y}`;let o=`M${t[0].x},${t[0].y}`;for(let e=1;e<t.length-1;++e){const r=t[e-1],i=t[e],c=t[e+1],s=r.x-i.x,u=r.y-i.y,f=c.x-i.x,a=c.y-i.y,l=Math.hypot(s,u),h=Math.hypot(f,a);if(l<1e-6||h<1e-6){o+=`L${i.x},${i.y}`;continue}const y=Math.min(n,l/2,h/2),d=i.x+s/l*y,g=i.y+u/l*y,x=i.x+f/h*y,k=i.y+a/h*y;o+=`L${d},${g}`,o+=`Q${i.x},${i.y} ${x},${k}`}const e=t[t.length-1];return o+=`L${e.x},${e.y}`}(n.points,n.radius)}(t):function(t){const n=t.source.x1,o=t.target.x0,e=(n+o)/2;return`M${n},${t.y0}C${e},${t.y0} ${e},${t.y1} ${o},${t.y1}`}(t)}return n.points=function(t){return t.circular?t.circularPathData.points:[{x:t.source.x1,y:t.y0},{x:t.target.x0,y:t.y1}]},n.debug=function(o){return arguments.length?(t=!!o,n):t},n},t.sankeyRight=function(t,n){return n-1-t.height},Object.defineProperty(t,"__esModule",{value:!0})});
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@lucastho/d3-sankey-circular-ng",
3
+ "version": "0.1.0",
4
+ "description": "Fork of d3-sankey adding support for circular links (back-edges and self-loops). Visualize flow between nodes in a directed network that may contain cycles.",
5
+ "keywords": [
6
+ "d3",
7
+ "d3-module",
8
+ "sankey",
9
+ "circular",
10
+ "cyclic",
11
+ "flow"
12
+ ],
13
+ "author": {
14
+ "name": "Lucas Tho"
15
+ },
16
+ "contributors": [
17
+ {
18
+ "name": "Mike Bostock",
19
+ "url": "https://bost.ocks.org/mike/"
20
+ }
21
+ ],
22
+ "license": "ISC",
23
+ "type": "module",
24
+ "main": "dist/d3-sankey-circular-ng.js",
25
+ "unpkg": "dist/d3-sankey-circular-ng.min.js",
26
+ "jsdelivr": "dist/d3-sankey-circular-ng.min.js",
27
+ "module": "src/index.js",
28
+ "exports": {
29
+ "umd": "./dist/d3-sankey-circular-ng.min.js",
30
+ "default": "./src/index.js"
31
+ },
32
+ "homepage": "https://github.com/lucastho/d3-sankey-circular-ng",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/lucastho/d3-sankey-circular-ng.git"
36
+ },
37
+ "bugs": {
38
+ "url": "https://github.com/lucastho/d3-sankey-circular-ng/issues"
39
+ },
40
+ "files": [
41
+ "dist/**/*.js",
42
+ "src/**/*.js"
43
+ ],
44
+ "scripts": {
45
+ "pretest": "rollup -c",
46
+ "test": "tape 'test/**/*-test.js' && eslint src",
47
+ "prepublishOnly": "rm -rf dist && yarn test",
48
+ "postpublish": "git push && git push --tags"
49
+ },
50
+ "dependencies": {
51
+ "d3-array": "^3.0.0"
52
+ },
53
+ "devDependencies": {
54
+ "eslint": "6",
55
+ "rollup": "1",
56
+ "rollup-plugin-terser": "5",
57
+ "tape": "4"
58
+ }
59
+ }
package/src/align.js ADDED
@@ -0,0 +1,23 @@
1
+ import {min} from "d3-array";
2
+
3
+ function targetDepth(d) {
4
+ return d.target.depth;
5
+ }
6
+
7
+ export function left(node) {
8
+ return node.depth;
9
+ }
10
+
11
+ export function right(node, n) {
12
+ return n - 1 - node.height;
13
+ }
14
+
15
+ export function justify(node, n) {
16
+ return node.sourceLinks.length ? node.depth : n - 1;
17
+ }
18
+
19
+ export function center(node) {
20
+ return node.targetLinks.length ? node.depth
21
+ : node.sourceLinks.length ? min(node.sourceLinks, targetDepth) - 1
22
+ : 0;
23
+ }
@@ -0,0 +1,5 @@
1
+ export default function constant(x) {
2
+ return function() {
3
+ return x;
4
+ };
5
+ }
package/src/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export {default as sankey} from "./sankey.js";
2
+ export {center as sankeyCenter, left as sankeyLeft, right as sankeyRight, justify as sankeyJustify} from "./align.js";
3
+ export {default as sankeyLinkCircular} from "./sankeyLinkCircular.js";