Loading...
BubbleSets 插件通过创建气泡形状来表示集合及其关系,帮助用户直观地理解集合间的交集、并集等逻辑关系。它是一种增强数据可视化效果的工具,特别适用于展示复杂的数据集合关系。
BubbleSets 插件主要适用于以下场景:
以下是一个简单的 BubbleSets 插件初始化示例:
const graph = new Graph({plugins: [{type: 'bubble-sets',members: ['node1', 'node2'], // 需要包裹的节点 ID 列表label: true, // 是否显示标签},],});
createGraph({autoFit: 'center',data: {nodes: [{id: 'node-0',data: { cluster: 'a' },style: { x: 555, y: 151 },},{id: 'node-1',data: { cluster: 'a' },style: { x: 532, y: 323 },},{id: 'node-2',data: { cluster: 'a' },style: { x: 473, y: 227 },},{id: 'node-3',data: { cluster: 'a' },style: { x: 349, y: 212 },},{id: 'node-4',data: { cluster: 'b' },style: { x: 234, y: 201 },},{id: 'node-5',data: { cluster: 'b' },style: { x: 338, y: 333 },},{id: 'node-6',data: { cluster: 'b' },style: { x: 365, y: 91 },},],edges: [{id: 'edge-0',source: 'node-0',target: 'node-2',},{id: 'edge-1',source: 'node-1',target: 'node-2',},{id: 'edge-2',source: 'node-2',target: 'node-3',},{id: 'edge-3',source: 'node-3',target: 'node-4',},{id: 'edge-4',source: 'node-3',target: 'node-5',},{id: 'edge-5',source: 'node-3',target: 'node-6',},],},node: {style: { labelText: (d) => d.id },palette: { field: 'cluster', color: ['#7e3feb', '#ffa940'] },},behaviors: ['drag-canvas', 'drag-element'],plugins: ['grid-line',{type: 'bubble-sets',key: 'bubble-sets',members: ['node-0', 'node-1', 'node-2', 'node-3'],labelText: 'bubblesets-a',fill: '#7e3feb',fillOpacity: 0.1,stroke: '#7e3feb',strokeOpacity: 1,labelFill: '#fff',labelPadding: 2,labelBackgroundFill: '#7e3feb',labelBackgroundRadius: 5,},],},{ width: 600, height: 450 },(gui, graph) => {const options = {type: 'bubble-sets',members: ['node-0', 'node-1', 'node-2', 'node-3'],avoidMembers: [],// stylefill: '#7e3feb',fillOpacity: 0.1,stroke: '#7e3feb',strokeOpacity: 1,// labellabel: true,labelCloseToPath: true,labelAutoRotate: true,labelOffsetX: 0,labelOffsetY: 0,labelPlacement: 'bottom',// bubblesetsmaxRoutingIterations: 100,maxMarchingIterations: 20,pixelGroup: 4,edgeR0: 10,edgeR1: 20,nodeR0: 15,nodeR1: 50,morphBuffer: 10,threshold: 1,memberInfluenceFactor: 1,edgeInfluenceFactor: 1,nonMemberInfluenceFactor: -0.8,virtualEdges: true,};const optionFolder = gui.addFolder('Bubblesets Options');optionFolder.add(options, 'type').disable();optionFolder.addColor(options, 'fill');optionFolder.addColor(options, 'stroke');optionFolder.add(options, 'fillOpacity', 0, 1, 0.1);optionFolder.add(options, 'strokeOpacity', 0, 1, 0.1);optionFolder.add(options, 'label');optionFolder.add(options, 'labelCloseToPath');optionFolder.add(options, 'labelAutoRotate');optionFolder.add(options, 'labelOffsetX', 0, 20, 1);optionFolder.add(options, 'labelOffsetY', 0, 20, 1);optionFolder.add(options, 'labelPlacement', ['left', 'right', 'top', 'bottom', 'center']);optionFolder.add(options, 'maxRoutingIterations', 0, 200, 1);optionFolder.add(options, 'maxMarchingIterations', 0, 40, 1);optionFolder.add(options, 'pixelGroup', 0, 20, 1);optionFolder.add(options, 'edgeR0', 0, 50, 1);optionFolder.add(options, 'edgeR1', 0, 50, 1);optionFolder.add(options, 'nodeR0', 0, 50, 1);optionFolder.add(options, 'nodeR1', 0, 50, 1);optionFolder.add(options, 'morphBuffer', 0, 20, 1);optionFolder.add(options, 'threshold', -1, 1, 0.1);optionFolder.add(options, 'memberInfluenceFactor', -1, 1, 0.1);optionFolder.add(options, 'edgeInfluenceFactor', -1, 1, 0.1);optionFolder.add(options, 'nonMemberInfluenceFactor', -1, 1, 0.1);optionFolder.add(options, 'virtualEdges');optionFolder.onChange(({ property, value }) => {graph.updatePlugin({key: 'bubble-sets',[property]: value,});graph.render();});const apiConfig = {member: 'node-1',avoidMember: 'node-1',};const apiFolder = gui.addFolder('Bubblesets API');const instance = graph.getPluginInstance('bubble-sets');const nodeIds = graph.getData().nodes.map((node) => node.id);const edgeIds = graph.getData().edges.map((edge) => edge.id);apiFolder.add(apiConfig, 'member', [...nodeIds, ...edgeIds]);apiFolder.add({ addMember: () => instance.addMember(apiConfig.member) }, 'addMember').name('add member');apiFolder.add({ removeMember: () => instance.removeMember(apiConfig.member) }, 'removeMember').name('remove member');apiFolder.add({ removeMember: () => alert('Members in Bubblesets: ' + instance.getMember()) }, 'removeMember').name('get member');apiFolder.add(apiConfig, 'avoidMember', nodeIds);apiFolder.add({ addAvoidMember: () => instance.addAvoidMember(apiConfig.avoidMember) }, 'addAvoidMember').name('add avoid member');apiFolder.add({ removeAvoidMember: () => instance.removeAvoidMember(apiConfig.avoidMember) }, 'removeAvoidMember').name('remove avoid member');apiFolder.add({ removeMember: () => alert('Avoid members in Bubblesets: ' + instance.getAvoidMember()) }, 'removeMember').name('get avoid member');},);
| 属性 | 描述 | 类型 | 默认值 | 必选 |
| ------------------------ | ---------------------------------------------------------------- | -------------------------------------------------------------- | ------------- | ---- | --- | --- | --- |
| type | 插件类型 | string | bubble-sets
| |
| key | 插件唯一标识符,用于后续更新 | string | - | ✓ |
| members | 成员元素,包括节点和边 | ID[] | - | ✓ |
| avoidMembers | 需要避开的元素,在绘制轮廓时不会包含这些元素(目前支持设置节点) | ID[] | - | |
| label | 是否显示标签 | boolean | true | |
| labelPlacement | 标签位置 | left
| right
| top
| bottom
| center
| bottom
| bottom
| |
| labelBackground | 是否显示背景 | boolean | false | |
| labelPadding | 标签内边距 | number | number[] | 0 | |
| labelCloseToPath | 标签是否贴合轮廓 | boolean | true | |
| labelAutoRotate | 标签是否跟随轮廓旋转 | boolean | true | |
| labelOffsetX | 标签 x 轴偏移量 | number | 0 | |
| labelOffsetY | 标签 y 轴偏移量 | number | 0 | |
| labelMaxWidth | 文本的最大宽度,超出会自动省略 | number | - | |
| maxRoutingIterations | 计算成员之间路径的最大迭代次数 | number | 100 | | | - | |
| maxMarchingIterations | 计算轮廓的最大迭代次数 | number | 20 | |
| pixelGroup | 每个潜在区域组的像素数,用于提高速度 | number | 4 | |
| edgeR0 | 边的半径参数 R0 | number | - | |
| edgeR1 | 边的半径参数 R1 | number | - | |
| nodeR0 | 节点的半径参数 R0 | number | - | |
| nodeR1 | 节点的半径参数 R1 | number | - | |
| morphBuffer | 形态缓冲区大小 | number |
| threshold | 阈值 | number | - | |
| memberInfluenceFactor | 成员影响因子 | number | - | |
| edgeInfluenceFactor | 边影响因子 | number | - | |
| nonMemberInfluenceFactor | 非成员影响因子 | number | - | |
| virtualEdges | 是否使用虚拟边 | boolean | - | |
ID[] 成员元素,包括节点和边
const graph = new Graph({plugins: [{type: 'bubble-sets',members: ['node-0', 'node-1', 'node-2'],},],});
效果如下:
createGraph({autoFit: 'view',data: {nodes: [{id: 'node-0',data: { cluster: 'a' },style: { x: 555, y: 151 },},{id: 'node-1',data: { cluster: 'a' },style: { x: 532, y: 323 },},{id: 'node-2',data: { cluster: 'a' },style: { x: 473, y: 227 },},{id: 'node-3',data: { cluster: 'a' },style: { x: 349, y: 212 },},{id: 'node-4',data: { cluster: 'b' },style: { x: 234, y: 201 },},{id: 'node-5',data: { cluster: 'b' },style: { x: 338, y: 333 },},{id: 'node-6',data: { cluster: 'b' },style: { x: 365, y: 91 },},],edges: [{id: 'edge-0',source: 'node-0',target: 'node-2',},{id: 'edge-1',source: 'node-1',target: 'node-2',},{id: 'edge-2',source: 'node-2',target: 'node-3',},{id: 'edge-3',source: 'node-3',target: 'node-4',},{id: 'edge-4',source: 'node-3',target: 'node-5',},{id: 'edge-5',source: 'node-3',target: 'node-6',},],},behaviors: ['drag-canvas', 'zoom-canvas'],plugins: [{type: 'bubble-sets',key: 'bubble-sets-a',members: ['node-0', 'node-1', 'node-2'],},],},{ width: 300, height: 150 },);
boolean Default:
true
标签是否贴合轮廓
示例:不让label贴合轮廓
const graph = new Graph({plugins: [{type: 'bubble-sets',members: ['node-0', 'node-1', 'node-2', 'node-3'],label: true, // 显示标签labelText: 'cluster-a',labelCloseToPath: false,},],});
效果如下:
createGraph({autoFit: 'view',data: {nodes: [{id: 'node-0',data: { cluster: 'a' },style: { x: 555, y: 151 },},{id: 'node-1',data: { cluster: 'a' },style: { x: 532, y: 323 },},{id: 'node-2',data: { cluster: 'a' },style: { x: 473, y: 227 },},{id: 'node-3',data: { cluster: 'a' },style: { x: 349, y: 212 },},{id: 'node-4',data: { cluster: 'b' },style: { x: 234, y: 201 },},{id: 'node-5',data: { cluster: 'b' },style: { x: 338, y: 333 },},{id: 'node-6',data: { cluster: 'b' },style: { x: 365, y: 91 },},],edges: [{id: 'edge-0',source: 'node-0',target: 'node-2',},{id: 'edge-1',source: 'node-1',target: 'node-2',},{id: 'edge-2',source: 'node-2',target: 'node-3',},{id: 'edge-3',source: 'node-3',target: 'node-4',},{id: 'edge-4',source: 'node-3',target: 'node-5',},{id: 'edge-5',source: 'node-3',target: 'node-6',},],},plugins: [{key: 'bubble-sets-a',type: 'bubble-sets',members: ['node-0', 'node-1', 'node-2', 'node-3'],label: true, // 显示标签labelText: 'cluster-a',labelCloseToPath: false,},],behaviors: ['drag-canvas', 'zoom-canvas'],},{ width: 300, height: 150 },);
boolean Default:
true
标签是否跟随轮廓旋转
示例:不让label标签跟随轮廓旋转
const graph = new Graph({plugins: [{type: 'bubble-sets',members: ['node-0', 'node-1', 'node-2', 'node-3'],label: true, // 显示标签labelText: 'cluster-a',labelAutoRotate: false,},],});
效果如下:
createGraph({autoFit: 'view',data: {nodes: [{id: 'node-0',data: { cluster: 'a' },style: { x: 555, y: 151 },},{id: 'node-1',data: { cluster: 'a' },style: { x: 532, y: 323 },},{id: 'node-2',data: { cluster: 'a' },style: { x: 473, y: 227 },},{id: 'node-3',data: { cluster: 'a' },style: { x: 349, y: 212 },},{id: 'node-4',data: { cluster: 'b' },style: { x: 234, y: 201 },},{id: 'node-5',data: { cluster: 'b' },style: { x: 338, y: 333 },},{id: 'node-6',data: { cluster: 'b' },style: { x: 365, y: 91 },},],edges: [{id: 'edge-0',source: 'node-0',target: 'node-2',},{id: 'edge-1',source: 'node-1',target: 'node-2',},{id: 'edge-2',source: 'node-2',target: 'node-3',},{id: 'edge-3',source: 'node-3',target: 'node-4',},{id: 'edge-4',source: 'node-3',target: 'node-5',},{id: 'edge-5',source: 'node-3',target: 'node-6',},],},plugins: [{key: 'bubble-sets-a',type: 'bubble-sets',members: ['node-0', 'node-1', 'node-2', 'node-3'],label: true, // 显示标签labelText: 'cluster-a',labelAutoRotate: false,},],behaviors: ['drag-canvas', 'zoom-canvas'],},{ width: 300, height: 150 },);
最简单的方式是直接使用预设配置:
const graph = new Graph({plugins: [{type: 'bubble-sets',members: ['node-0', 'node-1', 'node-2', 'node-3'],},],});
效果如下:
createGraph({autoFit: 'center',data: {nodes: [{id: 'node-0',data: { cluster: 'a' },style: { x: 555, y: 151 },},{id: 'node-1',data: { cluster: 'a' },style: { x: 532, y: 323 },},{id: 'node-2',data: { cluster: 'a' },style: { x: 473, y: 227 },},{id: 'node-3',data: { cluster: 'a' },style: { x: 349, y: 212 },},{id: 'node-4',data: { cluster: 'b' },style: { x: 234, y: 201 },},{id: 'node-5',data: { cluster: 'b' },style: { x: 338, y: 333 },},{id: 'node-6',data: { cluster: 'b' },style: { x: 365, y: 91 },},],edges: [{id: 'edge-0',source: 'node-0',target: 'node-2',},{id: 'edge-1',source: 'node-1',target: 'node-2',},{id: 'edge-2',source: 'node-2',target: 'node-3',},{id: 'edge-3',source: 'node-3',target: 'node-4',},{id: 'edge-4',source: 'node-3',target: 'node-5',},{id: 'edge-5',source: 'node-3',target: 'node-6',},],},behaviors: ['drag-canvas', 'zoom-canvas'],plugins: [{type: 'bubble-sets',key: 'bubble-sets-a',members: ['node-0', 'node-1', 'node-2', 'node-3'],},],},{ width: 300, height: 150 },);
const graph = new Graph({plugins: [{type: 'bubble-sets',members: ['node-0', 'node-1', 'node-2', 'node-3'],fill: '#7e3feb', // 气泡填充颜色fillOpacity: 0.1, // 填充透明度stroke: '#7e3feb', // 边框颜色strokeOpacity: 1, // 边框透明度},],});
效果如下:
createGraph({autoFit: 'center',data: {nodes: [{id: 'node-0',data: { cluster: 'a' },style: { x: 555, y: 151 },},{id: 'node-1',data: { cluster: 'a' },style: { x: 532, y: 323 },},{id: 'node-2',data: { cluster: 'a' },style: { x: 473, y: 227 },},{id: 'node-3',data: { cluster: 'a' },style: { x: 349, y: 212 },},{id: 'node-4',data: { cluster: 'b' },style: { x: 234, y: 201 },},{id: 'node-5',data: { cluster: 'b' },style: { x: 338, y: 333 },},{id: 'node-6',data: { cluster: 'b' },style: { x: 365, y: 91 },},],edges: [{id: 'edge-0',source: 'node-0',target: 'node-2',},{id: 'edge-1',source: 'node-1',target: 'node-2',},{id: 'edge-2',source: 'node-2',target: 'node-3',},{id: 'edge-3',source: 'node-3',target: 'node-4',},{id: 'edge-4',source: 'node-3',target: 'node-5',},{id: 'edge-5',source: 'node-3',target: 'node-6',},],},plugins: [{type: 'bubble-sets',members: ['node-0', 'node-1', 'node-2', 'node-3'],fill: '#7e3feb', // 气泡填充颜色fillOpacity: 0.1, // 填充透明度stroke: '#7e3feb', // 边框颜色strokeOpacity: 1, // 边框透明度},],behaviors: ['drag-canvas', 'zoom-canvas'],},{ width: 300, height: 150 },);
您可以配置标签的位置、背景、偏移量等属性,以增强可视化效果。
const graph = new Graph({plugins: [{type: 'bubble-sets',members: ['node-0', 'node-1', 'node-2', 'node-3'],label: true, // 显示标签labelPlacement: 'top', // 标签位置labelBackground: true, // 显示标签背景labelPadding: 5, // 标签内边距},],});
效果如下:
createGraph({autoFit: 'center',data: {nodes: [{id: 'node-0',data: { cluster: 'a' },style: { x: 555, y: 151 },},{id: 'node-1',data: { cluster: 'a' },style: { x: 532, y: 323 },},{id: 'node-2',data: { cluster: 'a' },style: { x: 473, y: 227 },},{id: 'node-3',data: { cluster: 'a' },style: { x: 349, y: 212 },},{id: 'node-4',data: { cluster: 'b' },style: { x: 234, y: 201 },},{id: 'node-5',data: { cluster: 'b' },style: { x: 338, y: 333 },},{id: 'node-6',data: { cluster: 'b' },style: { x: 365, y: 91 },},],edges: [{id: 'edge-0',source: 'node-0',target: 'node-2',},{id: 'edge-1',source: 'node-1',target: 'node-2',},{id: 'edge-2',source: 'node-2',target: 'node-3',},{id: 'edge-3',source: 'node-3',target: 'node-4',},{id: 'edge-4',source: 'node-3',target: 'node-5',},{id: 'edge-5',source: 'node-3',target: 'node-6',},],},plugins: [{key: 'bubble-sets-a',type: 'bubble-sets',members: ['node-0', 'node-1', 'node-2', 'node-3'],label: true, // 显示标签labelText: 'cluster-a',labelPlacement: 'top', // 标签位置labelBackground: true, // 显示标签背景labelPadding: 5, // 标签内边距},],behaviors: ['drag-canvas', 'zoom-canvas'],},{ width: 300, height: 150 },);