utils.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import { Linear } from '@antv/scale';
  2. import { lowerFirst } from '@antv/util';
  3. import { extent } from 'd3-array';
  4. import { indexOf } from '../utils/array';
  5. import { isPolar, isTranspose } from '../utils/coordinate';
  6. import { angle, angleWithQuadrant, dist, sub } from '../utils/vector';
  7. export function applyStyle(selection, style) {
  8. for (const [key, value] of Object.entries(style)) {
  9. selection.style(key, value);
  10. }
  11. }
  12. /**
  13. * Draw polygon path with points.
  14. * @param path
  15. * @param points
  16. */
  17. export function appendPolygon(path, points) {
  18. points.forEach((p, idx) => idx === 0 ? path.moveTo(p[0], p[1]) : path.lineTo(p[0], p[1]));
  19. path.closePath();
  20. return path;
  21. }
  22. /**
  23. * Draw arrow between `from` and `to`.
  24. * @param from
  25. * @param to
  26. * @returns
  27. */
  28. export function arrowPoints(from, to, options) {
  29. const { arrowSize } = options;
  30. const size = typeof arrowSize === 'string'
  31. ? (+parseFloat(arrowSize) / 100) * dist(from, to)
  32. : arrowSize;
  33. // TODO Use config from style.
  34. // Default arrow rotate is 30°.
  35. const arrowAngle = Math.PI / 6;
  36. const angle = Math.atan2(to[1] - from[1], to[0] - from[0]);
  37. const arrowAngle1 = Math.PI / 2 - angle - arrowAngle;
  38. const arrow1 = [
  39. to[0] - size * Math.sin(arrowAngle1),
  40. to[1] - size * Math.cos(arrowAngle1),
  41. ];
  42. const arrowAngle2 = angle - arrowAngle;
  43. const arrow2 = [
  44. to[0] - size * Math.cos(arrowAngle2),
  45. to[1] - size * Math.sin(arrowAngle2),
  46. ];
  47. return [arrow1, arrow2];
  48. }
  49. /**
  50. * Draw arc by from -> to, with center and radius.
  51. * @param path
  52. * @param from
  53. * @param to
  54. * @param center
  55. * @param radius
  56. */
  57. export function appendArc(path, from, to, center, radius) {
  58. const startAngle = angle(sub(center, from)) + Math.PI;
  59. const endAngle = angle(sub(center, to)) + Math.PI;
  60. path.arc(center[0], center[1], radius, startAngle, endAngle, endAngle - startAngle < 0);
  61. return path;
  62. }
  63. /**
  64. * @todo Fix wrong key point.
  65. */
  66. export function computeGradient(C, X, Y, from = 'y', mode = 'between') {
  67. const P = from === 'y' || from === true ? Y : X;
  68. const theta = from === 'y' || from === true ? 90 : 0;
  69. const I = indexOf(P);
  70. const [min, max] = extent(I, (i) => P[i]);
  71. // This need to improve for non-uniform distributed colors.
  72. const p = new Linear({
  73. domain: [min, max],
  74. range: [0, 100],
  75. });
  76. const percentage = (i) => p.map(P[i]);
  77. const gradientMode = {
  78. // Interpolate the colors for this segment.
  79. between: (i) => `${C[i]} ${percentage(i)}%`,
  80. // Use the color of the start point as the color for this segment.
  81. start: (i) => i === 0
  82. ? `${C[i]} ${percentage(i)}%`
  83. : `${C[i - 1]} ${percentage(i)}%, ${C[i]} ${percentage(i)}%`,
  84. // Use the color of the end point as the color for this segment.
  85. end: (i) => i === C.length - 1
  86. ? `${C[i]} ${percentage(i)}%`
  87. : `${C[i]} ${percentage(i)}%, ${C[i + 1]} ${percentage(i)}%`,
  88. };
  89. const gradient = I.sort((a, b) => percentage(a) - percentage(b))
  90. .map(gradientMode[mode] || gradientMode['between'])
  91. .join(',');
  92. return `linear-gradient(${theta}deg, ${gradient})`;
  93. }
  94. export function reorder(points) {
  95. const [p0, p1, p2, p3] = points;
  96. return [p3, p0, p1, p2];
  97. }
  98. export function getArcObject(coordinate, points, Y) {
  99. const [p0, p1, , p3] = isTranspose(coordinate) ? reorder(points) : points;
  100. const [y, y1] = Y;
  101. const center = coordinate.getCenter();
  102. const a1 = angleWithQuadrant(sub(p0, center));
  103. const a2 = angleWithQuadrant(sub(p1, center));
  104. // There are two situations that a2 === a1:
  105. // 1. a1 - a2 = 0
  106. // 2. |a1 - a2| = Math.PI * 2
  107. // Distinguish them by y and y1:
  108. const a3 = a2 === a1 && y !== y1 ? a2 + Math.PI * 2 : a2;
  109. return {
  110. startAngle: a1,
  111. endAngle: a3 - a1 >= 0 ? a3 : Math.PI * 2 + a3,
  112. innerRadius: dist(p3, center),
  113. outerRadius: dist(p0, center),
  114. };
  115. }
  116. /**
  117. * Get the mark.shape's style object.
  118. * @returns
  119. */
  120. export function getShapeTheme(theme, mark, shape, defaultShape) {
  121. const { defaultColor } = theme;
  122. const markTheme = theme[mark] || {};
  123. const shapeTheme = markTheme[shape] || markTheme[defaultShape];
  124. return Object.assign({ defaultColor }, shapeTheme);
  125. }
  126. /**
  127. * Pick connectStyle from style.
  128. * @param style
  129. */
  130. export function getConnectStyle(style) {
  131. const PREFIX = 'connect';
  132. return Object.fromEntries(Object.entries(style)
  133. .filter(([key]) => key.startsWith(PREFIX))
  134. .map(([key, value]) => [
  135. lowerFirst(key.replace(PREFIX, '').trim()),
  136. value,
  137. ])
  138. .filter(([key]) => key !== undefined));
  139. }
  140. export function toOpacityKey(options) {
  141. const { colorAttribute, opacityAttribute = colorAttribute } = options;
  142. return `${opacityAttribute}Opacity`;
  143. }
  144. export function getTransform(coordinate, value) {
  145. if (!isPolar(coordinate))
  146. return '';
  147. const center = coordinate.getCenter();
  148. const { transform: suffix } = value;
  149. return `translate(${center[0]}, ${center[1]}) ${suffix || ''}`;
  150. }
  151. export function getOrigin(points) {
  152. if (points.length === 1)
  153. return points[0];
  154. const [[x0, y0], [x2, y2]] = points;
  155. return [(x0 + x2) / 2, (y0 + y2) / 2];
  156. }
  157. //# sourceMappingURL=utils.js.map