Skip to content

Commit

Permalink
feat: 3d line plot (#5471)
Browse files Browse the repository at this point in the history
* feat: 3d line plot

* chore: update line3d examples

* fix: use lambert material instead of phong
  • Loading branch information
xiaoiver authored Aug 30, 2023
1 parent ad175a0 commit 4d157cd
Show file tree
Hide file tree
Showing 22 changed files with 492 additions and 9 deletions.
66 changes: 66 additions & 0 deletions __tests__/plots/api/chart-render-3d-line-plot-perspective.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { CameraType } from '@antv/g';
import { Renderer as WebGLRenderer } from '@antv/g-webgl';
import { Plugin as ThreeDPlugin } from '@antv/g-plugin-3d';
import { Plugin as ControlPlugin } from '@antv/g-plugin-control';
import { Runtime, extend } from '../../../src/api';
import { corelib, threedlib } from '../../../src/lib';

export function chartRender3dLinePlotPerspective(context) {
const { container } = context;

// Create a WebGL renderer.
const renderer = new WebGLRenderer();
renderer.registerPlugin(new ThreeDPlugin());
renderer.registerPlugin(new ControlPlugin());

const Chart = extend(Runtime, { ...corelib(), ...threedlib() });
const chart = new Chart({
container,
renderer,
depth: 400,
});

/**
* 3D Spiral
* @see https://plotly.com/javascript/3d-line-plots/
*/
const pointCount = 500;
let r: number;
const data: { x: number; y: number; z: number }[] = [];

for (let i = 0; i < pointCount; i++) {
r = i * (pointCount - i);
data.push({
x: r * Math.cos(i / 30),
y: r * Math.sin(i / 30),
z: i,
});
}

chart
.line3D()
.data(data)
.encode('x', 'x')
.encode('y', 'y')
.encode('z', 'z')
.encode('size', 4)
.coordinate({ type: 'cartesian3D' })
.scale('x', { nice: true })
.scale('y', { nice: true })
.scale('z', { nice: true })
.legend(false)
.axis('x', { gridLineWidth: 2 })
.axis('y', { gridLineWidth: 2, titleBillboardRotation: -Math.PI / 2 })
.axis('z', { gridLineWidth: 2 });

const finished = chart.render().then(() => {
const { canvas } = chart.getContext();
const camera = canvas!.getCamera();
camera.setPerspective(0.1, 5000, 45, 500 / 500);
camera.setType(CameraType.ORBITING);
});

return { finished };
}

chartRender3dLinePlotPerspective.skip = true;
66 changes: 66 additions & 0 deletions __tests__/plots/api/chart-render-3d-line-plot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { CameraType } from '@antv/g';
import { Renderer as WebGLRenderer } from '@antv/g-webgl';
import { Plugin as ThreeDPlugin } from '@antv/g-plugin-3d';
import { Plugin as ControlPlugin } from '@antv/g-plugin-control';
import { Runtime, extend } from '../../../src/api';
import { corelib, threedlib } from '../../../src/lib';

export function chartRender3dLinePlot(context) {
const { container } = context;

// Create a WebGL renderer.
const renderer = new WebGLRenderer();
renderer.registerPlugin(new ThreeDPlugin());
renderer.registerPlugin(new ControlPlugin());

const Chart = extend(Runtime, { ...corelib(), ...threedlib() });
const chart = new Chart({
container,
renderer,
depth: 400,
});

/**
* 3D Line
* @see https://plotly.com/javascript/3d-line-plots/
*/
const pointCount = 31;
let r: number;
const data: { x: number; y: number; z: number }[] = [];

for (let i = 0; i < pointCount; i++) {
r = 10 * Math.cos(i / 10);
data.push({
x: r * Math.cos(i),
y: r * Math.sin(i),
z: i,
});
}

chart
.line3D()
.data(data)
.encode('x', 'x')
.encode('y', 'y')
.encode('z', 'z')
.encode('size', 4)
.coordinate({ type: 'cartesian3D' })
.scale('x', { nice: true })
.scale('y', { nice: true })
.scale('z', { nice: true })
.legend(false)
.axis('x', { gridLineWidth: 2 })
.axis('y', { gridLineWidth: 2, titleBillboardRotation: -Math.PI / 2 })
.axis('z', { gridLineWidth: 2 });

const finished = chart.render().then(() => {
const { canvas } = chart.getContext();
const camera = canvas!.getCamera();
camera.setType(CameraType.ORBITING);
camera.rotate(-20, -20, 0);
});

return { finished };
}

chartRender3dLinePlot.skip = true;
1 change: 0 additions & 1 deletion __tests__/plots/api/chart-render-3d-scatter-plot-legend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ export function chartRender3dScatterPlotLegend(context) {
const Chart = extend(Runtime, { ...corelib(), ...threedlib() });
const chart = new Chart({
container,

renderer,
depth: 400,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ export function chartRender3dScatterPlotPerspective(context) {
const Chart = extend(Runtime, { ...corelib(), ...threedlib() });
const chart = new Chart({
container,

renderer,
depth: 400,
});
Expand Down
1 change: 0 additions & 1 deletion __tests__/plots/api/chart-render-3d-scatter-plot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ export function chartRender3dScatterPlot(context) {
const Chart = extend(Runtime, { ...corelib(), ...threedlib() });
const chart = new Chart({
container,

renderer,
depth: 400,
});
Expand Down
2 changes: 2 additions & 0 deletions __tests__/plots/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,5 @@ export { chartRenderEvent } from './chart-render-event';
export { chartRender3dScatterPlot } from './chart-render-3d-scatter-plot';
export { chartRender3dScatterPlotPerspective } from './chart-render-3d-scatter-plot-perspective';
export { chartRender3dScatterPlotLegend } from './chart-render-3d-scatter-plot-legend';
export { chartRender3dLinePlot } from './chart-render-3d-line-plot';
export { chartRender3dLinePlotPerspective } from './chart-render-3d-line-plot-perspective';
3 changes: 2 additions & 1 deletion __tests__/unit/lib/threed.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { threedlib } from '../../../src/lib';
import { Cartesian3D } from '../../../src/coordinate';
import { AxisZ } from '../../../src/component';
import { Point3D } from '../../../src/mark';
import { Point3D, Line3D } from '../../../src/mark';

describe('threedlib', () => {
it('threedlib() should returns expected threed components.', () => {
expect(threedlib()).toEqual({
'coordinate.cartesian3D': Cartesian3D,
'component.axisZ': AxisZ,
'mark.point3D': Point3D,
'mark.line3D': Line3D,
});
});
});
4 changes: 4 additions & 0 deletions site/docs/api/chart.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,10 @@ chart.render();

添加 point3D 图形,具体见 [3d](/spec/threed/point-threed)

### `chart.line3D`

添加 line3D 图形,具体见 [3d](/spec/threed/line-threed)

## 设置属性

### `chart.width`
Expand Down
6 changes: 6 additions & 0 deletions site/docs/spec/threed/lineThreed.en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
title: line3D
order: 2
---

<embed src="@/docs/spec/threed/lineThreed.zh.md"></embed>
74 changes: 74 additions & 0 deletions site/docs/spec/threed/lineThreed.zh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
title: line3D
order: 2
---

主要用于绘制 3D 折线图。

## 开始使用

首先需要使用 [@antv/g-webgl](https://g.antv.antgroup.com/api/renderer/webgl) 作为渲染器并注册以下两个插件:

- [g-plugin-3d](https://g.antv.antgroup.com/plugins/3d) 提供 3D 场景下的几何、材质和光照
- [g-plugin-control](https://g.antv.antgroup.com/plugins/control) 提供 3D 场景下的相机交互

然后设置 z 通道、scale 和 z 坐标轴。无需在场景中添加光源。

```js | ob
(() => {
const renderer = new gWebgl.Renderer();
renderer.registerPlugin(new gPluginControl.Plugin());
renderer.registerPlugin(new gPlugin3d.Plugin());

const Chart = G2.extend(G2.Runtime, { ...G2.corelib(), ...G2.threedlib() });

// 初始化图表实例
const chart = new Chart({
theme: 'classic',
renderer,
width: 500,
height: 500,
depth: 400,
});

const pointCount = 31;
let r;
const data = [];

for (let i = 0; i < pointCount; i++) {
r = 10 * Math.cos(i / 10);
data.push({
x: r * Math.cos(i),
y: r * Math.sin(i),
z: i,
});
}

chart
.line3D()
.data(data)
.encode('x', 'x')
.encode('y', 'y')
.encode('z', 'z')
.encode('size', 4)
.coordinate({ type: 'cartesian3D' })
.scale('x', { nice: true })
.scale('y', { nice: true })
.scale('z', { nice: true })
.legend(false)
.axis('x', { gridLineWidth: 2 })
.axis('y', { gridLineWidth: 2, titleBillboardRotation: -Math.PI / 2 })
.axis('z', { gridLineWidth: 2 });

chart.render().then(() => {
const { canvas } = chart.getContext();
const camera = canvas.getCamera();
camera.setPerspective(0.1, 5000, 45, 500 / 500);
camera.setType(g.CameraType.ORBITING);
});

return chart.getContainer();
})();
```

更多的案例,可以查看[图表示例](/examples)页面。
24 changes: 24 additions & 0 deletions site/examples/threed/line/demo/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"title": {
"zh": "中文分类",
"en": "Category"
},
"demos": [
{
"filename": "polyline.ts",
"title": {
"zh": "折线",
"en": "Polyline"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*0MEPQrNRlvoAAAAAAAAAAAAADmJ7AQ/original"
},
{
"filename": "spiral.ts",
"title": {
"zh": "螺旋线",
"en": "Spiral"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*Ak1iTZ1dpI0AAAAAAAAAAAAADmJ7AQ/original"
}
]
}
60 changes: 60 additions & 0 deletions site/examples/threed/line/demo/polyline.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { CameraType } from '@antv/g';
import { Renderer as WebGLRenderer } from '@antv/g-webgl';
import { Plugin as ThreeDPlugin } from '@antv/g-plugin-3d';
import { Plugin as ControlPlugin } from '@antv/g-plugin-control';
import { Runtime, corelib, threedlib, extend } from '@antv/g2';

// Create a WebGL renderer.
const renderer = new WebGLRenderer();
renderer.registerPlugin(new ThreeDPlugin());
renderer.registerPlugin(new ControlPlugin());

// Customize our own Chart with threedlib.
const Chart = extend(Runtime, { ...corelib(), ...threedlib() });
const chart = new Chart({
container: 'container',
theme: 'classic',
renderer,
depth: 400, // Define the depth of chart.
});

/**
* 3D Line
* @see https://plotly.com/javascript/3d-line-plots/
*/
const pointCount = 31;
let r;
const data = [];

for (let i = 0; i < pointCount; i++) {
r = 10 * Math.cos(i / 10);
data.push({
x: r * Math.cos(i),
y: r * Math.sin(i),
z: i,
});
}

chart
.line3D()
.data(data)
.encode('x', 'x')
.encode('y', 'y')
.encode('z', 'z')
.encode('size', 4)
.coordinate({ type: 'cartesian3D' })
.scale('x', { nice: true })
.scale('y', { nice: true })
.scale('z', { nice: true })
.legend(false)
.axis('x', { gridLineWidth: 2 })
.axis('y', { gridLineWidth: 2, titleBillboardRotation: -Math.PI / 2 })
.axis('z', { gridLineWidth: 2 });

chart.render().then(() => {
const { canvas } = chart.getContext();
const camera = canvas.getCamera();
// Use perspective projection mode.
camera.setPerspective(0.1, 5000, 45, 640 / 480);
camera.setType(CameraType.ORBITING);
});
Loading

0 comments on commit 4d157cd

Please sign in to comment.