chord.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.chordLayout = exports.getDefaultOptions = void 0;
  4. /*
  5. * for Arc Diagram (edges without weight) / Chord Diagram (edges with source and target weight)
  6. * graph data required (nodes, edges)
  7. */
  8. var util_1 = require("@antv/util");
  9. var DEFAULT_OPTIONS = {
  10. y: 0,
  11. nodeWidthRatio: 0.05,
  12. weight: false,
  13. nodePaddingRatio: 0.1,
  14. id: function (node) { return node.id; },
  15. source: function (edge) { return edge.source; },
  16. target: function (edge) { return edge.target; },
  17. sourceWeight: function (edge) { return edge.value || 1; },
  18. targetWeight: function (edge) { return edge.value || 1; },
  19. sortBy: null, // optional, id | weight | frequency | {function}
  20. };
  21. /**
  22. * 处理节点的value、edges
  23. * @param nodeById
  24. * @param edges
  25. * @param options
  26. */
  27. function processGraph(nodeById, edges, options) {
  28. (0, util_1.forIn)(nodeById, function (node, id) {
  29. // in edges, out edges
  30. node.inEdges = edges.filter(function (edge) { return "".concat(options.target(edge)) === "".concat(id); });
  31. node.outEdges = edges.filter(function (edge) { return "".concat(options.source(edge)) === "".concat(id); });
  32. // frequency
  33. node.edges = node.outEdges.concat(node.inEdges);
  34. node.frequency = node.edges.length;
  35. // weight
  36. node.value = 0;
  37. node.inEdges.forEach(function (edge) {
  38. node.value += options.targetWeight(edge);
  39. });
  40. node.outEdges.forEach(function (edge) {
  41. node.value += options.sourceWeight(edge);
  42. });
  43. });
  44. }
  45. /**
  46. * 节点排序
  47. * @param nodes
  48. * @param options
  49. */
  50. function sortNodes(nodes, options) {
  51. var sortMethods = {
  52. weight: function (a, b) { return b.value - a.value; },
  53. frequency: function (a, b) { return b.frequency - a.frequency; },
  54. id: function (a, b) { return "".concat(options.id(a)).localeCompare("".concat(options.id(b))); },
  55. };
  56. var method = sortMethods[options.sortBy];
  57. if (!method && (0, util_1.isFunction)(options.sortBy)) {
  58. method = options.sortBy;
  59. }
  60. if (method) {
  61. nodes.sort(method);
  62. }
  63. }
  64. function layoutNodes(nodes, options) {
  65. var len = nodes.length;
  66. if (!len) {
  67. throw new TypeError("Invalid nodes: it's empty!");
  68. }
  69. if (options.weight) {
  70. var nodePaddingRatio_1 = options.nodePaddingRatio;
  71. if (nodePaddingRatio_1 < 0 || nodePaddingRatio_1 >= 1) {
  72. throw new TypeError('Invalid nodePaddingRatio: it must be in range [0, 1)!');
  73. }
  74. var margin_1 = nodePaddingRatio_1 / (2 * len);
  75. var nodeWidthRatio_1 = options.nodeWidthRatio;
  76. if (nodeWidthRatio_1 <= 0 || nodeWidthRatio_1 >= 1) {
  77. throw new TypeError('Invalid nodeWidthRatio: it must be in range (0, 1)!');
  78. }
  79. var totalValue_1 = 0;
  80. nodes.forEach(function (node) {
  81. totalValue_1 += node.value;
  82. });
  83. nodes.forEach(function (node) {
  84. node.weight = node.value / totalValue_1;
  85. node.width = node.weight * (1 - nodePaddingRatio_1);
  86. node.height = nodeWidthRatio_1;
  87. });
  88. nodes.forEach(function (node, index) {
  89. // x
  90. var deltaX = 0;
  91. for (var i = index - 1; i >= 0; i--) {
  92. deltaX += nodes[i].width + 2 * margin_1;
  93. }
  94. var minX = (node.minX = margin_1 + deltaX);
  95. var maxX = (node.maxX = node.minX + node.width);
  96. var minY = (node.minY = options.y - nodeWidthRatio_1 / 2);
  97. var maxY = (node.maxY = minY + nodeWidthRatio_1);
  98. node.x = [minX, maxX, maxX, minX];
  99. node.y = [minY, minY, maxY, maxY];
  100. /* points
  101. * 3---2
  102. * | |
  103. * 0---1
  104. */
  105. // node.x = minX + 0.5 * node.width;
  106. // node.y = options.y;
  107. });
  108. }
  109. else {
  110. var deltaX_1 = 1 / len;
  111. nodes.forEach(function (node, index) {
  112. node.x = (index + 0.5) * deltaX_1;
  113. node.y = options.y;
  114. });
  115. }
  116. return nodes;
  117. }
  118. function locatingEdges(nodeById, edges, options) {
  119. if (options.weight) {
  120. var valueById_1 = {};
  121. (0, util_1.forIn)(nodeById, function (node, id) {
  122. valueById_1[id] = node.value;
  123. });
  124. edges.forEach(function (edge) {
  125. var sId = options.source(edge);
  126. var tId = options.target(edge);
  127. var sNode = nodeById[sId];
  128. var tNode = nodeById[tId];
  129. if (sNode && tNode) {
  130. var sValue = valueById_1[sId];
  131. var currentSValue = options.sourceWeight(edge);
  132. var sStart = sNode.minX + ((sNode.value - sValue) / sNode.value) * sNode.width;
  133. var sEnd = sStart + (currentSValue / sNode.value) * sNode.width;
  134. valueById_1[sId] -= currentSValue;
  135. var tValue = valueById_1[tId];
  136. var currentTValue = options.targetWeight(edge);
  137. var tStart = tNode.minX + ((tNode.value - tValue) / tNode.value) * tNode.width;
  138. var tEnd = tStart + (currentTValue / tNode.value) * tNode.width;
  139. valueById_1[tId] -= currentTValue;
  140. var y = options.y;
  141. edge.x = [sStart, sEnd, tStart, tEnd];
  142. edge.y = [y, y, y, y];
  143. // 将edge的source与target的id换为 sourceNode与targetNode
  144. edge.source = sNode;
  145. edge.target = tNode;
  146. }
  147. });
  148. }
  149. else {
  150. edges.forEach(function (edge) {
  151. var sNode = nodeById[options.source(edge)];
  152. var tNode = nodeById[options.target(edge)];
  153. if (sNode && tNode) {
  154. edge.x = [sNode.x, tNode.x];
  155. edge.y = [sNode.y, tNode.y];
  156. // 将edge的source与target的id换为 sourceNode与targetNode
  157. edge.source = sNode;
  158. edge.target = tNode;
  159. }
  160. });
  161. }
  162. return edges;
  163. }
  164. function getDefaultOptions(options) {
  165. return (0, util_1.assign)({}, DEFAULT_OPTIONS, options);
  166. }
  167. exports.getDefaultOptions = getDefaultOptions;
  168. function chordLayout(chordLayoutOptions, chordLayoutInputData) {
  169. var options = getDefaultOptions(chordLayoutOptions);
  170. var nodeById = {};
  171. var nodes = chordLayoutInputData.nodes;
  172. var links = chordLayoutInputData.links;
  173. nodes.forEach(function (node) {
  174. var id = options.id(node);
  175. nodeById[id] = node;
  176. });
  177. processGraph(nodeById, links, options);
  178. sortNodes(nodes, options);
  179. var outputNodes = layoutNodes(nodes, options);
  180. var outputLinks = locatingEdges(nodeById, links, options);
  181. return {
  182. nodes: outputNodes,
  183. links: outputLinks,
  184. };
  185. }
  186. exports.chordLayout = chordLayout;
  187. //# sourceMappingURL=chord.js.map