arc.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. "use strict";
  2. var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
  3. if (k2 === undefined) k2 = k;
  4. var desc = Object.getOwnPropertyDescriptor(m, k);
  5. if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
  6. desc = { enumerable: true, get: function() { return m[k]; } };
  7. }
  8. Object.defineProperty(o, k2, desc);
  9. }) : (function(o, m, k, k2) {
  10. if (k2 === undefined) k2 = k;
  11. o[k2] = m[k];
  12. }));
  13. var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
  14. Object.defineProperty(o, "default", { enumerable: true, value: v });
  15. }) : function(o, v) {
  16. o["default"] = v;
  17. });
  18. var __importStar = (this && this.__importStar) || function (mod) {
  19. if (mod && mod.__esModule) return mod;
  20. var result = {};
  21. if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
  22. __setModuleDefault(result, mod);
  23. return result;
  24. };
  25. Object.defineProperty(exports, "__esModule", { value: true });
  26. exports.Arc = void 0;
  27. const d3_array_1 = require("d3-array");
  28. const helper_1 = require("../../../utils/helper");
  29. const SortMethods = __importStar(require("./sort"));
  30. const DEFAULT_OPTIONS = {
  31. y: 0,
  32. thickness: 0.05,
  33. weight: false,
  34. marginRatio: 0.1,
  35. id: (node) => node.id,
  36. source: (edge) => edge.source,
  37. target: (edge) => edge.target,
  38. sourceWeight: (edge) => edge.value || 1,
  39. targetWeight: (edge) => edge.value || 1,
  40. sortBy: null,
  41. };
  42. /**
  43. * Layout for Arc / Chord diagram with d3 style.
  44. */
  45. function Arc(options) {
  46. const { y, thickness, weight, marginRatio, id, source, target, sourceWeight, targetWeight, sortBy, } = Object.assign(Object.assign({}, DEFAULT_OPTIONS), options);
  47. function arc(data) {
  48. // Clone first.
  49. const nodes = data.nodes.map((n) => (Object.assign({}, n)));
  50. const edges = data.edges.map((n) => (Object.assign({}, n)));
  51. // Keep reference in below functions.
  52. preprocess(nodes, edges);
  53. sortNodes(nodes, edges);
  54. layoutNodes(nodes, edges);
  55. layoutEdges(nodes, edges);
  56. return { nodes, edges };
  57. }
  58. /**
  59. * Calculate id, value, frequency for node, and source,target for edge.
  60. */
  61. function preprocess(nodes, edges) {
  62. edges.forEach((edge) => {
  63. edge.source = source(edge);
  64. edge.target = target(edge);
  65. edge.sourceWeight = sourceWeight(edge);
  66. edge.targetWeight = targetWeight(edge);
  67. });
  68. // Group edges by source, target.
  69. const edgesBySource = (0, d3_array_1.group)(edges, (e) => e.source);
  70. const edgesByTarget = (0, d3_array_1.group)(edges, (e) => e.target);
  71. nodes.forEach((node) => {
  72. node.id = id(node);
  73. const sources = edgesBySource.has(node.id)
  74. ? edgesBySource.get(node.id)
  75. : [];
  76. const targets = edgesByTarget.has(node.id)
  77. ? edgesByTarget.get(node.id)
  78. : [];
  79. node.frequency = sources.length + targets.length;
  80. node.value =
  81. (0, d3_array_1.sum)(sources, (d) => d.sourceWeight) +
  82. (0, d3_array_1.sum)(targets, (d) => d.targetWeight);
  83. });
  84. return { nodes, edges };
  85. }
  86. function sortNodes(nodes, edges) {
  87. const method = typeof sortBy === 'function' ? sortBy : SortMethods[sortBy];
  88. if (method) {
  89. nodes.sort(method);
  90. }
  91. }
  92. function layoutNodes(nodes, edges) {
  93. const size = nodes.length;
  94. if (!size) {
  95. throw (0, helper_1.error)("Invalid nodes: it's empty!");
  96. }
  97. // No weight.
  98. if (!weight) {
  99. const deltaX = 1 / size;
  100. nodes.forEach((node, i) => {
  101. node.x = (i + 0.5) * deltaX;
  102. node.y = y;
  103. });
  104. return { nodes, edges };
  105. }
  106. // todo: marginRatio should be in [0, 1)
  107. // todo: thickness shoule be in (0, 1)
  108. const margin = marginRatio / (2 * size);
  109. const total = nodes.reduce((prev, node) => (prev += node.value), 0);
  110. nodes.reduce((deltaX, node) => {
  111. node.weight = node.value / total;
  112. node.width = node.weight * (1 - marginRatio);
  113. node.height = thickness;
  114. /* points
  115. * 3---2
  116. * | |
  117. * 0---1
  118. */
  119. const minX = margin + deltaX;
  120. const maxX = minX + node.width;
  121. const minY = y - thickness / 2;
  122. const maxY = minY + thickness;
  123. node.x = [minX, maxX, maxX, minX];
  124. node.y = [minY, minY, maxY, maxY];
  125. // Return next deltaX.
  126. return deltaX + node.width + 2 * margin;
  127. }, 0);
  128. return {
  129. nodes,
  130. edges,
  131. };
  132. }
  133. /**
  134. * Get edge layout information from nodes, and save into edge object.
  135. */
  136. function layoutEdges(nodes, edges) {
  137. const nodesMap = new Map(nodes.map((d) => [d.id, d]));
  138. if (!weight) {
  139. edges.forEach((edge) => {
  140. const sourceId = source(edge);
  141. const targetId = target(edge);
  142. const sourceNode = nodesMap.get(sourceId);
  143. const targetNode = nodesMap.get(targetId);
  144. // Edge's layout information is Equal with node.
  145. if (sourceNode && targetNode) {
  146. edge.x = [sourceNode.x, targetNode.x];
  147. edge.y = [sourceNode.y, targetNode.y];
  148. }
  149. });
  150. return { nodes, edges };
  151. }
  152. // Initial edge.x, edge.y.
  153. edges.forEach((edge) => {
  154. edge.x = [0, 0, 0, 0];
  155. edge.y = [y, y, y, y];
  156. });
  157. // Group edges by source, target.
  158. const edgesBySource = (0, d3_array_1.group)(edges, (e) => e.source);
  159. const edgesByTarget = (0, d3_array_1.group)(edges, (e) => e.target);
  160. // When weight = true, we need to calculation the bbox of edge start/end.
  161. nodes.forEach((node) => {
  162. const { edges, width, x, y, value, id } = node;
  163. const sourceEdges = edgesBySource.get(id) || [];
  164. const targetEdges = edgesByTarget.get(id) || [];
  165. let offset = 0;
  166. /* points
  167. * 0----------2
  168. * | |
  169. * 1----------3
  170. */
  171. sourceEdges.map((edge) => {
  172. const w = (edge.sourceWeight / value) * width;
  173. edge.x[0] = node.x[0] + offset;
  174. edge.x[1] = node.x[0] + offset + w;
  175. offset += w;
  176. });
  177. targetEdges.forEach((edge) => {
  178. const w = (edge.targetWeight / value) * width;
  179. edge.x[3] = node.x[0] + offset;
  180. edge.x[2] = node.x[0] + offset + w;
  181. offset += w;
  182. });
  183. });
  184. }
  185. return arc;
  186. }
  187. exports.Arc = Arc;
  188. //# sourceMappingURL=arc.js.map