Skip to content

Commit

Permalink
feat: add 3d elements, plugin (#5597)
Browse files Browse the repository at this point in the history
* refactor: adjust exports, use enum replace const enum

* fix: fix issue that unexpected z attr

* feat(utils): add TupleMap and getCacheKey

* feat(elements): add 3d elements

* feat(plugins): add 3d light

* refactor: adjust 3d renderer

* refactor: adjust exports

* test: add demos

* chore: config packages.json, jest config

* test: fix test case

* chore: update g6-extension-3d version

* chore: setup project configs

* test: fix demo type issue

* chore: config tsconfig for type-check
  • Loading branch information
Aarebecca committed Mar 29, 2024
1 parent bcd0804 commit da5297c
Show file tree
Hide file tree
Showing 52 changed files with 1,091 additions and 57 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Expand Up @@ -43,6 +43,7 @@
"GSHAPE",
"mindmap",
"onframe",
"Phong",
"Polyline",
"ranksep"
],
Expand Down
5 changes: 4 additions & 1 deletion package.json
Expand Up @@ -42,6 +42,7 @@
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jsdom": "^23.2.0",
"lil-gui": "^0.19.2",
"limit-size": "^0.1.4",
"lint-staged": "^15.2.2",
"lodash": "^4.17.21",
Expand All @@ -54,8 +55,10 @@
"rollup": "^4.12.0",
"rollup-plugin-polyfill-node": "^0.13.0",
"rollup-plugin-visualizer": "^5.12.0",
"stats.js": "^0.17.0",
"ts-jest": "^29.1.2",
"turbo": "^1.12.4",
"typescript": "^5.3.3"
"typescript": "^5.3.3",
"vite": "^5.1.5"
}
}
4 changes: 4 additions & 0 deletions packages/g6-extension-3d/__tests__/demos/index.ts
@@ -0,0 +1,4 @@
export * from './layer-top';
export * from './position';
export * from './shapes';
export * from './solar-system';
71 changes: 71 additions & 0 deletions packages/g6-extension-3d/__tests__/demos/layer-top.ts
@@ -0,0 +1,71 @@
import type { G6Spec, GraphData } from '@antv/g6';
import { Graph, register } from '@antv/g6';
import { Light, Line3D, Plane, Sphere, renderer } from '../../src';

export const layerTop = async (context: G6Spec) => {
register('plugin', '3d-light', Light);
register('node', 'sphere', Sphere);
register('node', 'plane', Plane);
register('edge', 'line3d', Line3D);

const result = await fetch('https://assets.antv.antgroup.com/g6/3-layer-top.json');
const { nodes, edges } = await result.json();

const colors = ['rgb(240, 134, 82)', 'rgb(30, 160, 230)', 'rgb(122, 225, 116)'];
const data: GraphData = {};
data.nodes = nodes.map(({ name, pos, layer }: any) => ({
id: name,
data: { layer },
style: {
type: 'sphere',
radius: 10,
color: colors[layer - 1],
materialType: 'phong',
...pos,
},
}));

new Array(3).fill(0).forEach((_, i) => {
data.nodes!.push({
id: `plane-${i + 1}`,
style: {
type: 'plane',
size: 1000,
color: colors[i],
y: -300 + 300 * i + 10,
},
});
});

data.edges = edges.map(({ source, target }: any) => ({
source,
target,
}));

const graph = new Graph({
...context,
renderer,
data,
node: {
style: {},
},
edge: {
style: {
type: 'line3d',
lineWidth: 5,
},
},
plugins: [
{
type: '3d-light',
directional: {
direction: [0, 0, 1],
},
},
],
});

await graph.render();

return graph;
};
44 changes: 44 additions & 0 deletions packages/g6-extension-3d/__tests__/demos/position.ts
@@ -0,0 +1,44 @@
import type { G6Spec } from '@antv/g6';
import { Graph, register } from '@antv/g6';
import { Light, Line3D, Sphere, renderer } from '../../src';

export const positionValidate = async (context: G6Spec) => {
register('plugin', '3d-light', Light);
register('node', 'sphere', Sphere);
register('edge', 'line3d', Line3D);

const graph = new Graph({
...context,
renderer,
data: {
nodes: [
{ id: '1', style: { x: 100, y: 100 } },
{ id: '2', style: { x: 200, y: 200 } },
{ id: '3', style: { x: 200, y: 100, z: 150 } },
],
edges: [
{ source: '1', target: '2' },
{ source: '2', target: '3' },
{ source: '1', target: '3' },
],
},
node: {
style: { type: 'sphere', materialType: 'phong' },
},
edge: {
style: {
type: 'line3d',
},
},
plugins: [
{
type: '3d-light',
directional: {
direction: [0, 0, 1],
},
},
],
});

await graph.render();
};
59 changes: 59 additions & 0 deletions packages/g6-extension-3d/__tests__/demos/shapes.ts
@@ -0,0 +1,59 @@
import type { G6Spec } from '@antv/g6';
import { Graph, register } from '@antv/g6';
import { Capsule, Cone, Cube, Cylinder, Light, Plane, Sphere, Torus, renderer } from '../../src';

