chord.js 6.5 KB

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