chordLayout.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. /**
  20. * AUTO-GENERATED FILE. DO NOT MODIFY.
  21. */
  22. /*
  23. * Licensed to the Apache Software Foundation (ASF) under one
  24. * or more contributor license agreements. See the NOTICE file
  25. * distributed with this work for additional information
  26. * regarding copyright ownership. The ASF licenses this file
  27. * to you under the Apache License, Version 2.0 (the
  28. * "License"); you may not use this file except in compliance
  29. * with the License. You may obtain a copy of the License at
  30. *
  31. * http://www.apache.org/licenses/LICENSE-2.0
  32. *
  33. * Unless required by applicable law or agreed to in writing,
  34. * software distributed under the License is distributed on an
  35. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36. * KIND, either express or implied. See the License for the
  37. * specific language governing permissions and limitations
  38. * under the License.
  39. */
  40. import { normalizeArcAngles } from 'zrender/lib/core/PathProxy.js';
  41. import { getCircleLayout } from '../../util/layout.js';
  42. var RADIAN = Math.PI / 180;
  43. export default function chordCircularLayout(ecModel, api) {
  44. ecModel.eachSeriesByType('chord', function (seriesModel) {
  45. chordLayout(seriesModel, api);
  46. });
  47. }
  48. function chordLayout(seriesModel, api) {
  49. var nodeData = seriesModel.getData();
  50. var nodeGraph = nodeData.graph;
  51. var edgeData = seriesModel.getEdgeData();
  52. var edgeCount = edgeData.count();
  53. if (!edgeCount) {
  54. return;
  55. }
  56. var _a = getCircleLayout(seriesModel, api),
  57. cx = _a.cx,
  58. cy = _a.cy,
  59. r = _a.r,
  60. r0 = _a.r0;
  61. var padAngle = Math.max((seriesModel.get('padAngle') || 0) * RADIAN, 0);
  62. var minAngle = Math.max((seriesModel.get('minAngle') || 0) * RADIAN, 0);
  63. var startAngle = -seriesModel.get('startAngle') * RADIAN;
  64. var endAngle = startAngle + Math.PI * 2;
  65. var clockwise = seriesModel.get('clockwise');
  66. var dir = clockwise ? 1 : -1;
  67. // Normalize angles
  68. var angles = [startAngle, endAngle];
  69. normalizeArcAngles(angles, !clockwise);
  70. var normalizedStartAngle = angles[0],
  71. normalizedEndAngle = angles[1];
  72. var totalAngle = normalizedEndAngle - normalizedStartAngle;
  73. var allZero = nodeData.getSum('value') === 0 && edgeData.getSum('value') === 0;
  74. // Sum of each node's edge values
  75. var nodeValues = [];
  76. var renderedNodeCount = 0;
  77. nodeGraph.eachEdge(function (edge) {
  78. // All links use the same value 1 when allZero is true
  79. var value = allZero ? 1 : edge.getValue('value');
  80. if (allZero && (value > 0 || minAngle)) {
  81. // When allZero is true, angle is in direct proportion to number
  82. // of links both in and out of the node.
  83. renderedNodeCount += 2;
  84. }
  85. var node1Index = edge.node1.dataIndex;
  86. var node2Index = edge.node2.dataIndex;
  87. nodeValues[node1Index] = (nodeValues[node1Index] || 0) + value;
  88. nodeValues[node2Index] = (nodeValues[node2Index] || 0) + value;
  89. });
  90. // Update nodeValues with data.value if exists
  91. var nodeValueSum = 0;
  92. nodeGraph.eachNode(function (node) {
  93. var dataValue = node.getValue('value');
  94. if (!isNaN(dataValue)) {
  95. nodeValues[node.dataIndex] = Math.max(dataValue, nodeValues[node.dataIndex] || 0);
  96. }
  97. if (!allZero && (nodeValues[node.dataIndex] > 0 || minAngle)) {
  98. // When allZero is false, angle is in direct proportion to node's
  99. // value
  100. renderedNodeCount++;
  101. }
  102. nodeValueSum += nodeValues[node.dataIndex] || 0;
  103. });
  104. if (renderedNodeCount === 0 || nodeValueSum === 0) {
  105. return;
  106. }
  107. if (padAngle * renderedNodeCount >= Math.abs(totalAngle)) {
  108. // Not enough angle to render the pad, minAngle has higher priority, and padAngle takes the rest
  109. padAngle = Math.max(0, (Math.abs(totalAngle) - minAngle * renderedNodeCount) / renderedNodeCount);
  110. }
  111. if ((padAngle + minAngle) * renderedNodeCount >= Math.abs(totalAngle)) {
  112. // Not enough angle to render the minAngle, so ignore the minAngle
  113. minAngle = (Math.abs(totalAngle) - padAngle * renderedNodeCount) / renderedNodeCount;
  114. }
  115. var unitAngle = (totalAngle - padAngle * renderedNodeCount * dir) / nodeValueSum;
  116. var totalDeficit = 0; // sum of deficits of nodes with span < minAngle
  117. var totalSurplus = 0; // sum of (spans - minAngle) of nodes with span > minAngle
  118. var totalSurplusSpan = 0; // sum of spans of nodes with span > minAngle
  119. var minSurplus = Infinity; // min of (spans - minAngle) of nodes with span > minAngle
  120. nodeGraph.eachNode(function (node) {
  121. var value = nodeValues[node.dataIndex] || 0;
  122. var spanAngle = unitAngle * (nodeValueSum ? value : 1) * dir;
  123. if (Math.abs(spanAngle) < minAngle) {
  124. totalDeficit += minAngle - Math.abs(spanAngle);
  125. } else {
  126. minSurplus = Math.min(minSurplus, Math.abs(spanAngle) - minAngle);
  127. totalSurplus += Math.abs(spanAngle) - minAngle;
  128. totalSurplusSpan += Math.abs(spanAngle);
  129. }
  130. node.setLayout({
  131. angle: spanAngle,
  132. value: value
  133. });
  134. });
  135. var surplusAsMuchAsPossible = false;
  136. if (totalDeficit > totalSurplus) {
  137. // Not enough angle to spread the nodes, scale all
  138. var scale_1 = totalDeficit / totalSurplus;
  139. nodeGraph.eachNode(function (node) {
  140. var spanAngle = node.getLayout().angle;
  141. if (Math.abs(spanAngle) >= minAngle) {
  142. node.setLayout({
  143. angle: spanAngle * scale_1,
  144. ratio: scale_1
  145. }, true);
  146. } else {
  147. node.setLayout({
  148. angle: minAngle,
  149. ratio: minAngle === 0 ? 1 : spanAngle / minAngle
  150. }, true);
  151. }
  152. });
  153. } else {
  154. // For example, if totalDeficit is 60 degrees and totalSurplus is 70
  155. // degrees but one of the sector can only reduced by 1 degree,
  156. // if we decrease it with the ratio of value to other surplused nodes,
  157. // it will have smaller angle than minAngle itself.
  158. // So we need to borrow some angle from other nodes.
  159. nodeGraph.eachNode(function (node) {
  160. if (surplusAsMuchAsPossible) {
  161. return;
  162. }
  163. var spanAngle = node.getLayout().angle;
  164. var borrowRatio = Math.min(spanAngle / totalSurplusSpan, 1);
  165. var borrowAngle = borrowRatio * totalDeficit;
  166. if (spanAngle - borrowAngle < minAngle) {
  167. // It will have less than minAngle after borrowing
  168. surplusAsMuchAsPossible = true;
  169. }
  170. });
  171. }
  172. var restDeficit = totalDeficit;
  173. nodeGraph.eachNode(function (node) {
  174. if (restDeficit <= 0) {
  175. return;
  176. }
  177. var spanAngle = node.getLayout().angle;
  178. if (spanAngle > minAngle && minAngle > 0) {
  179. var borrowRatio = surplusAsMuchAsPossible ? 1 : Math.min(spanAngle / totalSurplusSpan, 1);
  180. var maxBorrowAngle = spanAngle - minAngle;
  181. var borrowAngle = Math.min(maxBorrowAngle, Math.min(restDeficit, totalDeficit * borrowRatio));
  182. restDeficit -= borrowAngle;
  183. node.setLayout({
  184. angle: spanAngle - borrowAngle,
  185. ratio: (spanAngle - borrowAngle) / spanAngle
  186. }, true);
  187. } else if (minAngle > 0) {
  188. node.setLayout({
  189. angle: minAngle,
  190. ratio: spanAngle === 0 ? 1 : minAngle / spanAngle
  191. }, true);
  192. }
  193. });
  194. var angle = normalizedStartAngle;
  195. var edgeAccAngle = [];
  196. nodeGraph.eachNode(function (node) {
  197. var spanAngle = Math.max(node.getLayout().angle, minAngle);
  198. node.setLayout({
  199. cx: cx,
  200. cy: cy,
  201. r0: r0,
  202. r: r,
  203. startAngle: angle,
  204. endAngle: angle + spanAngle * dir,
  205. clockwise: clockwise
  206. }, true);
  207. edgeAccAngle[node.dataIndex] = angle;
  208. angle += (spanAngle + padAngle) * dir;
  209. });
  210. nodeGraph.eachEdge(function (edge) {
  211. var value = allZero ? 1 : edge.getValue('value');
  212. var spanAngle = unitAngle * (nodeValueSum ? value : 1) * dir;
  213. var node1Index = edge.node1.dataIndex;
  214. var sStartAngle = edgeAccAngle[node1Index] || 0;
  215. var sSpan = Math.abs((edge.node1.getLayout().ratio || 1) * spanAngle);
  216. var sEndAngle = sStartAngle + sSpan * dir;
  217. var s1 = [cx + r0 * Math.cos(sStartAngle), cy + r0 * Math.sin(sStartAngle)];
  218. var s2 = [cx + r0 * Math.cos(sEndAngle), cy + r0 * Math.sin(sEndAngle)];
  219. var node2Index = edge.node2.dataIndex;
  220. var tStartAngle = edgeAccAngle[node2Index] || 0;
  221. var tSpan = Math.abs((edge.node2.getLayout().ratio || 1) * spanAngle);
  222. var tEndAngle = tStartAngle + tSpan * dir;
  223. var t1 = [cx + r0 * Math.cos(tStartAngle), cy + r0 * Math.sin(tStartAngle)];
  224. var t2 = [cx + r0 * Math.cos(tEndAngle), cy + r0 * Math.sin(tEndAngle)];
  225. edge.setLayout({
  226. s1: s1,
  227. s2: s2,
  228. sStartAngle: sStartAngle,
  229. sEndAngle: sEndAngle,
  230. t1: t1,
  231. t2: t2,
  232. tStartAngle: tStartAngle,
  233. tEndAngle: tEndAngle,
  234. cx: cx,
  235. cy: cy,
  236. r: r0,
  237. value: value,
  238. clockwise: clockwise
  239. });
  240. edgeAccAngle[node1Index] = sEndAngle;
  241. edgeAccAngle[node2Index] = tEndAngle;
  242. });
  243. }