diff --git a/__tests__/plots/api/chart-render-3d-scatter-plot-perspective.ts b/__tests__/plots/api/chart-render-3d-scatter-plot-perspective.ts
index ba43d4506a..0cfc969a6f 100644
--- a/__tests__/plots/api/chart-render-3d-scatter-plot-perspective.ts
+++ b/__tests__/plots/api/chart-render-3d-scatter-plot-perspective.ts
@@ -1,8 +1,9 @@
-import { CameraType, Canvas } from '@antv/g';
+import { CameraType } from '@antv/g';
import { Renderer as WebGLRenderer } from '@antv/g-webgl';
import { Plugin as ThreeDPlugin, DirectionalLight } from '@antv/g-plugin-3d';
import { Plugin as ControlPlugin } from '@antv/g-plugin-control';
-import { Chart } from '../../../src/api';
+import { Runtime, extend } from '../../../src/api';
+import { corelib, threedlib } from '../../../src/lib';
export function chartRender3dScatterPlotPerspective(context) {
const { container } = context;
@@ -12,61 +13,51 @@ export function chartRender3dScatterPlotPerspective(context) {
renderer.registerPlugin(new ThreeDPlugin());
renderer.registerPlugin(new ControlPlugin());
- const canvas = new Canvas({
+ const Chart = extend(Runtime, { ...corelib(), ...threedlib() });
+ const chart = new Chart({
container,
- width: 640,
- height: 480,
+ theme: 'classic',
renderer,
+ depth: 400,
});
- const camera = canvas.getCamera();
- camera.setPerspective(0.1, 5000, 45, 500 / 500);
- camera.setType(CameraType.ORBITING);
-
- // Add a directional light into scene.
- const light = new DirectionalLight({
- style: {
- intensity: 3,
- fill: 'white',
- direction: [-1, 0, 1],
- },
- });
- canvas.appendChild(light);
-
- const chart = new Chart({ theme: 'classic', container, canvas });
- chart.options({
- width: 500,
- height: 500,
- depth: 400,
- type: 'point3D',
- padding: 'auto',
- data: {
+ chart
+ .point3D()
+ .data({
type: 'fetch',
value: 'data/cars2.csv',
- },
- encode: {
- x: 'Horsepower',
- y: 'Miles_per_Gallon',
- z: 'Weight_in_lbs',
- size: 'Origin',
- color: 'Cylinders',
- shape: 'cube',
- },
- scale: {
- x: { nice: true },
- y: { nice: true },
- z: { nice: true },
- },
- coordinate: { type: 'cartesian3D' },
- axis: {
- x: { gridLineWidth: 3 },
- y: { gridLineWidth: 3, titleBillboardRotation: -Math.PI / 2 },
- z: { gridLineWidth: 3 },
- },
- legend: false,
- });
+ })
+ .encode('x', 'Horsepower')
+ .encode('y', 'Miles_per_Gallon')
+ .encode('z', 'Weight_in_lbs')
+ .encode('size', 'Origin')
+ .encode('color', 'Cylinders')
+ .encode('shape', 'cube')
+ .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();
+ 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);
+
+ // Add a directional light into scene.
+ const light = new DirectionalLight({
+ style: {
+ intensity: 3,
+ fill: 'white',
+ direction: [-1, 0, 1],
+ },
+ });
+ canvas!.appendChild(light);
+ });
return { finished };
}
diff --git a/__tests__/plots/api/chart-render-3d-scatter-plot.ts b/__tests__/plots/api/chart-render-3d-scatter-plot.ts
index 1957ef36fe..c18e81d6f7 100644
--- a/__tests__/plots/api/chart-render-3d-scatter-plot.ts
+++ b/__tests__/plots/api/chart-render-3d-scatter-plot.ts
@@ -2,7 +2,8 @@ import { CameraType } from '@antv/g';
import { Renderer as WebGLRenderer } from '@antv/g-webgl';
import { Plugin as ThreeDPlugin, DirectionalLight } from '@antv/g-plugin-3d';
import { Plugin as ControlPlugin } from '@antv/g-plugin-control';
-import { Chart } from '../../../src/api';
+import { Runtime, extend } from '../../../src/api';
+import { corelib, threedlib } from '../../../src/lib';
export function chartRender3dScatterPlot(context) {
const { container } = context;
@@ -12,6 +13,7 @@ export function chartRender3dScatterPlot(context) {
renderer.registerPlugin(new ThreeDPlugin());
renderer.registerPlugin(new ControlPlugin());
+ const Chart = extend(Runtime, { ...corelib(), ...threedlib() });
const chart = new Chart({
container,
theme: 'classic',
diff --git a/site/.dumirc.ts b/site/.dumirc.ts
index 2b06c20e15..e696183678 100644
--- a/site/.dumirc.ts
+++ b/site/.dumirc.ts
@@ -215,6 +215,14 @@ export default defineConfig({
},
order: 14,
},
+ {
+ slug: 'spec/3d',
+ title: {
+ zh: '3D 图表 - 3D Charts',
+ en: '3D',
+ },
+ order: 15,
+ },
{
slug: 'spec/theme',
title: {
diff --git a/site/docs/api/chart.zh.md b/site/docs/api/chart.zh.md
index 042c3cfcda..9e6daffec5 100644
--- a/site/docs/api/chart.zh.md
+++ b/site/docs/api/chart.zh.md
@@ -37,6 +37,7 @@ chart.render();
| container | 指定 chart 绘制的 DOM,可以传入 DOM id,也可以直接传入 dom 实例 | `string \| HTMLElement` | |
| width | 图表宽度 | `number` | 640 |
| height | 图表高度 | `number` | 480 |
+| depth | 图表深度,在 3D 图表中使用 | `number` | 0 |
| renderer | 指定渲染引擎,默认使用 canvas。 | | |
| plugins | 指定渲染时使用的插件 ,具体见 [plugin](/api/plugin/rough) | `any[]` | |
| autoFit | 图表是否自适应容器宽高,默认为 `false`,用户需要手动设置 `width` 和 `height`。
当 `autoFit: true` 时,会自动取图表容器的宽高,如果用户设置了 `height`,那么会以用户设置的 `height` 为准。 | `boolean` | false |
@@ -196,7 +197,7 @@ chart.render();
### `chart.point3D`
-添加 point3D 图形,具体见 [mark](/spec/mark/point3D)。
+添加 point3D 图形,具体见 [3d](/spec/3d/point3-d)。
## 设置属性
diff --git a/site/docs/manual/extra-topics/3d-charts.en.md b/site/docs/manual/extra-topics/3d-charts.en.md
index f040cd421c..195d7d6f86 100644
--- a/site/docs/manual/extra-topics/3d-charts.en.md
+++ b/site/docs/manual/extra-topics/3d-charts.en.md
@@ -1,5 +1,5 @@
---
-title: 3D Charts
+title: Use 3D Charts
order: 11
---
diff --git a/site/docs/manual/extra-topics/3d-charts.zh.md b/site/docs/manual/extra-topics/3d-charts.zh.md
index 8bb2981ba6..a3374f20f2 100644
--- a/site/docs/manual/extra-topics/3d-charts.zh.md
+++ b/site/docs/manual/extra-topics/3d-charts.zh.md
@@ -1,11 +1,12 @@
---
-title: 3D 图表
+title: 绘制 3D 图表
order: 11
---
以 3D 散点图为例,创建图表需要以下步骤:
- 创建 WebGL 渲染器和插件
+- 扩展 threedlib
- 设置 z 通道、比例尺和坐标轴
- 在场景中设置相机
- 添加光源
@@ -18,7 +19,7 @@ order: 11
首先安装依赖:
```bash
-$ npm install @antv/g-webgl @antv/g-plugin-3d @antv/g-plugin-control --save;
+$ npm install @antv/g-webgl @antv/g-plugin-3d @antv/g-plugin-control --save
```
然后使用 [@antv/g-webgl](https://g.antv.antgroup.com/api/renderer/webgl) 作为渲染器并注册以下两个插件:
@@ -36,6 +37,16 @@ renderer.registerPlugin(new ThreeDPlugin());
renderer.registerPlugin(new ControlPlugin());
```
+## 扩展 threedlib
+
+由于 3D 相关的功能代码体积巨大,我们将其分离到 `threedlib` 中,在运行时扩展它并自定义 Chart 对象:
+
+```ts
+import { Runtime, corelib, threedlib, extend } from '@antv/g2';
+
+const Chart = extend(Runtime, { ...corelib(), ...threedlib() });
+```
+
## 设置 z 通道、比例尺和坐标轴
在创建 Chart 时通过 `depth` 指定深度:
@@ -49,7 +60,7 @@ const chart = new Chart({
});
```
-我们使用 [point3D](/spec/mark/point3-d) Mark 并选择 cube 作为 shape 进行绘制。
+我们使用 [point3D](/spec/3d/point3-d) Mark 并选择 cube 作为 shape 进行绘制。
随后设置 z 通道、比例尺和坐标轴。
```ts
@@ -92,7 +103,62 @@ chart.render().then(() => {
效果如下:
-
+```js | ob { pin: false }
+(() => {
+ 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,
+ depth: 400,
+ });
+
+ chart
+ .point3D()
+ .data({
+ type: 'fetch',
+ value:
+ 'https://gw.alipayobjects.com/os/bmw-prod/2c813e2d-2276-40b9-a9af-cf0a0fb7e942.csv',
+ })
+ .encode('x', 'Horsepower')
+ .encode('y', 'Miles_per_Gallon')
+ .encode('z', 'Weight_in_lbs')
+ .encode('color', 'Cylinders')
+ .encode('shape', 'cube')
+ .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);
+
+ // Add a directional light into scene.
+ const light = new gPlugin3d.DirectionalLight({
+ style: {
+ intensity: 3,
+ fill: 'white',
+ direction: [-1, 0, 1],
+ },
+ });
+ canvas.appendChild(light);
+ });
+
+ return chart.getContainer();
+})();
+```
我们还可以让相机固定视点进行一定角度的旋转,这里使用了 [rotate](https://g.antv.antgroup.com/api/camera/action#rotate):
@@ -100,7 +166,62 @@ chart.render().then(() => {
camera.rotate(-20, -20, 0);
```
-
+```js | ob { pin: false }
+(() => {
+ 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,
+ depth: 400,
+ });
+
+ chart
+ .point3D()
+ .data({
+ type: 'fetch',
+ value:
+ 'https://gw.alipayobjects.com/os/bmw-prod/2c813e2d-2276-40b9-a9af-cf0a0fb7e942.csv',
+ })
+ .encode('x', 'Horsepower')
+ .encode('y', 'Miles_per_Gallon')
+ .encode('z', 'Weight_in_lbs')
+ .encode('color', 'Cylinders')
+ .encode('shape', 'cube')
+ .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.setType(g.CameraType.ORBITING);
+ camera.rotate(-20, -20, 0);
+
+ // Add a directional light into scene.
+ const light = new gPlugin3d.DirectionalLight({
+ style: {
+ intensity: 3,
+ fill: 'white',
+ direction: [-1, 0, 1],
+ },
+ });
+ canvas.appendChild(light);
+ });
+
+ return chart.getContainer();
+})();
+```
## 添加光源
@@ -121,10 +242,6 @@ canvas.appendChild(light);
## 使用相机交互
-3D 场景下的交互和 2D 场景有很大的不同,[g-plugin-control](https://g.antv.antgroup.com/plugins/control) 提供了 3D 场景下基于相机的交互。当我们拖拽画布时,会控制相机绕视点进行旋转操作,而鼠标滚轮的缩放会让相机进行 dolly 操作:
-
-
-
-需要注意的是缩放操作在正交投影下是没有效果的,但旋转操作依然有效:
+3D 场景下的交互和 2D 场景有很大的不同,[g-plugin-control](https://g.antv.antgroup.com/plugins/control) 提供了 3D 场景下基于相机的交互。当我们拖拽画布时,会控制相机绕视点进行旋转操作,而鼠标滚轮的缩放会让相机进行 dolly 操作。
-
+需要注意的是缩放操作在正交投影下是没有效果的,但旋转操作依然有效。
diff --git a/site/docs/manual/introduction/why-g2.zh.md b/site/docs/manual/introduction/why-g2.zh.md
index 56b99d08bd..ed80f9cc78 100644
--- a/site/docs/manual/introduction/why-g2.zh.md
+++ b/site/docs/manual/introduction/why-g2.zh.md
@@ -686,7 +686,7 @@ import { Runtime, corelib, extend } from '@antv/g2';
// 基于 corelib 对 Runtime 进行扩展
// 1. 增加类型(如果使用的 TypeScript)
// 2. 增加 Mark
-const Chart = extend(Runtime, corelib);
+const Chart = extend(Runtime, { ...corelib() });
const chart = new Chart({ container: 'container' });
diff --git a/site/docs/spec/3d/point3D.en.md b/site/docs/spec/3d/point3D.en.md
new file mode 100644
index 0000000000..896c822015
--- /dev/null
+++ b/site/docs/spec/3d/point3D.en.md
@@ -0,0 +1,6 @@
+---
+title: point3D
+order: 1
+---
+
+
diff --git a/site/docs/spec/mark/point3D.zh.md b/site/docs/spec/3d/point3D.zh.md
similarity index 95%
rename from site/docs/spec/mark/point3D.zh.md
rename to site/docs/spec/3d/point3D.zh.md
index 9a212a9c6f..3656c475df 100644
--- a/site/docs/spec/mark/point3D.zh.md
+++ b/site/docs/spec/3d/point3D.zh.md
@@ -21,12 +21,13 @@ import { CameraType } from '@antv/g';
import { Renderer as WebGLRenderer } from '@antv/g-webgl';
import { Plugin as ThreeDPlugin, DirectionalLight } from '@antv/g-plugin-3d';
import { Plugin as ControlPlugin } from '@antv/g-plugin-control';
-import { Chart } from '@antv/g2';
+import { Runtime, corelib, threedlib, extend } from '@antv/g2';
const renderer = new WebGLRenderer();
renderer.registerPlugin(new ThreeDPlugin());
renderer.registerPlugin(new ControlPlugin());
+const Chart = extend(Runtime, { ...corelib(), ...threedlib() });
const chart = new Chart({
container: 'container',
theme: 'classic',
@@ -61,9 +62,6 @@ chart.render().then(() => {
const camera = canvas.getCamera();
camera.setType(CameraType.ORBITING);
- // TODO: infer by depth in layout process.
- canvas.document.documentElement.translate(0, 0, -200);
-
// Add a directional light into scene.
const light = new DirectionalLight({
style: {
diff --git a/site/docs/spec/coordinate/cartesian3D.en.md b/site/docs/spec/coordinate/cartesian3D.en.md
new file mode 100644
index 0000000000..4865c88f91
--- /dev/null
+++ b/site/docs/spec/coordinate/cartesian3D.en.md
@@ -0,0 +1,6 @@
+---
+title: cartesian3D
+order: 5
+---
+
+
diff --git a/site/docs/spec/coordinate/cartesian3D.zh.md b/site/docs/spec/coordinate/cartesian3D.zh.md
new file mode 100644
index 0000000000..6062942265
--- /dev/null
+++ b/site/docs/spec/coordinate/cartesian3D.zh.md
@@ -0,0 +1,50 @@
+---
+title: cartesian3D
+order: 5
+---
+
+在 2D 笛卡尔坐标系基础上,通过增加 Z 轴扩展而来。[示例](/manual/extra-topics/3d-charts)
+
+## 开始使用
+
+
+
+```js
+import { Runtime, corelib, threedlib, extend } from '@antv/g2';
+
+const Chart = extend(Runtime, { ...corelib(), ...threedlib() });
+
+const chart = new Chart({
+ container: 'container',
+ theme: 'classic',
+ renderer,
+ depth: 400,
+});
+
+chart.coordinate({
+ type: 'cartesian3D',
+});
+
+chart
+ .point3D()
+ .data({
+ type: 'fetch',
+ value:
+ 'https://gw.alipayobjects.com/os/bmw-prod/2c813e2d-2276-40b9-a9af-cf0a0fb7e942.csv',
+ })
+ .encode('x', 'Horsepower')
+ .encode('y', 'Miles_per_Gallon')
+ .encode('z', 'Weight_in_lbs')
+ .encode('size', 'Origin')
+ .encode('color', 'Cylinders')
+ .encode('shape', 'cube')
+ .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();
+```
diff --git a/site/docs/spec/mark/point3D.en.md b/site/docs/spec/mark/point3D.en.md
deleted file mode 100644
index 7a82fe06b5..0000000000
--- a/site/docs/spec/mark/point3D.en.md
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: point
-order: 1
----
-
-
diff --git a/site/examples/3d/scatter/demo/orthographic-projection.ts b/site/examples/3d/scatter/demo/orthographic-projection.ts
index 19fef5a054..2583d096e4 100644
--- a/site/examples/3d/scatter/demo/orthographic-projection.ts
+++ b/site/examples/3d/scatter/demo/orthographic-projection.ts
@@ -2,13 +2,15 @@ import { CameraType } from '@antv/g';
import { Renderer as WebGLRenderer } from '@antv/g-webgl';
import { Plugin as ThreeDPlugin, DirectionalLight } from '@antv/g-plugin-3d';
import { Plugin as ControlPlugin } from '@antv/g-plugin-control';
-import { Chart } from '@antv/g2';
+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',
diff --git a/site/examples/3d/scatter/demo/perspective-projection.ts b/site/examples/3d/scatter/demo/perspective-projection.ts
index 291d7ec5d6..b858addcaa 100644
--- a/site/examples/3d/scatter/demo/perspective-projection.ts
+++ b/site/examples/3d/scatter/demo/perspective-projection.ts
@@ -2,13 +2,15 @@ import { CameraType } from '@antv/g';
import { Renderer as WebGLRenderer } from '@antv/g-webgl';
import { Plugin as ThreeDPlugin, DirectionalLight } from '@antv/g-plugin-3d';
import { Plugin as ControlPlugin } from '@antv/g-plugin-control';
-import { Chart } from '@antv/g2';
+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',
@@ -40,6 +42,7 @@ chart
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);
diff --git a/site/examples/3d/scatter/demo/sphere-shape.ts b/site/examples/3d/scatter/demo/sphere-shape.ts
index e1a51aa55d..f7d68853ef 100644
--- a/site/examples/3d/scatter/demo/sphere-shape.ts
+++ b/site/examples/3d/scatter/demo/sphere-shape.ts
@@ -2,13 +2,15 @@ import { CameraType } from '@antv/g';
import { Renderer as WebGLRenderer } from '@antv/g-webgl';
import { Plugin as ThreeDPlugin, DirectionalLight } from '@antv/g-plugin-3d';
import { Plugin as ControlPlugin } from '@antv/g-plugin-control';
-import { Chart } from '@antv/g2';
+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',
diff --git a/src/api/utils.ts b/src/api/utils.ts
index 193e1aa8ba..262c02ef39 100644
--- a/src/api/utils.ts
+++ b/src/api/utils.ts
@@ -64,7 +64,7 @@ export function valueOf(node: Node): Record {
export function sizeOf(options, container) {
const { autoFit } = options;
if (autoFit) return getContainerSize(container);
- const { width = 640, height = 480, depth = 640 } = options;
+ const { width = 640, height = 480, depth = 0 } = options;
return { width, height, depth };
}
diff --git a/src/index.ts b/src/index.ts
index fe46b86196..33eae8c927 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -15,7 +15,7 @@ export {
AREA_CLASS_NAME,
} from './runtime';
-export { corelib, stdlib, litelib } from './lib';
+export { corelib, stdlib, litelib, threedlib } from './lib';
export * from './mark';
diff --git a/src/lib/std.ts b/src/lib/std.ts
index b62a19d3f1..a1138b557a 100644
--- a/src/lib/std.ts
+++ b/src/lib/std.ts
@@ -2,14 +2,12 @@ import { corelib } from './core';
import { geolib } from './geo';
import { graphlib } from './graph';
import { plotlib } from './plot';
-import { threedlib } from './threed';
export function stdlib() {
return {
...geolib(),
...graphlib(),
...plotlib(),
- ...threedlib(),
...corelib(),
} as const;
}