From 91844fd0621918a4dfefe43af7e8cf8bd7e53579 Mon Sep 17 00:00:00 2001 From: Timo Suoranta Date: Mon, 4 Dec 2023 15:19:49 +0800 Subject: [PATCH] feat: add plane-node for v5 (#5189) * feat: add planeNode --------- Co-authored-by: TT --- packages/g6/src/stdlib/index.ts | 2 + packages/g6/src/stdlib/item/node/index.ts | 1 + packages/g6/src/stdlib/item/node/plane.ts | 105 ++++ packages/g6/src/util/shape3d.ts | 14 +- .../docs/apis/shape/PlaneGeometryProps.en.md | 50 +- .../docs/apis/shape/PlaneGeometryProps.zh.md | 51 +- .../feature/features/demo/layerTopology.js | 558 ++++++++++++++++++ .../examples/feature/features/demo/meta.json | 8 + 8 files changed, 775 insertions(+), 14 deletions(-) create mode 100644 packages/g6/src/stdlib/item/node/plane.ts create mode 100644 packages/site/examples/feature/features/demo/layerTopology.js diff --git a/packages/g6/src/stdlib/index.ts b/packages/g6/src/stdlib/index.ts index 0dd4010ef11..77491dc4fdf 100644 --- a/packages/g6/src/stdlib/index.ts +++ b/packages/g6/src/stdlib/index.ts @@ -34,6 +34,7 @@ const { ModelRectNode, ImageNode, CubeNode, + PlaneNode, BaseNode, BaseNode3D, } = Nodes; @@ -255,6 +256,7 @@ const Extensions = { EllipseNode, ModelRectNode, CubeNode, + PlaneNode, BaseNode, BaseNode3D, // edges diff --git a/packages/g6/src/stdlib/item/node/index.ts b/packages/g6/src/stdlib/item/node/index.ts index 6ba37632328..67b2951a9f5 100644 --- a/packages/g6/src/stdlib/item/node/index.ts +++ b/packages/g6/src/stdlib/item/node/index.ts @@ -10,5 +10,6 @@ export * from './diamond'; export * from './modelRect'; export * from './image'; export * from './cube'; +export * from './plane'; export * from './base'; export * from './base3d'; diff --git a/packages/g6/src/stdlib/item/node/plane.ts b/packages/g6/src/stdlib/item/node/plane.ts new file mode 100644 index 00000000000..3fffd31687f --- /dev/null +++ b/packages/g6/src/stdlib/item/node/plane.ts @@ -0,0 +1,105 @@ +import { DisplayObject } from '@antv/g'; +import { NodeDisplayModel } from '../../../types'; +import { State } from '../../../types/item'; +import { + NodeModelData, + NodeShapeMap, + NodeShapeStyles, +} from '../../../types/node'; +import { BaseNode3D } from './base3d'; + +export class PlaneNode extends BaseNode3D { + override defaultStyles = { + keyShape: { + width: 100, + depth: 100, + materialType: 'basic', + materialProps: { + wireframe: false, + wireframeColor: 'black', + cullMode: 0, + }, + x: 0, + y: 0, + z: 0, + }, + }; + mergedStyles: NodeShapeStyles; + constructor(props) { + super(props); + } + public draw( + model: NodeDisplayModel, + shapeMap: NodeShapeMap, + diffData?: { previous: NodeModelData; current: NodeModelData }, + diffState?: { previous: State[]; current: State[] }, + ): NodeShapeMap { + const { data = {} } = model; + + let shapes: NodeShapeMap = { keyShape: undefined }; + + // keyShape + shapes.keyShape = this.drawKeyShape(model, shapeMap, diffData); + + // haloShape + if (data.haloShape && this.drawHaloShape) { + shapes.haloShape = this.drawHaloShape(model, shapeMap, diffData); + } + + // labelShape + if (data.labelShape) { + shapes.labelShape = this.drawLabelShape(model, shapeMap, diffData); + } + + // labelBackgroundShape + if (data.labelBackgroundShape) { + shapes.labelBackgroundShape = this.drawLabelBackgroundShape( + model, + shapeMap, + diffData, + ); + } + + // iconShape + if (data.iconShape) { + shapes.iconShape = this.drawIconShape(model, shapeMap, diffData); + } + + // badgeShape + if (data.badgeShapes) { + const badgeShapes = this.drawBadgeShapes( + model, + shapeMap, + diffData, + diffState, + ); + shapes = { + ...shapes, + ...badgeShapes, + }; + } + + // otherShapes + if (data.otherShapes && this.drawOtherShapes) { + shapes = { + ...shapes, + ...this.drawOtherShapes(model, shapeMap, diffData), + }; + } + return shapes; + } + + public drawKeyShape( + model: NodeDisplayModel, + shapeMap: NodeShapeMap, + diffData?: { previous: NodeModelData; current: NodeModelData }, + diffState?: { previous: State[]; current: State[] }, + ): DisplayObject { + return this.upsertShape( + 'plane', + 'keyShape', + this.mergedStyles.keyShape, + shapeMap, + ); + } +} diff --git a/packages/g6/src/util/shape3d.ts b/packages/g6/src/util/shape3d.ts index 383a43a5aed..86debba9460 100644 --- a/packages/g6/src/util/shape3d.ts +++ b/packages/g6/src/util/shape3d.ts @@ -33,7 +33,7 @@ export const createShape3D = ( } // materialType: 'lambert' | 'phong' | 'basic', TODO: type - const { materialType = 'lambert', ...otherStyles } = style as any; + const { materialType = 'lambert', materialProps = {}, ...otherStyles } = style as any; if (!device.GeometryCache) { device.GeometryCache = {}; } @@ -59,21 +59,23 @@ export const createShape3D = ( case 'basic': device.MaterialCache[materialType as string] = new MeshBasicMaterial( device, + materialProps ); break; case 'phong': { - const materialProps = { - shininess: 30, - }; device.MaterialCache[materialType as string] = new MeshPhongMaterial( device, - materialProps, + { + shininess: 30, + ...materialProps + } ); } case 'lambert': default: { device.MaterialCache[materialType as string] = new MeshLambertMaterial( device, + materialProps ); break; } @@ -104,7 +106,7 @@ export const createShape3D = ( ]); break; case 'plane': - shape.scale([style.width / GEOMETRY_SIZE, style.depth / GEOMETRY_SIZE]); + shape.scale(style.width / GEOMETRY_SIZE, 1, style.depth / GEOMETRY_SIZE); break; case 'sphere': default: { diff --git a/packages/site/docs/apis/shape/PlaneGeometryProps.en.md b/packages/site/docs/apis/shape/PlaneGeometryProps.en.md index 73b0870e2de..7f921568f7c 100644 --- a/packages/site/docs/apis/shape/PlaneGeometryProps.en.md +++ b/packages/site/docs/apis/shape/PlaneGeometryProps.en.md @@ -11,7 +11,7 @@ Plane geometry, default lying on the XZ plane **Type**: `number` -**Default**: `0` +**Default**: `100` width @@ -19,7 +19,7 @@ width **Type**: `number` -**Default**: `0` +**Default**: `100` depth @@ -27,7 +27,7 @@ depth **Type**: `number` -**Default**: `1` +**Default**: `5` width segments @@ -35,6 +35,48 @@ width segments **Type**: `number` -**Default**: `1` +**Default**: `5` depth segments + +## materialType + +**Type**:`'basic' | 'phong' | 'lambert'` + +**Default**:`basic` + +material type + +## materialProps material-related parameters + +### wireframe + +**Type**:`boolean` + +**Default**:`false` + +Enable wireframe,Commonly used to visually display triangular surfaces + +### wireframeColor + +**Type**:`string` + +**Default**:`black` + +After enabling wireframe, you can specify a color, which defaults to 'black' + +### wireframeLineWidth + +**Type**:`number` + +**Default**:`1` + +After enabling wireframe, you can specify the line width, which defaults to 1 + +### cullMode + +**Type**:`number` + +**Default**:`0` + +Turn on face removal, default to 0, which means no removal. 1 is front removal, 2 is back removal, and 3 is front and back removal diff --git a/packages/site/docs/apis/shape/PlaneGeometryProps.zh.md b/packages/site/docs/apis/shape/PlaneGeometryProps.zh.md index f49a1b3b782..5a4cf300e91 100644 --- a/packages/site/docs/apis/shape/PlaneGeometryProps.zh.md +++ b/packages/site/docs/apis/shape/PlaneGeometryProps.zh.md @@ -11,7 +11,7 @@ order: 11 **类型**:`number` -**默认值**:`0` +**默认值**:`100` 宽度 @@ -19,7 +19,7 @@ order: 11 **类型**:`number` -**默认值**:`0` +**默认值**:`100` 深度 @@ -27,7 +27,7 @@ order: 11 **类型**:`number` -**默认值**:`1` +**默认值**:`5` 宽度分段数 @@ -35,6 +35,49 @@ order: 11 **类型**:`number` -**默认值**:`1` +**默认值**:`5` 深度分段数 + +## materialType + +**类型**:`'basic' | 'phong' | 'lambert'` + +**默认值**:`basic` + +材质类型 + +## materialProps 材质相关属性 + +### wireframe + +**类型**:`boolean` + +**默认值**:`false` + +是否绘制 wireframe,常用于直观展示三角面 + +### wireframeColor + +**类型**:`string` + +**默认值**:`black` + +开启 wireframe 后可指定颜色,默认为 'black' + +### wireframeLineWidth + +**类型**:`number` + +**默认值**:`1` + +开启 wireframe 后可指定线宽,默认为 1 + +### cullMode + +**类型**:`number` + +**默认值**:`0` + +开启 面剔除,默认为 0,即不剔除,1 为正面剔除,2 为背面剔除, 3 为正背面剔除 + diff --git a/packages/site/examples/feature/features/demo/layerTopology.js b/packages/site/examples/feature/features/demo/layerTopology.js new file mode 100644 index 00000000000..d280e66e9e0 --- /dev/null +++ b/packages/site/examples/feature/features/demo/layerTopology.js @@ -0,0 +1,558 @@ +import {Graph, Extensions, extend} from '@antv/g6'; + +const ExtGraph = extend(Graph, { + nodes: { + 'sphere-node': Extensions.SphereNode, + 'cube-node': Extensions.CubeNode, + 'plane-node': Extensions.PlaneNode, + }, + behaviors: { + 'orbit-canvas-3d': Extensions.OrbitCanvas3D, + 'zoom-canvas-3d': Extensions.ZoomCanvas3D, + }, +}); + +const planeGroup = [] +const colors = ['rgb(240, 134, 82)', 'rgb(30, 160, 230)', 'rgb(122, 225, 116)'] +const sphereR = 15 +for (let i = 0; i < 3; i++) { + const plane = { + id: `plane-${i}`, + data: { + type: 'plane-node', + x: 0, + y: -300 + 300 * i + sphereR, + z: 0, + keyShape: { + fill: colors[i], + width: 10000, + depth: 10000, + materialProps: { + wireframe: true, + cullMode: 0, + wireframeColor: '#797979', + } + }, + }, + } + planeGroup.push(plane) +} + + +const nodes = [ + { + "name": 1, + "pos": { + "x": 61.21392822265625, + "y": 300, + "z": -264.4036865234375 + }, + "layer": 3 + }, + { + "name": 2, + "pos": { + "x": -255.1844024658203, + "y": 300, + "z": -216.97540283203125 + }, + "layer": 3 + }, + { + "name": 3, + "pos": { + "x": -91.655029296875, + "y": 300, + "z": -125.21270751953125 + }, + "layer": 3 + }, + { + "name": 4, + "pos": { + "x": -39.05999755859375, + "y": 300, + "z": 88.867919921875 + }, + "layer": 3 + }, + { + "name": 5, + "pos": { + "x": -50.56976318359375, + "y": 300, + "z": -209.14599609375 + }, + "layer": 3 + }, + { + "name": 6, + "pos": { + "x": 282.10833740234375, + "y": 300, + "z": 24.2239990234375 + }, + "layer": 3 + }, + { + "name": 7, + "pos": { + "x": 97.94744873046875, + "y": 300, + "z": -63.923828125 + }, + "layer": 3 + }, + { + "name": 8, + "pos": { + "x": -212.96136474609375, + "y": 300, + "z": 193.93087768554688 + }, + "layer": 3 + }, + { + "name": 9, + "pos": { + "x": 46.8973388671875, + "y": 300, + "z": 271.3457794189453 + }, + "layer": 3 + }, + { + "name": 10, + "pos": { + "x": 280.34747314453125, + "y": 300, + "z": 226.57763671875 + }, + "layer": 3 + }, + { + "name": 11, + "pos": { + "x": -15.212249755859375, + "y": 0, + "z": 120.65432739257812 + }, + "layer": 2 + }, + { + "name": 12, + "pos": { + "x": 183.6497802734375, + "y": 0, + "z": 73.28384399414062 + }, + "layer": 2 + }, + { + "name": 13, + "pos": { + "x": -297.0749969482422, + "y": 0, + "z": 308.8816833496094 + }, + "layer": 2 + }, + { + "name": 14, + "pos": { + "x": -217.73849487304688, + "y": 0, + "z": 117.55078125 + }, + "layer": 2 + }, + { + "name": 19, + "pos": { + "x": 202.20880126953125, + "y": 0, + "z": -40.325439453125 + }, + "layer": 2 + }, + { + "name": 20, + "pos": { + "x": -34.244873046875, + "y": 0, + "z": -127.05670166015625 + }, + "layer": 2 + }, + { + "name": 15, + "pos": { + "x": 59.89288330078125, + "y": 0, + "z": -303.281494140625 + }, + "layer": 2 + }, + { + "name": 16, + "pos": { + "x": -160.287841796875, + "y": 0, + "z": -23.3919677734375 + }, + "layer": 2 + }, + { + "name": 17, + "pos": { + "x": -94.52520751953125, + "y": 0, + "z": 307.85633850097656 + }, + "layer": 2 + }, + { + "name": 18, + "pos": { + "x": 221.6644287109375, + "y": 0, + "z": 164.96102905273438 + }, + "layer": 2 + }, + { + "name": 21, + "pos": { + "x": 49.6173095703125, + "y": -300, + "z": 22.866851806640625 + }, + "layer": 1 + }, + { + "name": 22, + "pos": { + "x": 19.21929931640625, + "y": -300, + "z": -178.12158203125 + }, + "layer": 1 + }, + { + "name": 23, + "pos": { + "x": -100.36669921875, + "y": -300, + "z": 160.96636962890625 + }, + "layer": 1 + }, + { + "name": 26, + "pos": { + "x": 189.1593017578125, + "y": -300, + "z": -298.3272705078125 + }, + "layer": 1 + }, + { + "name": 27, + "pos": { + "x": -68.7808837890625, + "y": -300, + "z": 36.220458984375 + }, + "layer": 1 + }, + { + "name": 28, + "pos": { + "x": 279.00189208984375, + "y": -300, + "z": 93.63009643554688 + }, + "layer": 1 + }, + { + "name": 30, + "pos": { + "x": -269.5784606933594, + "y": -300, + "z": 244.6395263671875 + }, + "layer": 1 + }, + { + "name": 34, + "pos": { + "x": 205.86956787109375, + "y": -300, + "z": -96.7197265625 + }, + "layer": 1 + }, + { + "name": 35, + "pos": { + "x": -267.73089599609375, + "y": -300, + "z": 43.14031982421875 + }, + "layer": 1 + } +] +const edges = [ + { + "source": 1, + "target": 12, + "layer": 4, + }, + { + "source": 5, + "target": 18, + "layer": 4 + }, + { + "source": 6, + "target": 13, + "layer": 4 + }, + { + "source": 7, + "target": 14, + "layer": 4 + }, + { + "source": 5, + "target": 19, + "layer": 4 + }, + { + "source": 15, + "target": 22, + "layer": 4 + }, + { + "source": 16, + "target": 23, + "layer": 4 + }, + { + "source": 17, + "target": 27, + "layer": 4 + }, + { + "source": 18, + "target": 21, + "layer": 4 + }, + { + "source": 19, + "target": 26, + "layer": 4 + }, + { + "source": 3, + "target": 1, + "layer": 1 + }, + { + "source": 5, + "target": 2, + "layer": 1 + }, + { + "source": 7, + "target": 3, + "layer": 1 + }, + { + "source": 7, + "target": 5, + "layer": 1 + }, + { + "source": 7, + "target": 4, + "layer": 1 + }, + { + "source": 7, + "target": 6, + "layer": 1 + }, + { + "source": 4, + "target": 8, + "layer": 1 + }, + { + "source": 4, + "target": 9, + "layer": 1 + }, + { + "source": 6, + "target": 10, + "layer": 1 + }, + { + "source": 11, + "target": 12, + "layer": 2 + }, + { + "source": 11, + "target": 17, + "layer": 2 + }, + { + "source": 11, + "target": 16, + "layer": 2 + }, + { + "source": 13, + "target": 17, + "layer": 2 + }, + { + "source": 18, + "target": 19, + "layer": 2 + }, + { + "source": 15, + "target": 20, + "layer": 2 + }, + { + "source": 11, + "target": 14, + "layer": 2 + }, + { + "source": 21, + "target": 22, + "layer": 3 + }, + { + "source": 21, + "target": 23, + "layer": 3 + }, + { + "source": 22, + "target": 34, + "layer": 3 + }, + { + "source": 23, + "target": 35, + "layer": 3 + }, + { + "source": 35, + "target": 30, + "layer": 3 + }, + { + "source": 35, + "target": 27, + "layer": 3 + }, + { + "source": 34, + "target": 26, + "layer": 3 + }, + { + "source": 34, + "target": 28, + "layer": 3 + } +] + +edges.forEach((edge, index) => { + edge.id = `edge${index}` +}) + +nodes.forEach((node) => { + node.id = node.name; + node.data = { + type: 'sphere-node', + x: node.pos.x, + y: node.pos.y, + z: node.pos.z, + keyShape: { + fill: colors[node.layer - 1], + r: sphereR, + }, + } +}) + + +const data = { + nodes: [ + ...planeGroup, + ...nodes, + ], + edges: edges, +}; + +const container = document.getElementById('container'); +const width = container.scrollWidth; +const height = container.scrollHeight || 500; + + +const graph = new ExtGraph({ + container: 'container', + width, + height, + renderer: 'webgl-3d', + modes: { + default: [ + { + type: 'orbit-canvas-3d', + trigger: 'drag', + }, + 'zoom-canvas-3d', + ], + }, + theme: { + type: 'spec', + base: 'dark', + }, + data, + edge: { + keyShape: { + lineWidth: 2, + stroke: '#f6e432', // 边主图形描边颜色 + }, + }, +}); + + + +graph.on('afterlayout', () => { + const canvas = graph.canvas + const camera = canvas.getCamera(); + camera.createLandmark('reset', { + position: [0, -400, 2000], + focalPoint: [0, 0, 0], + zoom: 1, + }); + camera.gotoLandmark('reset', { + duration: 1000, + easing: 'ease-in', + onfinish: () => { + canvas.addEventListener("afterrender", () => { + graph.canvas.getRoot().rotate(0, 0.3, 0); + // graph.itemController.nodeGroup.rotate(0, 0.3, 0); + // graph.itemController.edgeGroup.rotate(0, 0.3, 0); + }); + } + }); +}); diff --git a/packages/site/examples/feature/features/demo/meta.json b/packages/site/examples/feature/features/demo/meta.json index 1c2c78e89e0..5f4770af7ca 100644 --- a/packages/site/examples/feature/features/demo/meta.json +++ b/packages/site/examples/feature/features/demo/meta.json @@ -75,6 +75,14 @@ "en": "WASM Layout" }, "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*4_4oRaN8hLcAAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "layerTopology.js", + "title": { + "zh": "三层拓扑模型", + "en": "Three Layers Topology Model" + }, + "screenshot": "" } ] }