BubbleSets was originally proposed by Christopher Collins in the 2009 paper "Bubble Sets: Revealing Set Relations with Isocontours over Existing Visualizations".
The principle is to represent sets by creating a shape similar to a bubble. Each set is represented by a unique "bubble", and the elements in the set are contained within this bubble. If two sets have an intersection, then the two bubbles will have an overlapping part, which represents the intersection of the two sets.
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');},);
bubble-sets
Plugin type
boolean Default:
true
Whether to display the label
string[]
Elements to avoid, these elements will not be included when drawing the contour, currently only nodes are supported
string[]
Member elements, including nodes and edges
TextStyleProps includes the following properties:
icon{TextStyleProps} means you need to use the following property names:
boolean Default:
true
Whether the label rotates with the contour. Only effective when closeToPath is true
boolean Default:
true
Whether the label is close to the contour
number
The maximum width of the text, which will be automatically ellipsis if exceeded
number Default:
0
Label x-axis offset
number Default:
0
Label y-axis offset
'left' | 'right' | 'top' | 'bottom' | 'center' Default:
'bottom'
Label position
boolean
Whether to show background
number | number[] Default:
0
Label padding
TextStyleProps includes the following properties:
icon{TextStyleProps} means you need to use the following property names:
Add elements to avoid
addAvoidMember(avoidMembers: ID | ID[]): void;
Parameter | Type | Description |
---|---|---|
avoidMembers | string | string[] | 单个或多个 |
Returns:
Add member elements
addMember(members: ID | ID[]): void;
Parameter | Type | Description |
---|---|---|
members | string | string[] | 单个或多个 |
Returns:
Get elements to avoid
getAvoidMember(): string[];
Returns:
Type: string[]
Description: 成员元素数组
Get member elements
getMember(): string[];
Returns:
Type: string[]
Description: 成员元素数组
Remove elements to avoid
removeAvoidMember(avoidMembers: ID | ID[]): void;
Parameter | Type | Description |
---|---|---|
avoidMembers | string | string[] | 单个或多个 |
Returns:
Remove member elements
removeMember(members: ID | ID[]): void;
Parameter | Type | Description |
---|---|---|
members | string | string[] | 单个或多个 |
Returns:
Update elements to avoid
updateAvoidMember(avoidMembers: ID | ID[]): void;
Parameter | Type | Description |
---|---|---|
avoidMembers | string | string[] | 单个或多个 |
Returns:
Update member elements
updateMember(members: ID[] | ((prev: ID[]) => ID[])): void;
Parameter | Type | Description |
---|---|---|
members | string[] | ((prev: string[]) => string[]) | 值或者回调函数 |
Returns: