From 4e08bf6a747fe78bcab20063f0873bf24a31cabe Mon Sep 17 00:00:00 2001
From: Runtus <893119806@qq.com>
Date: Mon, 11 Dec 2023 19:54:15 +0800
Subject: [PATCH 1/4] fix: fix the tooltip shown on one element line chart
---
.../plots/api/chart-tooltip-one-element.ts | 30 +++++++++++++++++++
__tests__/plots/api/index.ts | 1 +
2 files changed, 31 insertions(+)
create mode 100644 __tests__/plots/api/chart-tooltip-one-element.ts
diff --git a/__tests__/plots/api/chart-tooltip-one-element.ts b/__tests__/plots/api/chart-tooltip-one-element.ts
new file mode 100644
index 0000000000..6d0d5163ad
--- /dev/null
+++ b/__tests__/plots/api/chart-tooltip-one-element.ts
@@ -0,0 +1,30 @@
+import { Chart } from '../../../src';
+
+export function chartTooltipOneElement(context) {
+ const { container } = context;
+ const chart = new Chart({
+ container,
+ });
+
+ chart
+ .line()
+ .data({
+ value: [
+ {
+ date: '2007-04-23T00:00:00.000Z',
+ close: 93.24,
+ },
+ ],
+ })
+ .encode('x', 'date')
+ .encode('y', 'close')
+ .encode('size', 10)
+ .interaction('tooltip', { mount: 'body' })
+ .label({
+ text: 'close',
+ });
+
+ const finished = chart.render();
+
+ return { chart, finished };
+}
diff --git a/__tests__/plots/api/index.ts b/__tests__/plots/api/index.ts
index 04c71a7d57..1f53152472 100644
--- a/__tests__/plots/api/index.ts
+++ b/__tests__/plots/api/index.ts
@@ -48,3 +48,4 @@ export { chartOptionsCallbackChildren } from './chart-options-callback-children'
export { chartAutoFitSlider } from './chart-auto-fit-slider';
export { chart3d } from './chart-3d';
export { chartOnScrollbarFilter } from './chart-on-scrollbar-filter';
+export { chartTooltipOneElement } from './chart-tooltip-one-element';
From d3c1110a8d2cd4f4ec08131fde5849950d50d2b1 Mon Sep 17 00:00:00 2001
From: Runtus <893119806@qq.com>
Date: Tue, 12 Dec 2023 11:36:53 +0800
Subject: [PATCH 2/4] fix: change test examples
---
.../plots/api/chart-tooltip-one-element.ts | 30 -------------------
__tests__/plots/api/index.ts | 1 -
2 files changed, 31 deletions(-)
delete mode 100644 __tests__/plots/api/chart-tooltip-one-element.ts
diff --git a/__tests__/plots/api/chart-tooltip-one-element.ts b/__tests__/plots/api/chart-tooltip-one-element.ts
deleted file mode 100644
index 6d0d5163ad..0000000000
--- a/__tests__/plots/api/chart-tooltip-one-element.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { Chart } from '../../../src';
-
-export function chartTooltipOneElement(context) {
- const { container } = context;
- const chart = new Chart({
- container,
- });
-
- chart
- .line()
- .data({
- value: [
- {
- date: '2007-04-23T00:00:00.000Z',
- close: 93.24,
- },
- ],
- })
- .encode('x', 'date')
- .encode('y', 'close')
- .encode('size', 10)
- .interaction('tooltip', { mount: 'body' })
- .label({
- text: 'close',
- });
-
- const finished = chart.render();
-
- return { chart, finished };
-}
diff --git a/__tests__/plots/api/index.ts b/__tests__/plots/api/index.ts
index 1f53152472..04c71a7d57 100644
--- a/__tests__/plots/api/index.ts
+++ b/__tests__/plots/api/index.ts
@@ -48,4 +48,3 @@ export { chartOptionsCallbackChildren } from './chart-options-callback-children'
export { chartAutoFitSlider } from './chart-auto-fit-slider';
export { chart3d } from './chart-3d';
export { chartOnScrollbarFilter } from './chart-on-scrollbar-filter';
-export { chartTooltipOneElement } from './chart-tooltip-one-element';
From 2530443666566b416dfb1df9df0a3c3c79628a96 Mon Sep 17 00:00:00 2001
From: Runtus <893119806@qq.com>
Date: Thu, 21 Dec 2023 11:01:20 +0800
Subject: [PATCH 3/4] feat: rebase v5
---
.../titanicPointPackSharedDataPadding.svg | 16538 ++++++++++++++++
.../static/titanicPointPackSharedRowData.svg | 16538 ++++++++++++++++
__tests__/plots/static/index.ts | 2 +
.../titanic-point-pack-shared-data-padding.ts | 41 +
.../titanic-point-pack-shared-row-data.ts | 41 +
site/docs/spec/transform/pack.zh.md | 7 +
src/runtime/plot.ts | 3 +-
src/spec/transform.ts | 2 +
src/transform/pack.ts | 21 +-
9 files changed, 33183 insertions(+), 10 deletions(-)
create mode 100644 __tests__/integration/snapshots/static/titanicPointPackSharedDataPadding.svg
create mode 100644 __tests__/integration/snapshots/static/titanicPointPackSharedRowData.svg
create mode 100644 __tests__/plots/static/titanic-point-pack-shared-data-padding.ts
create mode 100644 __tests__/plots/static/titanic-point-pack-shared-row-data.ts
diff --git a/__tests__/integration/snapshots/static/titanicPointPackSharedDataPadding.svg b/__tests__/integration/snapshots/static/titanicPointPackSharedDataPadding.svg
new file mode 100644
index 0000000000..0cbc949789
--- /dev/null
+++ b/__tests__/integration/snapshots/static/titanicPointPackSharedDataPadding.svg
@@ -0,0 +1,16538 @@
+
\ No newline at end of file
diff --git a/__tests__/integration/snapshots/static/titanicPointPackSharedRowData.svg b/__tests__/integration/snapshots/static/titanicPointPackSharedRowData.svg
new file mode 100644
index 0000000000..38e691e394
--- /dev/null
+++ b/__tests__/integration/snapshots/static/titanicPointPackSharedRowData.svg
@@ -0,0 +1,16538 @@
+
\ No newline at end of file
diff --git a/__tests__/plots/static/index.ts b/__tests__/plots/static/index.ts
index e60774b7f4..55aeaeb419 100644
--- a/__tests__/plots/static/index.ts
+++ b/__tests__/plots/static/index.ts
@@ -311,3 +311,5 @@ export { premierLeagueTable } from './premier-league-table';
export { singlePointBasic } from './single-point-basic';
export { populationFlowChordDefault } from './population-flow-chord-default';
export { aaplLineRrangeXY } from './aapl-line-rangeXY';
+export { titanicPointPackSharedRowData } from './titanic-point-pack-shared-row-data';
+export { titanicPointPackSharedDataPadding } from './titanic-point-pack-shared-data-padding';
diff --git a/__tests__/plots/static/titanic-point-pack-shared-data-padding.ts b/__tests__/plots/static/titanic-point-pack-shared-data-padding.ts
new file mode 100644
index 0000000000..bb8ef58e65
--- /dev/null
+++ b/__tests__/plots/static/titanic-point-pack-shared-data-padding.ts
@@ -0,0 +1,41 @@
+import { G2Spec } from '../../../src';
+
+export function titanicPointPackSharedDataPadding(): G2Spec {
+ return {
+ type: 'facetRect',
+ data: {
+ type: 'fetch',
+ value: 'data/titanic.csv',
+ transform: [
+ {
+ type: 'sortBy',
+ fields: ['survived'],
+ },
+ {
+ type: 'map',
+ callback: ({ survived, ...d }) => ({
+ ...d,
+ survived: survived + '',
+ }),
+ },
+ ],
+ },
+ shareData: true,
+ encode: {
+ x: 'pclass',
+ },
+ children: [
+ {
+ type: 'point',
+ transform: [{ type: 'pack', padding: 2 }],
+ legend: {
+ color: { labelFormatter: (d) => (d === '1' ? 'Yes' : 'No') },
+ },
+ encode: {
+ color: 'survived',
+ shape: 'point',
+ },
+ },
+ ],
+ };
+}
diff --git a/__tests__/plots/static/titanic-point-pack-shared-row-data.ts b/__tests__/plots/static/titanic-point-pack-shared-row-data.ts
new file mode 100644
index 0000000000..bed12f4cc8
--- /dev/null
+++ b/__tests__/plots/static/titanic-point-pack-shared-row-data.ts
@@ -0,0 +1,41 @@
+import { G2Spec } from '../../../src';
+
+export function titanicPointPackSharedRowData(): G2Spec {
+ return {
+ type: 'facetRect',
+ data: {
+ type: 'fetch',
+ value: 'data/titanic.csv',
+ transform: [
+ {
+ type: 'sortBy',
+ fields: ['survived'],
+ },
+ {
+ type: 'map',
+ callback: ({ survived, ...d }) => ({
+ ...d,
+ survived: survived + '',
+ }),
+ },
+ ],
+ },
+ shareData: true,
+ encode: {
+ x: 'pclass',
+ },
+ children: [
+ {
+ type: 'point',
+ transform: [{ type: 'pack', direction: 'row' }],
+ legend: {
+ color: { labelFormatter: (d) => (d === '1' ? 'Yes' : 'No') },
+ },
+ encode: {
+ color: 'survived',
+ shape: 'point',
+ },
+ },
+ ],
+ };
+}
diff --git a/site/docs/spec/transform/pack.zh.md b/site/docs/spec/transform/pack.zh.md
index 51233a196a..d5fe07703d 100644
--- a/site/docs/spec/transform/pack.zh.md
+++ b/site/docs/spec/transform/pack.zh.md
@@ -52,3 +52,10 @@ facetRect
chart.render();
```
+
+## 选项
+
+| 属性 | 描述 | 类型 | 默认值 |
+|-------------------|------------------------------------------------|---------------------|-----------------------|
+| padding | 每个元素之间的间距,单位为px | `number` | `0` |
+| direction | 元素的堆叠方向 | `'row' \| 'col'` | `col` |
\ No newline at end of file
diff --git a/src/runtime/plot.ts b/src/runtime/plot.ts
index e7edabdc03..83e53a446d 100644
--- a/src/runtime/plot.ts
+++ b/src/runtime/plot.ts
@@ -739,6 +739,7 @@ function initializeState(
// This is for unit visualization to sync data domain.
dataDomain,
modifier,
+ transform,
key: markKey,
} = mark;
const { index, channels, tooltip } = state;
@@ -761,7 +762,7 @@ function initializeState(
calcPoints(index, markScaleInstance, value, coordinate),
);
const count = dataDomain || I.length;
- const T = modifier ? modifier(P, count, layout) : [];
+ const T = modifier ? modifier(P, count, layout, transform) : [];
const titleOf = (i) => tooltip.title?.[i]?.value;
const itemsOf = (i) => tooltip.items.map((V) => V[i]);
const visualData: Record[] = I.map((d, i) => {
diff --git a/src/spec/transform.ts b/src/spec/transform.ts
index 5245ef6d62..6ea95f2911 100644
--- a/src/spec/transform.ts
+++ b/src/spec/transform.ts
@@ -223,6 +223,8 @@ export type FlexXTransform = {
export type PackTransform = {
type?: 'pack';
+ padding?: number;
+ direction?: 'row' | 'col';
};
export type Reducer =
diff --git a/src/transform/pack.ts b/src/transform/pack.ts
index 3e03c9f452..59e0553900 100644
--- a/src/transform/pack.ts
+++ b/src/transform/pack.ts
@@ -1,10 +1,14 @@
import { deepMix } from '@antv/util';
import { TransformComponent as TC } from '../runtime';
import { calcBBox } from '../utils/vector';
+import { PackTransform } from '../spec';
-export type PackOptions = Record;
+export type PackOptions = Omit;
+
+function modifier(P, count, layout, transform) {
+ const style = transform.find((item) => item.type === 'pack');
+ const { padding = 0, direction = 'col' } = style;
-function modifier(P, count, layout) {
const pcount = P.length;
if (pcount === 0) return [];
@@ -37,20 +41,19 @@ function modifier(P, count, layout) {
return P.map((points, m) => {
const [x, y, width, height] = calcBBox(points);
-
- const i = m % col;
- const j = Math.floor(m / col);
+ const i = direction === 'col' ? m % col : Math.floor(m / row);
+ const j = direction === 'col' ? Math.floor(m / col) : m % row;
const newX = i * size;
const newY = (row - j - 1) * size + space;
- const sx = size / width;
- const sy = size / height;
+ const sx = (size - padding) / width;
+ const sy = (size - padding) / height;
// Translate the shape and mark to make sure the center of
// shape is overlap before and after scale transformation.
- const tx = newX - x + offsetX * i;
- const ty = newY - y - intervalY * j - offsetY;
+ const tx = newX - x + offsetX * i + (1 / 2) * padding;
+ const ty = newY - y - intervalY * j - offsetY + (1 / 2) * padding;
return `translate(${tx}, ${ty}) scale(${sx}, ${sy})`;
});
}
From f105c2495b8a77f57161127ed1890da00bf9e2ca Mon Sep 17 00:00:00 2001
From: Runtus <893119806@qq.com>
Date: Thu, 21 Dec 2023 13:17:11 +0800
Subject: [PATCH 4/4] fix: code style
---
src/runtime/plot.ts | 3 +-
src/transform/pack.ts | 95 ++++++++++++++++++++++---------------------
2 files changed, 50 insertions(+), 48 deletions(-)
diff --git a/src/runtime/plot.ts b/src/runtime/plot.ts
index 83e53a446d..e7edabdc03 100644
--- a/src/runtime/plot.ts
+++ b/src/runtime/plot.ts
@@ -739,7 +739,6 @@ function initializeState(
// This is for unit visualization to sync data domain.
dataDomain,
modifier,
- transform,
key: markKey,
} = mark;
const { index, channels, tooltip } = state;
@@ -762,7 +761,7 @@ function initializeState(
calcPoints(index, markScaleInstance, value, coordinate),
);
const count = dataDomain || I.length;
- const T = modifier ? modifier(P, count, layout, transform) : [];
+ const T = modifier ? modifier(P, count, layout) : [];
const titleOf = (i) => tooltip.title?.[i]?.value;
const itemsOf = (i) => tooltip.items.map((V) => V[i]);
const visualData: Record[] = I.map((d, i) => {
diff --git a/src/transform/pack.ts b/src/transform/pack.ts
index 59e0553900..f9100fe348 100644
--- a/src/transform/pack.ts
+++ b/src/transform/pack.ts
@@ -5,57 +5,60 @@ import { PackTransform } from '../spec';
export type PackOptions = Omit;
-function modifier(P, count, layout, transform) {
- const style = transform.find((item) => item.type === 'pack');
- const { padding = 0, direction = 'col' } = style;
+function pack(options: PackOptions) {
+ const { padding = 0, direction = 'col' } = options;
+ return (P, count, layout) => {
+ const pcount = P.length;
+ if (pcount === 0) return [];
- const pcount = P.length;
- if (pcount === 0) return [];
+ // col * row >= count
+ // row is close to col * aspect, so
+ // col * (col * aspect) >= count
+ const { innerWidth, innerHeight } = layout;
+ const aspect = innerHeight / innerWidth;
+ let col = Math.ceil(Math.sqrt(count / aspect));
- // col * row >= count
- // row is close to col * aspect, so
- // col * (col * aspect) >= count
- const { innerWidth, innerHeight } = layout;
- const aspect = innerHeight / innerWidth;
- let col = Math.ceil(Math.sqrt(count / aspect));
+ // Increase col to avoid total height of packed shape
+ // being large than height of bbox.
+ let size = innerWidth / col;
+ let row = Math.ceil(count / col);
+ let h0 = row * size;
+ while (h0 > innerHeight) {
+ col = col + 1;
+ size = innerWidth / col;
+ row = Math.ceil(count / col);
+ h0 = row * size;
+ }
- // Increase col to avoid total height of packed shape
- // being large than height of bbox.
- let size = innerWidth / col;
- let row = Math.ceil(count / col);
- let h0 = row * size;
- while (h0 > innerHeight) {
- col = col + 1;
- size = innerWidth / col;
- row = Math.ceil(count / col);
- h0 = row * size;
- }
+ // Some offset to increase the space usage.
+ const space = innerHeight - row * size;
+ const intervalY = row <= 1 ? 0 : space / (row - 1);
+ const [offsetX, offsetY] =
+ row <= 1
+ ? [
+ (innerWidth - pcount * size) / (pcount - 1),
+ (innerHeight - size) / 2,
+ ]
+ : [0, 0];
- // Some offset to increase the space usage.
- const space = innerHeight - row * size;
- const intervalY = row <= 1 ? 0 : space / (row - 1);
- const [offsetX, offsetY] =
- row <= 1
- ? [(innerWidth - pcount * size) / (pcount - 1), (innerHeight - size) / 2]
- : [0, 0];
+ return P.map((points, m) => {
+ const [x, y, width, height] = calcBBox(points);
+ const i = direction === 'col' ? m % col : Math.floor(m / row);
+ const j = direction === 'col' ? Math.floor(m / col) : m % row;
- return P.map((points, m) => {
- const [x, y, width, height] = calcBBox(points);
- const i = direction === 'col' ? m % col : Math.floor(m / row);
- const j = direction === 'col' ? Math.floor(m / col) : m % row;
+ const newX = i * size;
+ const newY = (row - j - 1) * size + space;
- const newX = i * size;
- const newY = (row - j - 1) * size + space;
+ const sx = (size - padding) / width;
+ const sy = (size - padding) / height;
- const sx = (size - padding) / width;
- const sy = (size - padding) / height;
-
- // Translate the shape and mark to make sure the center of
- // shape is overlap before and after scale transformation.
- const tx = newX - x + offsetX * i + (1 / 2) * padding;
- const ty = newY - y - intervalY * j - offsetY + (1 / 2) * padding;
- return `translate(${tx}, ${ty}) scale(${sx}, ${sy})`;
- });
+ // Translate the shape and mark to make sure the center of
+ // shape is overlap before and after scale transformation.
+ const tx = newX - x + offsetX * i + (1 / 2) * padding;
+ const ty = newY - y - intervalY * j - offsetY + (1 / 2) * padding;
+ return `translate(${tx}, ${ty}) scale(${sx}, ${sy})`;
+ });
+ };
}
/**
@@ -63,9 +66,9 @@ function modifier(P, count, layout, transform) {
* @todo Improve or change algorithm to increase space usage.
* @todo Take some special case into account.
*/
-export const Pack: TC = () => {
+export const Pack: TC = (options) => {
return (I, mark) => {
- return [I, deepMix({}, mark, { modifier, axis: false })];
+ return [I, deepMix({}, mark, { modifier: pack(options), axis: false })];
};
};