export const shapes = async (context: G6Spec) => {
register('plugin', '3d-light', Light);
register('node', 'sphere', Sphere);
register('node', 'plane', Plane);
register('node', 'cylinder', Cylinder);
register('node', 'cone', Cone);
register('node', 'cube', Cube);
register('node', 'capsule', Capsule);
register('node', 'torus', Torus);

const graph = new Graph({
...context,
renderer,
data: {
nodes: [
{
id: '1',
style: {
type: 'sphere',
texture: 'https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*cdTdTI2bNl8AAAAAAAAAAAAADmJ7AQ/original',
},
},
{ id: '2', style: { type: 'plane', size: 50 } },
{ id: '3', style: { type: 'cylinder' } },
{ id: '4', style: { type: 'cone' } },
{
id: '5',
style: {
type: 'cube',
texture: 'https://gw.alipayobjects.com/mdn/rms_6ae20b/afts/img/A*8TlCRIsKeUkAAAAAAAAAAAAAARQnAQ',
},
},
{ id: '6', style: { type: 'capsule' } },
{ id: '7', style: { type: 'torus' } },
],
},
node: {
style: {
materialType: 'phong',
x: (_, i) => 100 + (i % 5) * 100,
y: (_, i) => 100 + Math.floor(i / 5) * 100,
},
},
plugins: [
{
type: '3d-light',
directional: {
direction: [0, 0, 1],
},
},
],
});

await graph.render();
};
112 changes: 112 additions & 0 deletions packages/g6-extension-3d/__tests__/demos/solar-system.ts
@@ -0,0 +1,112 @@
import type { DisplayObject } from '@antv/g';
import type { G6Spec, Vector3 } from '@antv/g6';
import { Graph, register } from '@antv/g6';
import { Light, Sphere, renderer } from '../../src';

export const solarSystem = async (context: G6Spec) => {
register('plugin', '3d-light', Light);
register('node', 'sphere', Sphere);

const graph = new Graph({
...context,
renderer,
background: 'black',
data: {
nodes: [
{
id: 'sum',
style: {
x: 300,
y: 300,
radius: 100,
texture: 'https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*-mZfQr8LtPUAAAAAAAAAAAAADmJ7AQ/original',
},
},
{
id: 'mars',
style: {
x: 430,
y: 300,
z: 0,
radius: 20,
texture: 'https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*mniGTZktpecAAAAAAAAAAAAADmJ7AQ/original',
},
},
{
id: 'earth',
style: {
x: 500,
y: 300,
z: 0,
radius: 30,
texture: 'https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*cdTdTI2bNl8AAAAAAAAAAAAADmJ7AQ/original',
},
},
{
id: 'jupiter',
style: {
x: 600,
y: 300,
z: 0,
radius: 50,
texture: 'https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*t_mQSZYAT70AAAAAAAAAAAAADmJ7AQ/original',
},
},
],
},
node: {
style: {
type: 'sphere',
materialShininess: 0,
labelText: (d) => d.id,
},
},
plugins: [
{
type: '3d-light',
directional: {
direction: [0, 0, 1],
},
},
],
});

await graph.render();

// @ts-expect-error graph is private
const element = graph.context.element!;

const sum = element.getElement('sum')!;
const mars = element.getElement('mars')!;
const earth = element.getElement('earth')!;
const jupiter = element.getElement('jupiter')!;

const setRotation = (element: DisplayObject, speed: number) => {
setInterval(() => {
element.rotate(0, -speed, 0);
}, 30);
};
setRotation(sum, 0.1);
setRotation(mars, 0.8);
setRotation(earth, 1);
setRotation(jupiter, 0.5);

const setRevolution = (element: DisplayObject, center: Vector3, speed: number) => {
setInterval(() => {
const [x, y, z] = element.getPosition();
const [cx, cy, cz] = center;
const angle = (speed * Math.PI) / 180;

const newX = (x - cx) * Math.cos(angle) + (z - cz) * Math.sin(angle) + cx;
const newZ = -(x - cx) * Math.sin(angle) + (z - cz) * Math.cos(angle) + cz;

element.setPosition(newX, y, newZ);
}, 30);
};

setRevolution(mars, [300, 300, 0], 1.5);
setRevolution(earth, [300, 300, 0], 1);
setRevolution(jupiter, [300, 300, 0], 0.5);

return graph;
};
18 changes: 18 additions & 0 deletions packages/g6-extension-3d/__tests__/index.html
@@ -0,0 +1,18 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@antv/g6-extension-3d</title>
<style>
#container {
width: 500px;
height: 500px;
}
</style>
</head>
<body>
<div id="container"></div>
<script type="module" src="./main.ts"></script>
</body>
</html>
43 changes: 43 additions & 0 deletions packages/g6-extension-3d/__tests__/main.ts
@@ -0,0 +1,43 @@
import GUI from 'lil-gui';
import * as demos from './demos';

const demoNames = Object.keys(demos);

const options = {
demo: '',
};

const panel = new GUI({ autoPlace: true });
const __STORAGE__ = '__G6_EXTENSION_3D_DEMO__';
const load = () => {
const data = localStorage.getItem(__STORAGE__);
if (data) panel.load(JSON.parse(data));
};
const save = () => {
localStorage.setItem(__STORAGE__, JSON.stringify(panel.save()));
};
panel
.add(options, 'demo', demoNames)
.name('Demo')
.onChange((name: string) => {
render(name);
save();
});
load();

function initContainer() {
const container = document.getElementById('container')!;
container.innerHTML = '';
return container;
}

function initContext() {
const container = initContainer();
return { container };
}

function render(name: string) {
const context = initContext();
const demo = demos[name as keyof typeof demos];
demo(context);
}

0 comments on commit da5297c

Please sign in to comment.