stackY.js 3.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import { deepMix } from '@antv/util';
  2. import { column, columnOf, inferredColumn, maybeColumnOf, } from './utils/helper';
  3. import { normalizeComparator, createGroups, applyOrder } from './utils/order';
  4. /**
  5. * The stack transform group marks into series by color channel,
  6. * and then produce new y channel for each series by specified order,
  7. * say to form vertical "stacks" by specified channels.
  8. */
  9. export const StackY = (options = {}) => {
  10. const { groupBy = 'x', orderBy = null, reverse = false, y: fromY = 'y', y1: fromY1 = 'y1', series = true, } = options;
  11. return (I, mark) => {
  12. const { data, encode, style = {} } = mark;
  13. const [Y, fy] = columnOf(encode, 'y');
  14. const [Y1, fy1] = columnOf(encode, 'y1');
  15. const [S] = series
  16. ? maybeColumnOf(encode, 'series', 'color')
  17. : columnOf(encode, 'color');
  18. // Create groups and apply specified order for each group.
  19. const groups = createGroups(groupBy, I, mark);
  20. const createComparator = normalizeComparator(orderBy);
  21. const comparator = createComparator(data, Y, S);
  22. if (comparator)
  23. applyOrder(groups, comparator);
  24. // Stack y channels to produce new y and y1 channel.
  25. const newY = new Array(I.length);
  26. const newY1 = new Array(I.length);
  27. const TY = new Array(I.length);
  28. const F = [];
  29. const L = [];
  30. for (const G of groups) {
  31. if (reverse)
  32. G.reverse();
  33. // For range interval with specified y and y1.
  34. const start = Y1 ? +Y1[G[0]] : 0;
  35. // Split positive indices of Y and negative Y.
  36. const PG = [];
  37. const NG = [];
  38. for (const i of G) {
  39. const y = (TY[i] = +Y[i] - start);
  40. if (y < 0)
  41. NG.push(i);
  42. else if (y >= 0)
  43. PG.push(i);
  44. }
  45. // Store the first and last layer.
  46. const FG = PG.length > 0 ? PG : NG;
  47. const LG = NG.length > 0 ? NG : PG;
  48. let i = PG.length - 1;
  49. let j = 0;
  50. // Find the last non-zero index.
  51. while (i > 0 && Y[FG[i]] === 0)
  52. i--;
  53. // Find the first non-zero index.
  54. while (j < LG.length - 1 && Y[LG[j]] === 0)
  55. j++;
  56. F.push(FG[i]);
  57. L.push(LG[j]);
  58. // Stack negative y in reverse order.
  59. let ny = start;
  60. for (const i of NG.reverse()) {
  61. const y = TY[i];
  62. ny = newY[i] = (newY1[i] = ny) + y;
  63. }
  64. // Stack positive y in input order.
  65. let py = start;
  66. for (const i of PG) {
  67. const y = TY[i];
  68. if (y > 0)
  69. py = newY[i] = (newY1[i] = py) + y;
  70. else
  71. newY[i] = newY1[i] = py;
  72. }
  73. }
  74. // Only set top radius for the first layer,
  75. // and set bottom radius for the last layer.
  76. const FS = new Set(F);
  77. const LS = new Set(L);
  78. // Choose new y or y1 channel as the new y channel.
  79. const V = fromY === 'y' ? newY : newY1;
  80. const V1 = fromY1 === 'y' ? newY : newY1;
  81. return [
  82. I,
  83. deepMix({}, mark, {
  84. encode: {
  85. y0: inferredColumn(Y, fy),
  86. y: column(V, fy),
  87. y1: column(V1, fy1),
  88. },
  89. style: Object.assign({ first: (_, i) => FS.has(i), last: (_, i) => LS.has(i) }, style),
  90. }),
  91. ];
  92. };
  93. };
  94. StackY.props = {};
  95. //# sourceMappingURL=stackY.js.map