Merge branch 'main' of https://gitlab.com/phenomLi/StructV2 into main
This commit is contained in:
@@ -5,44 +5,32 @@ import { ViewContainer } from "../View/viewContainer";
|
||||
|
||||
|
||||
/**
|
||||
* 初始化视图拖拽功能
|
||||
*
|
||||
* @param g6Instance
|
||||
* @param hasLeak
|
||||
*/
|
||||
export function InitDragCanvasWithLeak(viewContainer: ViewContainer) {
|
||||
let g6Instance = viewContainer.getG6Instance(),
|
||||
isDragStart = false,
|
||||
startPositionY = 0,
|
||||
currentLeakAreaY = 0;
|
||||
prevDy = 0;
|
||||
|
||||
g6Instance.on('canvas:dragstart', event => {
|
||||
isDragStart = true;
|
||||
startPositionY = event.canvasY;
|
||||
currentLeakAreaY = viewContainer.leakAreaY;
|
||||
});
|
||||
|
||||
g6Instance.on('canvas:drag', event => {
|
||||
if(!isDragStart) {
|
||||
g6Instance.on('viewportchange', event => {
|
||||
if(event.action !== 'translate') {
|
||||
return false;
|
||||
}
|
||||
|
||||
let zoom = g6Instance.getZoom(),
|
||||
dy = (event.canvasY - startPositionY) / zoom,
|
||||
leakAreaY = currentLeakAreaY + dy;
|
||||
let translateX = event.matrix[7],
|
||||
dy = translateX- prevDy;
|
||||
|
||||
prevDy = translateX;
|
||||
|
||||
viewContainer.leakAreaY = leakAreaY;
|
||||
if(viewContainer.hasLeak) {
|
||||
EventBus.emit('onLeakAreaUpdate', {
|
||||
leakAreaY: viewContainer.leakAreaY,
|
||||
hasLeak: viewContainer.hasLeak
|
||||
viewContainer.leakAreaY = viewContainer.leakAreaY + dy;
|
||||
if (viewContainer.hasLeak) {
|
||||
EventBus.emit('onLeakAreaUpdate', {
|
||||
leakAreaY: viewContainer.leakAreaY,
|
||||
hasLeak: viewContainer.hasLeak
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
g6Instance.on('canvas:dragend', event => {
|
||||
isDragStart = false;
|
||||
startPositionY = 0;
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,66 +1,38 @@
|
||||
import { Graph } from "@antv/g6-pc";
|
||||
import { Graph } from "@antv/g6";
|
||||
import { SVNode } from "../Model/SVNode";
|
||||
import { LayoutGroupOptions } from "../options";
|
||||
import { SVNodeAppendage } from "../Model/SVNodeAppendage";
|
||||
|
||||
|
||||
/**
|
||||
* 在初始化渲染器之后,修正节点拖拽时,外部指针没有跟着动的问题
|
||||
* 在初始化渲染器之后,修正节点拖拽时,外部指针或者其他 appendage 没有跟着动的问题
|
||||
*
|
||||
*/
|
||||
export function FixNodeMarkerDrag(g6Instance: Graph, optionsTable: { [key: string]: LayoutGroupOptions }) {
|
||||
let dragActive: boolean = false;
|
||||
|
||||
const nodeData = {
|
||||
node: null,
|
||||
startX: 0,
|
||||
startY: 0
|
||||
};
|
||||
|
||||
const markerData = {
|
||||
marker: null,
|
||||
startX: 0,
|
||||
startY: 0
|
||||
};
|
||||
|
||||
const freedLabelData = {
|
||||
freedLabel: null,
|
||||
startX: 0,
|
||||
startY: 0
|
||||
};
|
||||
export function FixNodeMarkerDrag(g6Instance: Graph) {
|
||||
let dragActive: boolean = false,
|
||||
nodeData: { node: SVNode, startX: number, startY: number },
|
||||
appendagesData: { appendage: SVNodeAppendage, startX: number, startY: number }[] = [];
|
||||
|
||||
g6Instance.on('node:dragstart', event => {
|
||||
nodeData.node = event.item['SVModel'];
|
||||
let node: SVNode = nodeData.node;
|
||||
let node: SVNode = event.item['SVModel'];
|
||||
|
||||
if (node.isNode() === false || node.leaked) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const dragNode = optionsTable[node.layout].behavior.dragNode;
|
||||
|
||||
if (dragNode === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(dragNode) && dragNode.find(item => item === node.sourceType) === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
dragActive = true;
|
||||
nodeData.startX = event.canvasX;
|
||||
nodeData.startY = event.canvasY;
|
||||
nodeData = {
|
||||
node,
|
||||
startX: event.canvasX,
|
||||
startY: event.canvasY
|
||||
};
|
||||
|
||||
if (node.marker) {
|
||||
markerData.marker = node.marker;
|
||||
markerData.startX = markerData.marker.get('x');
|
||||
markerData.startY = markerData.marker.get('y');
|
||||
}
|
||||
|
||||
if(node.freedLabel) {
|
||||
freedLabelData.freedLabel = node.freedLabel;
|
||||
freedLabelData.startX = freedLabelData.freedLabel.get('x');
|
||||
freedLabelData.startY = freedLabelData.freedLabel.get('y');
|
||||
}
|
||||
node.appendages.forEach(item => {
|
||||
appendagesData.push({
|
||||
appendage: item,
|
||||
startX: item.get('x'),
|
||||
startY: item.get('y')
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
g6Instance.on('node:dragend', event => {
|
||||
@@ -68,25 +40,15 @@ export function FixNodeMarkerDrag(g6Instance: Graph, optionsTable: { [key: strin
|
||||
return false;
|
||||
}
|
||||
|
||||
let distanceX = event.canvasX - nodeData.startX,
|
||||
distanceY = event.canvasY - nodeData.startY,
|
||||
nodeX = nodeData.node.get('x'),
|
||||
nodeY = nodeData.node.get('y');
|
||||
let node: SVNode = nodeData.node;
|
||||
|
||||
nodeData.node.set({
|
||||
x: nodeX + distanceX,
|
||||
y: nodeY + distanceY
|
||||
node.set({
|
||||
x: node.G6Item.getModel().x,
|
||||
y: node.G6Item.getModel().y
|
||||
});
|
||||
|
||||
nodeData.node = null;
|
||||
nodeData.startX = 0;
|
||||
nodeData.startY = 0;
|
||||
markerData.marker = null;
|
||||
markerData.startX = 0;
|
||||
markerData.startY = 0;
|
||||
freedLabelData.freedLabel = null;
|
||||
freedLabelData.startX = 0;
|
||||
freedLabelData.startY = 0;
|
||||
nodeData = null;
|
||||
appendagesData.length = 0;
|
||||
dragActive = false;
|
||||
});
|
||||
|
||||
@@ -99,18 +61,11 @@ export function FixNodeMarkerDrag(g6Instance: Graph, optionsTable: { [key: strin
|
||||
dy = ev.canvasY - nodeData.startY,
|
||||
zoom = g6Instance.getZoom();
|
||||
|
||||
if(markerData.marker) {
|
||||
markerData.marker.set({
|
||||
x: markerData.startX + dx / zoom,
|
||||
y: markerData.startY + dy / zoom
|
||||
appendagesData.forEach(item => {
|
||||
item.appendage.set({
|
||||
x: item.startX + dx / zoom,
|
||||
y: item.startY + dy / zoom
|
||||
});
|
||||
}
|
||||
|
||||
if(freedLabelData.freedLabel) {
|
||||
freedLabelData.freedLabel.set({
|
||||
x: freedLabelData.startX + dx / zoom,
|
||||
y: freedLabelData.startY + dy / zoom
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import { SVModel } from "../Model/SVModel";
|
||||
import { LayoutGroupOptions } from "../options";
|
||||
|
||||
|
||||
|
||||
@@ -8,15 +7,8 @@ import { LayoutGroupOptions } from "../options";
|
||||
* @param optionsTable
|
||||
* @returns
|
||||
*/
|
||||
export function InitViewBehaviors(optionsTable: { [key: string]: LayoutGroupOptions }) {
|
||||
const dragNodeTable: { [key: string]: boolean | string[] } = {},
|
||||
selectNodeTable: { [key: string]: boolean | string[] } = {},
|
||||
defaultModes = [];
|
||||
|
||||
Object.keys(optionsTable).forEach(item => {
|
||||
dragNodeTable[item] = optionsTable[item].behavior.dragNode;
|
||||
selectNodeTable[item] = optionsTable[item].behavior.selectNode;
|
||||
});
|
||||
export function InitViewBehaviors() {
|
||||
const defaultModes = [];
|
||||
|
||||
const dragNodeFilter = event => {
|
||||
let g6Item = event.item,
|
||||
@@ -26,17 +18,7 @@ export function InitViewBehaviors(optionsTable: { [key: string]: LayoutGroupOpti
|
||||
return false;
|
||||
}
|
||||
|
||||
let dragNode = optionsTable[node.layout].behavior.dragNode;
|
||||
|
||||
if (typeof dragNode === 'boolean') {
|
||||
return dragNode;
|
||||
}
|
||||
|
||||
if (Array.isArray(dragNode) && dragNode.indexOf(node.sourceType) > -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
const selectNodeFilter = event => {
|
||||
@@ -47,17 +29,7 @@ export function InitViewBehaviors(optionsTable: { [key: string]: LayoutGroupOpti
|
||||
return false;
|
||||
}
|
||||
|
||||
let selectNode = optionsTable[node.layout].behavior.selectNode;
|
||||
|
||||
if (typeof selectNode === 'boolean') {
|
||||
return selectNode;
|
||||
}
|
||||
|
||||
if (Array.isArray(selectNode) && selectNode.indexOf(node.sourceType) > -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
defaultModes.push({
|
||||
@@ -69,6 +41,10 @@ export function InitViewBehaviors(optionsTable: { [key: string]: LayoutGroupOpti
|
||||
type: 'drag-canvas'
|
||||
});
|
||||
|
||||
// defaultModes.push({
|
||||
// type: 'zoom-canvas'
|
||||
// });
|
||||
|
||||
defaultModes.push({
|
||||
type: 'click-select',
|
||||
shouldBegin: selectNodeFilter
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import { Graph, IGroup } from "@antv/g6-pc";
|
||||
import { ext } from '@antv/matrix-util';
|
||||
|
||||
const transform = ext.transform;
|
||||
|
||||
|
||||
/**
|
||||
* 初始化视图缩放功能
|
||||
* @param g6Instance
|
||||
* @param generalModelsGroup
|
||||
*/
|
||||
export function InitZoomCanvas(g6Instance: Graph, g6GeneralGroup: IGroup) {
|
||||
const minZoom = 0.2,
|
||||
maxZoom = 2,
|
||||
step = 0.15;
|
||||
|
||||
g6Instance.on('wheel', event => {
|
||||
let delta = event.wheelDelta,
|
||||
matrix = g6GeneralGroup.getMatrix(),
|
||||
center = [event.x, event.y],
|
||||
targetScale = 1;
|
||||
|
||||
if (delta > 0) {
|
||||
targetScale += step;
|
||||
}
|
||||
|
||||
if (delta < 0) {
|
||||
targetScale -= step;
|
||||
}
|
||||
|
||||
matrix = transform(matrix, [
|
||||
['t', -center[0], -center[1]],
|
||||
['s', targetScale, targetScale],
|
||||
['t', center[0], center[1]],
|
||||
]);
|
||||
|
||||
if ((minZoom && matrix[0] < minZoom) || (maxZoom && matrix[0] > maxZoom)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
g6GeneralGroup.setMatrix(matrix);
|
||||
g6Instance.paint();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import { EventBus } from "../Common/eventBus";
|
||||
import { ViewContainer } from "../View/viewContainer";
|
||||
|
||||
|
||||
/**
|
||||
* 缩放这里搞不出来,尽力了
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param g6Instance
|
||||
* @param generalModelsGroup
|
||||
*/
|
||||
export function InitZoomCanvasWithLeak(viewContainer: ViewContainer) {
|
||||
let g6Instance = viewContainer.getG6Instance(),
|
||||
prevDy = 0;
|
||||
|
||||
let prevZoom = 1;
|
||||
|
||||
// g6Instance.on('viewportchange', event => {
|
||||
// if(event.action !== 'zoom') {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// console.log(event.matrix);
|
||||
|
||||
// viewContainer.leakAreaY = event.matrix[4] * viewContainer.leakAreaY + event.matrix[7];
|
||||
// if (viewContainer.hasLeak) {
|
||||
// EventBus.emit('onLeakAreaUpdate', {
|
||||
// leakAreaY: viewContainer.leakAreaY,
|
||||
// hasLeak: viewContainer.hasLeak
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
|
||||
g6Instance.on('wheelzoom', event => {
|
||||
let dy = event.y - viewContainer.leakAreaY,
|
||||
dZoom = prevZoom - g6Instance.getZoom();
|
||||
|
||||
prevZoom = g6Instance.getZoom();
|
||||
|
||||
viewContainer.leakAreaY = viewContainer.leakAreaY + dy * dZoom;
|
||||
if (viewContainer.hasLeak) {
|
||||
EventBus.emit('onLeakAreaUpdate', {
|
||||
leakAreaY: viewContainer.leakAreaY,
|
||||
hasLeak: viewContainer.hasLeak
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Util } from "./util";
|
||||
import { BoundingRect, Bound } from "./boundingRect";
|
||||
import { SVModel } from "../Model/SVModel";
|
||||
import { ext } from '@antv/matrix-util';
|
||||
import { SVLink } from "../Model/SVLink";
|
||||
|
||||
|
||||
|
||||
|
||||
+3
-3
@@ -2,7 +2,7 @@ import { EdgeConfig, GraphData, NodeConfig } from "@antv/g6-core";
|
||||
import { LayoutGroup, LayoutGroupTable } from "../Model/modelConstructor";
|
||||
import { SVLink } from "../Model/SVLink";
|
||||
import { SVModel } from "../Model/SVModel";
|
||||
import { SV } from "../StructV";
|
||||
import { Util as G6Util } from '@antv/g6';
|
||||
|
||||
|
||||
/**
|
||||
@@ -25,7 +25,7 @@ export const Util = {
|
||||
* @param obj
|
||||
*/
|
||||
objectClone<T extends Object>(obj: T): T {
|
||||
return obj? JSON.parse(JSON.stringify(obj)): { };
|
||||
return obj? JSON.parse(JSON.stringify(obj)): null;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -132,7 +132,7 @@ export const Util = {
|
||||
* @param rotation
|
||||
*/
|
||||
calcRotateMatrix(matrix: number[], rotation: number): number[] {
|
||||
const Mat3 = SV.G6.Util.mat3;
|
||||
const Mat3 = G6Util.mat3;
|
||||
Mat3.rotate(matrix, matrix, rotation);
|
||||
return matrix;
|
||||
}
|
||||
|
||||
+1
-2
@@ -24,8 +24,7 @@ export class SVLink extends SVModel {
|
||||
this.G6ModelProps = this.generateG6ModelProps(options);
|
||||
}
|
||||
|
||||
|
||||
protected generateG6ModelProps(options: LinkOption): EdgeConfig {
|
||||
generateG6ModelProps(options: LinkOption): EdgeConfig {
|
||||
let sourceAnchor = options.sourceAnchor,
|
||||
targetAnchor = options.targetAnchor;
|
||||
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
import { INode, NodeConfig } from "@antv/g6-core";
|
||||
import { Util } from "../Common/util";
|
||||
import { MarkerOption, NodeLabelOption, Style } from "../options";
|
||||
import { SVModel } from "./SVModel";
|
||||
import { SVNode } from "./SVNode";
|
||||
|
||||
|
||||
|
||||
export class SVMarker extends SVModel {
|
||||
public target: SVNode;
|
||||
public label: string | string[];
|
||||
public anchor: number;
|
||||
|
||||
public shadowG6Item: INode;
|
||||
public G6Item: INode;
|
||||
|
||||
constructor(id: string, type: string, group: string, layout: string, label: string | string[], target: SVNode, options: MarkerOption) {
|
||||
super(id, type, group, layout, 'marker');
|
||||
|
||||
this.target = target;
|
||||
this.label = label;
|
||||
|
||||
this.target.marker = this;
|
||||
this.G6ModelProps = this.generateG6ModelProps(options);
|
||||
}
|
||||
|
||||
protected generateG6ModelProps(options: MarkerOption): NodeConfig {
|
||||
this.anchor = options.anchor;
|
||||
|
||||
const type = options.type,
|
||||
defaultSize: [number, number] = type === 'pointer'? [8, 30]: [12, 12];
|
||||
|
||||
return {
|
||||
id: this.id,
|
||||
x: 0,
|
||||
y: 0,
|
||||
rotation: 0,
|
||||
type: options.type || 'marker',
|
||||
size: options.size || defaultSize,
|
||||
anchorPoints: null,
|
||||
label: typeof this.label === 'string'? this.label: this.label.join(', '),
|
||||
style: Util.objectClone<Style>(options.style),
|
||||
labelCfg: Util.objectClone<NodeLabelOption>(options.labelOptions)
|
||||
};
|
||||
}
|
||||
|
||||
public getLabelSizeRadius(): number {
|
||||
const { width, height } = this.shadowG6Item.getContainer().getChildren()[2].getBBox();
|
||||
return width > height? width: height;
|
||||
}
|
||||
};
|
||||
+51
-25
@@ -1,9 +1,9 @@
|
||||
import { Util } from "../Common/util";
|
||||
import { ModelOption, Style } from "../options";
|
||||
import { Style } from "../options";
|
||||
import { BoundingRect } from "../Common/boundingRect";
|
||||
import { EdgeConfig, Item, NodeConfig } from "@antv/g6-core";
|
||||
import { Point } from "@antv/g-base";
|
||||
|
||||
import { Graph } from "_@antv_g6-pc@0.5.0@@antv/g6-pc";
|
||||
import merge from 'merge';
|
||||
|
||||
|
||||
|
||||
@@ -11,13 +11,16 @@ import { Point } from "@antv/g-base";
|
||||
export class SVModel {
|
||||
public id: string;
|
||||
public sourceType: string;
|
||||
|
||||
|
||||
public g6Instance: Graph;
|
||||
public shadowG6Instance: Graph;
|
||||
public group: string;
|
||||
public layout: string;
|
||||
public G6ModelProps: NodeConfig | EdgeConfig;
|
||||
public shadowG6Item: Item;
|
||||
public G6Item: Item;
|
||||
|
||||
public preLayout: boolean; // 是否进入预备布局阶段
|
||||
public discarded: boolean;
|
||||
public freed: boolean;
|
||||
public leaked: boolean;
|
||||
@@ -26,13 +29,17 @@ export class SVModel {
|
||||
private transformMatrix: number[];
|
||||
private modelType: string;
|
||||
|
||||
constructor(id: string, type: string, group: string, layout: string, modelType: string) {
|
||||
public layoutX: number;
|
||||
public layoutY: number;
|
||||
|
||||
constructor(id: string, type: string, group: string, layout: string, modelType: string) {
|
||||
this.id = id;
|
||||
this.sourceType = type;
|
||||
this.group = group;
|
||||
this.layout = layout;
|
||||
this.shadowG6Item = null;
|
||||
this.G6Item = null;
|
||||
this.preLayout = false;
|
||||
this.discarded = false;
|
||||
this.freed = false;
|
||||
this.leaked = false;
|
||||
@@ -45,7 +52,7 @@ export class SVModel {
|
||||
* 定义 G6 model 的属性
|
||||
* @param option
|
||||
*/
|
||||
protected generateG6ModelProps(options: ModelOption) {
|
||||
generateG6ModelProps(options: unknown): NodeConfig | EdgeConfig {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -64,53 +71,72 @@ export class SVModel {
|
||||
* @returns
|
||||
*/
|
||||
set(attr: string | object, value?: any) {
|
||||
if(this.discarded) {
|
||||
if (this.discarded) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof attr === 'object') {
|
||||
if (typeof attr === 'object') {
|
||||
Object.keys(attr).map(item => {
|
||||
this.set(item, attr[item]);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.G6ModelProps[attr] === value) {
|
||||
if (this.G6ModelProps[attr] === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(attr === 'style' || attr === 'labelCfg') {
|
||||
Object.assign(this.G6ModelProps[attr], value);
|
||||
if (attr === 'style' || attr === 'labelCfg') {
|
||||
this.G6ModelProps[attr] = merge(this.G6ModelProps[attr] || {}, value);
|
||||
}
|
||||
else {
|
||||
this.G6ModelProps[attr] = value;
|
||||
}
|
||||
|
||||
if(attr === 'rotation') {
|
||||
if (attr === 'rotation') {
|
||||
const matrix = Util.calcRotateMatrix(this.getMatrix(), value);
|
||||
this.setMatrix(matrix);
|
||||
}
|
||||
|
||||
// 更新G6Item
|
||||
if(this.G6Item) {
|
||||
if(attr === 'x' || attr === 'y') {
|
||||
this.G6Item.updatePosition({ [attr]: value } as Point);
|
||||
this.G6Item.refresh();
|
||||
if (this.G6Item) {
|
||||
if (this.preLayout) {
|
||||
const G6ItemModel = this.G6Item.getModel();
|
||||
G6ItemModel[attr] = value;
|
||||
}
|
||||
else {
|
||||
this.G6Item.update(this.G6ModelProps);
|
||||
this.g6Instance.updateItem(this.G6Item, this.G6ModelProps);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新shadowG6Item
|
||||
if(this.shadowG6Item) {
|
||||
if(attr === 'x' || attr === 'y') {
|
||||
this.shadowG6Item.updatePosition({ [attr]: value } as Point);
|
||||
this.shadowG6Item.refresh();
|
||||
}
|
||||
else {
|
||||
this.shadowG6Item.update(this.G6ModelProps);
|
||||
if (this.shadowG6Item) {
|
||||
this.shadowG6Instance.updateItem(this.shadowG6Item, this.G6ModelProps);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param G6ModelProps
|
||||
*/
|
||||
updateG6ModelStyle(G6ModelProps: NodeConfig | EdgeConfig) {
|
||||
const newG6ModelProps = {
|
||||
style: {
|
||||
...G6ModelProps.style
|
||||
},
|
||||
labelCfg: {
|
||||
...G6ModelProps.labelCfg
|
||||
}
|
||||
};
|
||||
|
||||
this.G6ModelProps = merge(this.G6ModelProps, newG6ModelProps);
|
||||
|
||||
if (this.G6Item) {
|
||||
this.g6Instance.updateItem(this.G6Item, this.G6ModelProps);
|
||||
}
|
||||
|
||||
if (this.shadowG6Item) {
|
||||
this.shadowG6Instance.updateItem(this.shadowG6Item, this.G6ModelProps);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +154,7 @@ export class SVModel {
|
||||
getMatrix(): number[] {
|
||||
return [...this.transformMatrix];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置变换矩阵
|
||||
* @param matrix
|
||||
|
||||
+18
-87
@@ -1,98 +1,34 @@
|
||||
import { INode, NodeConfig } from "@antv/g6-core";
|
||||
import { Util } from "../Common/util";
|
||||
import { NodeIndexOption, NodeLabelOption, NodeOption, Style } from "../options";
|
||||
import { NodeLabelOption, NodeOption, Style } from "../options";
|
||||
import { SourceNode } from "../sources";
|
||||
import { SVLink } from "./SVLink";
|
||||
import { SVMarker } from "./SVMarker";
|
||||
import { SVModel } from "./SVModel";
|
||||
import { SVAddressLabel, SVFreedLabel, SVIndexLabel, SVMarker, SVNodeAppendage } from "./SVNodeAppendage";
|
||||
|
||||
|
||||
export class SVFreedLabel extends SVModel {
|
||||
public node: SVNode;
|
||||
|
||||
constructor(id: string, type: string, group: string, layout: string, node: SVNode) {
|
||||
super(id, type, group, layout, 'freedLabel');
|
||||
|
||||
this.node = node;
|
||||
this.node.freedLabel = this;
|
||||
this.G6ModelProps = this.generateG6ModelProps();
|
||||
}
|
||||
|
||||
generateG6ModelProps() {
|
||||
return {
|
||||
id: this.id,
|
||||
x: 0,
|
||||
y: 0,
|
||||
type: 'rect',
|
||||
label: '已释放',
|
||||
labelCfg: {
|
||||
style: {
|
||||
fill: '#b83b5e',
|
||||
opacity: 0.6
|
||||
}
|
||||
},
|
||||
size: [0, 0],
|
||||
style: {
|
||||
stroke: null,
|
||||
fill: 'transparent'
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class SVLeakAddress extends SVModel {
|
||||
public node: SVNode;
|
||||
private sourceId: string;
|
||||
|
||||
constructor(id: string, type: string, group: string, layout: string, node: SVNode) {
|
||||
super(id, type, group, layout, 'leakAddress');
|
||||
|
||||
this.node = node;
|
||||
this.sourceId = node.sourceId;
|
||||
this.node.leakAddress = this;
|
||||
this.G6ModelProps = this.generateG6ModelProps();
|
||||
}
|
||||
|
||||
generateG6ModelProps() {
|
||||
return {
|
||||
id: this.id,
|
||||
x: 0,
|
||||
y: 0,
|
||||
type: 'rect',
|
||||
label: this.sourceId,
|
||||
labelCfg: {
|
||||
style: {
|
||||
fill: '#666',
|
||||
fontSize: 16
|
||||
}
|
||||
},
|
||||
size: [0, 0],
|
||||
style: {
|
||||
stroke: null,
|
||||
fill: 'transparent'
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class SVNode extends SVModel {
|
||||
public sourceId: string;
|
||||
public sourceNode: SourceNode;
|
||||
public marker: SVMarker;
|
||||
public freedLabel: SVFreedLabel;
|
||||
public leakAddress: SVLeakAddress;
|
||||
public links: {
|
||||
inDegree: SVLink[];
|
||||
outDegree: SVLink[];
|
||||
};
|
||||
|
||||
private label: string | string[];
|
||||
private disable: boolean;
|
||||
|
||||
public shadowG6Item: INode;
|
||||
public G6Item: INode;
|
||||
|
||||
public marker: SVMarker;
|
||||
public freedLabel: SVFreedLabel;
|
||||
public indexLabel: SVIndexLabel;
|
||||
public addressLabel: SVAddressLabel;
|
||||
public appendages: SVNodeAppendage[];
|
||||
|
||||
constructor(id: string, type: string, group: string, layout: string, sourceNode: SourceNode, label: string | string[], options: NodeOption) {
|
||||
super(id, type, group, layout, 'node');
|
||||
|
||||
@@ -108,22 +44,15 @@ export class SVNode extends SVModel {
|
||||
this.sourceNode = sourceNode;
|
||||
this.sourceId = sourceNode.id.toString();
|
||||
|
||||
this.marker = null;
|
||||
this.links = { inDegree: [], outDegree: [] };
|
||||
this.appendages = [];
|
||||
this.sourceNode = sourceNode;
|
||||
this.label = label;
|
||||
this.G6ModelProps = this.generateG6ModelProps(options);
|
||||
}
|
||||
|
||||
protected generateG6ModelProps(options: NodeOption): NodeConfig {
|
||||
let indexOptions = Util.objectClone<NodeIndexOption>(options.indexOptions);
|
||||
|
||||
if (indexOptions) {
|
||||
Object.keys(indexOptions).map(key => {
|
||||
let indexOptionItem = indexOptions[key];
|
||||
indexOptionItem.value = this.sourceNode[key] ?? '';
|
||||
});
|
||||
}
|
||||
generateG6ModelProps(options: NodeOption): NodeConfig {
|
||||
const style = Util.objectClone<Style>(options.style);
|
||||
|
||||
return {
|
||||
...this.sourceNode,
|
||||
@@ -135,9 +64,11 @@ export class SVNode extends SVModel {
|
||||
size: options.size || [60, 30],
|
||||
anchorPoints: options.anchorPoints,
|
||||
label: this.label as string,
|
||||
style: Util.objectClone<Style>(options.style),
|
||||
labelCfg: Util.objectClone<NodeLabelOption>(options.labelOptions),
|
||||
indexCfg: indexOptions
|
||||
style: {
|
||||
...style,
|
||||
fill: this.disable ? '#ccc' : style.fill
|
||||
},
|
||||
labelCfg: Util.objectClone<NodeLabelOption>(options.labelOptions)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
import { INode, NodeConfig, EdgeConfig } from "@antv/g6-core";
|
||||
import { Util } from "../Common/util";
|
||||
import { AddressLabelOption, IndexLabelOption, MarkerOption, NodeLabelOption, Style } from "../options";
|
||||
import { SVModel } from "./SVModel";
|
||||
import { SVNode } from "./SVNode";
|
||||
|
||||
|
||||
|
||||
|
||||
export class SVNodeAppendage extends SVModel {
|
||||
public target: SVNode;
|
||||
|
||||
constructor(id: string, type: string, group: string, layout: string, modelType: string, target: SVNode) {
|
||||
super(id, type, group, layout, modelType);
|
||||
|
||||
this.target = target;
|
||||
this.target.appendages.push(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 已释放节点下面的文字(“已释放‘)
|
||||
*/
|
||||
export class SVFreedLabel extends SVNodeAppendage {
|
||||
constructor(id: string, type: string, group: string, layout: string, target: SVNode) {
|
||||
super(id, type, group, layout, 'freedLabel', target);
|
||||
|
||||
this.target.freedLabel = this;
|
||||
this.G6ModelProps = this.generateG6ModelProps();
|
||||
}
|
||||
|
||||
generateG6ModelProps() {
|
||||
return {
|
||||
id: this.id,
|
||||
x: 0,
|
||||
y: 0,
|
||||
type: 'rect',
|
||||
label: '已释放',
|
||||
labelCfg: {
|
||||
style: {
|
||||
fill: '#b83b5e',
|
||||
opacity: 0.6
|
||||
}
|
||||
},
|
||||
size: [0, 0],
|
||||
style: {
|
||||
stroke: null,
|
||||
fill: 'transparent'
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 被移动到泄漏区的节点上面显示的地址
|
||||
*/
|
||||
export class SVAddressLabel extends SVNodeAppendage {
|
||||
private sourceId: string;
|
||||
|
||||
constructor(id: string, type: string, group: string, layout: string, target: SVNode, options: AddressLabelOption) {
|
||||
super(id, type, group, layout, 'addressLabel', target);
|
||||
|
||||
this.sourceId = target.sourceId;
|
||||
this.target.addressLabel = this;
|
||||
this.G6ModelProps = this.generateG6ModelProps(options);
|
||||
}
|
||||
|
||||
generateG6ModelProps(options: AddressLabelOption) {
|
||||
return {
|
||||
id: this.id,
|
||||
x: 0,
|
||||
y: 0,
|
||||
type: 'rect',
|
||||
label: this.sourceId,
|
||||
labelCfg: {
|
||||
style: {
|
||||
fill: '#666',
|
||||
fontSize: 16,
|
||||
...options.style
|
||||
}
|
||||
},
|
||||
size: [0, 0],
|
||||
style: {
|
||||
stroke: null,
|
||||
fill: 'transparent'
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 节点的下标文字
|
||||
*/
|
||||
export class SVIndexLabel extends SVNodeAppendage {
|
||||
private value: string;
|
||||
|
||||
constructor(id: string, indexName: string, group: string, layout: string, value: string, target: SVNode, options: IndexLabelOption) {
|
||||
super(id, indexName, group, layout, 'indexLabel', target);
|
||||
|
||||
this.target.indexLabel = this;
|
||||
this.value = value;
|
||||
this.G6ModelProps = this.generateG6ModelProps(options) as NodeConfig;
|
||||
}
|
||||
|
||||
generateG6ModelProps(options: IndexLabelOption): NodeConfig | EdgeConfig {
|
||||
return {
|
||||
id: this.id,
|
||||
x: 0,
|
||||
y: 0,
|
||||
type: 'rect',
|
||||
label: this.value,
|
||||
labelCfg: {
|
||||
style: {
|
||||
fill: '#bbb',
|
||||
textAlign: 'center',
|
||||
textBaseline: 'middle',
|
||||
fontSize: 14,
|
||||
fontStyle: 'italic',
|
||||
...options.style
|
||||
}
|
||||
},
|
||||
size: [0, 0],
|
||||
style: {
|
||||
stroke: null,
|
||||
fill: 'transparent'
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 外部指针
|
||||
*/
|
||||
export class SVMarker extends SVNodeAppendage {
|
||||
public label: string | string[];
|
||||
public anchor: number;
|
||||
|
||||
public shadowG6Item: INode;
|
||||
public G6Item: INode;
|
||||
|
||||
constructor(id: string, type: string, group: string, layout: string, label: string | string[], target: SVNode, options: MarkerOption) {
|
||||
super(id, type, group, layout, 'marker', target);
|
||||
|
||||
this.label = label;
|
||||
|
||||
this.target.marker = this;
|
||||
this.G6ModelProps = this.generateG6ModelProps(options);
|
||||
}
|
||||
|
||||
generateG6ModelProps(options: MarkerOption): NodeConfig {
|
||||
this.anchor = options.anchor;
|
||||
|
||||
const type = options.type,
|
||||
defaultSize: [number, number] = type === 'pointer' ? [8, 30] : [12, 12];
|
||||
|
||||
return {
|
||||
id: this.id,
|
||||
x: 0,
|
||||
y: 0,
|
||||
rotation: 0,
|
||||
type: options.type || 'marker',
|
||||
size: options.size || defaultSize,
|
||||
anchorPoints: null,
|
||||
label: typeof this.label === 'string' ? this.label : this.label.join(', '),
|
||||
style: Util.objectClone<Style>(options.style),
|
||||
labelCfg: Util.objectClone<NodeLabelOption>(options.labelOptions)
|
||||
};
|
||||
}
|
||||
|
||||
public getLabelSizeRadius(): number {
|
||||
const { width, height } = this.shadowG6Item.getContainer().getChildren()[2].getBBox();
|
||||
return width > height ? width : height;
|
||||
}
|
||||
};
|
||||
@@ -1,19 +1,20 @@
|
||||
import { Util } from "../Common/util";
|
||||
import { Engine } from "../engine";
|
||||
import { LayoutCreator, LayoutGroupOptions, LinkOption, MarkerOption, NodeOption } from "../options";
|
||||
import { AddressLabelOption, IndexLabelOption, LayoutCreator, LayoutGroupOptions, LinkOption, MarkerOption, NodeOption } from "../options";
|
||||
import { sourceLinkData, LinkTarget, Sources, SourceNode } from "../sources";
|
||||
import { SV } from "../StructV";
|
||||
import { SVLink } from "./SVLink";
|
||||
import { SVMarker } from "./SVMarker";
|
||||
import { SVModel } from "./SVModel";
|
||||
import { SVFreedLabel, SVLeakAddress, SVNode } from "./SVNode";
|
||||
import { SVNode } from "./SVNode";
|
||||
import { SVAddressLabel, SVFreedLabel, SVIndexLabel, SVMarker } from "./SVNodeAppendage";
|
||||
|
||||
|
||||
export type LayoutGroup = {
|
||||
name: string;
|
||||
node: SVNode[];
|
||||
indexLabel: SVIndexLabel[];
|
||||
freedLabel: SVFreedLabel[];
|
||||
leakAddress: SVLeakAddress[];
|
||||
addressLabel: SVAddressLabel[];
|
||||
link: SVLink[];
|
||||
marker: SVMarker[];
|
||||
layoutCreator: LayoutCreator;
|
||||
@@ -38,13 +39,12 @@ export class ModelConstructor {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建svnode,svlink 和 svmarker
|
||||
* 构建SVNode,SVLink, SVMarker, SVAddressLabel, SVIndexLabel等
|
||||
* @param sourceList
|
||||
*/
|
||||
public construct(sources: Sources): LayoutGroupTable {
|
||||
const layoutGroupTable = new Map<string, LayoutGroup>(),
|
||||
layoutMap: { [key: string]: LayoutCreator } = SV.registeredLayout,
|
||||
optionsTable = this.engine.optionsTable;
|
||||
layoutMap: { [key: string]: LayoutCreator } = SV.registeredLayout;
|
||||
|
||||
Object.keys(sources).forEach(group => {
|
||||
let sourceGroup = sources[group],
|
||||
@@ -59,21 +59,25 @@ export class ModelConstructor {
|
||||
prevString: string = this.prevSourcesStringMap[group],
|
||||
nodeList: SVNode[] = [],
|
||||
freedLabelList: SVFreedLabel[] = [],
|
||||
leakAddress: SVLeakAddress[] = [],
|
||||
addressLabelList: SVAddressLabel[] = [],
|
||||
indexLabelList: SVIndexLabel[] = [],
|
||||
markerList: SVMarker[] = [];
|
||||
|
||||
if (prevString === sourceDataString) {
|
||||
return;
|
||||
}
|
||||
|
||||
const options: LayoutGroupOptions = optionsTable[layout],
|
||||
const options: LayoutGroupOptions = layoutCreator.defineOptions(sourceGroup.data),
|
||||
sourceData = layoutCreator.sourcesPreprocess(sourceGroup.data, options),
|
||||
nodeOptions = options.node || options['element'] || {},
|
||||
markerOptions = options.marker || {};
|
||||
markerOptions = options.marker || {},
|
||||
indexLabelOptions = options.indexLabel || {},
|
||||
addressLabelOption = options.addressLabel || {};
|
||||
|
||||
nodeList = this.constructNodes(nodeOptions, group, sourceData, layout);
|
||||
leakAddress = nodeList.map(item => item.leakAddress);
|
||||
nodeList = this.constructNodes(group, layout, nodeOptions, sourceData);
|
||||
markerList = this.constructMarkers(group, layout, markerOptions, nodeList);
|
||||
indexLabelList = this.constructIndexLabel(group, layout, indexLabelOptions, nodeList);
|
||||
addressLabelList = this.constructAddressLabel(group, layout, addressLabelOption, nodeList);
|
||||
nodeList.forEach(item => {
|
||||
if(item.freedLabel) {
|
||||
freedLabelList.push(item.freedLabel);
|
||||
@@ -84,12 +88,19 @@ export class ModelConstructor {
|
||||
name: group,
|
||||
node: nodeList,
|
||||
freedLabel: freedLabelList,
|
||||
leakAddress: leakAddress,
|
||||
addressLabel: addressLabelList,
|
||||
indexLabel: indexLabelList,
|
||||
link: [],
|
||||
marker: markerList,
|
||||
options: options,
|
||||
layoutCreator,
|
||||
modelList: [...nodeList, ...markerList, ...freedLabelList, ...leakAddress],
|
||||
modelList: [
|
||||
...nodeList,
|
||||
...markerList,
|
||||
...freedLabelList,
|
||||
...addressLabelList,
|
||||
...indexLabelList
|
||||
],
|
||||
layout,
|
||||
isHide: false
|
||||
});
|
||||
@@ -97,7 +108,7 @@ export class ModelConstructor {
|
||||
|
||||
layoutGroupTable.forEach((layoutGroup: LayoutGroup, group: string) => {
|
||||
const linkOptions = layoutGroup.options.link || {},
|
||||
linkList: SVLink[] = this.constructLinks(linkOptions, layoutGroup.node, layoutGroupTable, group, layoutGroup.layout, );
|
||||
linkList: SVLink[] = this.constructLinks(group, layoutGroup.layout, linkOptions, layoutGroup.node, layoutGroupTable);
|
||||
|
||||
layoutGroup.link = linkList;
|
||||
layoutGroup.modelList.push(...linkList);
|
||||
@@ -108,13 +119,6 @@ export class ModelConstructor {
|
||||
return this.layoutGroupTable;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
public getLayoutGroupTable(): LayoutGroupTable {
|
||||
return this.layoutGroupTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从源数据构建 node 集
|
||||
@@ -124,7 +128,7 @@ export class ModelConstructor {
|
||||
* @param layout
|
||||
* @returns
|
||||
*/
|
||||
private constructNodes(nodeOptions: { [key: string]: NodeOption }, group: string, sourceList: SourceNode[], layout: string): SVNode[] {
|
||||
private constructNodes(group: string, layout: string, nodeOptions: { [key: string]: NodeOption }, sourceList: SourceNode[]): SVNode[] {
|
||||
let defaultSourceNodeType: string = 'default',
|
||||
nodeList: SVNode[] = [];
|
||||
|
||||
@@ -150,7 +154,7 @@ export class ModelConstructor {
|
||||
* @param layoutGroupTable
|
||||
* @returns
|
||||
*/
|
||||
private constructLinks(linkOptions: { [key: string]: LinkOption }, nodes: SVNode[], layoutGroupTable: LayoutGroupTable, group: string, layout: string): SVLink[] {
|
||||
private constructLinks(group: string, layout: string, linkOptions: { [key: string]: LinkOption }, nodes: SVNode[], layoutGroupTable: LayoutGroupTable): SVLink[] {
|
||||
let linkList: SVLink[] = [],
|
||||
linkNames = Object.keys(linkOptions);
|
||||
|
||||
@@ -197,6 +201,52 @@ export class ModelConstructor {
|
||||
return linkList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从配置项构建 indexLabel 集
|
||||
* @param group
|
||||
* @param layout
|
||||
* @param indexLabelOptions
|
||||
*/
|
||||
private constructIndexLabel(group: string, layout: string, indexLabelOptions: { [key: string]: IndexLabelOption }, nodes: SVNode[]): SVIndexLabel[] {
|
||||
let indexLabelList: SVIndexLabel[] = [],
|
||||
indexNames = Object.keys(indexLabelOptions);
|
||||
|
||||
indexNames.forEach(name => {
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
let node = nodes[i],
|
||||
value = node[name];
|
||||
|
||||
// 若没有指针字段的结点则跳过
|
||||
if (!value) continue;
|
||||
|
||||
let id = `${group}.${name}#${value}`,
|
||||
indexLabel = new SVIndexLabel(id, name, group, layout, value, node, indexLabelOptions[name]);
|
||||
|
||||
indexLabelList.push(indexLabel);
|
||||
}
|
||||
});
|
||||
|
||||
return indexLabelList;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param group
|
||||
* @param layout
|
||||
* @param addressLabelOption
|
||||
* @param nodes
|
||||
*/
|
||||
private constructAddressLabel(group: string, layout: string, addressLabelOption: AddressLabelOption, nodes: SVNode[]): SVAddressLabel[] {
|
||||
let addressLabelList: SVAddressLabel[] = [];
|
||||
|
||||
nodes.forEach(item => {
|
||||
const addressLabel = new SVAddressLabel(`${item.id}-address-label`, item.sourceType, group, layout, item, addressLabelOption);
|
||||
addressLabelList.push(addressLabel);
|
||||
});
|
||||
|
||||
return addressLabelList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从配置和 node 集构建 marker 集
|
||||
* @param markerOptions
|
||||
@@ -216,8 +266,8 @@ export class ModelConstructor {
|
||||
if (!markerData) continue;
|
||||
|
||||
let id = `${group}.${name}.${Array.isArray(markerData) ? markerData.join('-') : markerData}`,
|
||||
marker = this.createMarker(id, name, markerData, group, layout, node, markerOptions[name]);
|
||||
|
||||
marker = new SVMarker(id, name, group, layout, markerData, node, markerOptions[name]);
|
||||
|
||||
markerList.push(marker);
|
||||
}
|
||||
});
|
||||
@@ -259,8 +309,7 @@ export class ModelConstructor {
|
||||
let label: string | string[] = this.resolveNodeLabel(options.label, sourceNode),
|
||||
id = sourceNodeType + '.' + sourceNode.id.toString(),
|
||||
node = new SVNode(id, sourceNodeType, group, layout, sourceNode, label, options);
|
||||
node.leakAddress = new SVLeakAddress(`${id}-leak-adress`, sourceNodeType, group, layout, node);
|
||||
|
||||
|
||||
if(node.freed) {
|
||||
node.freedLabel = new SVFreedLabel(`${id}-freed-label`, sourceNodeType, group, layout, node);
|
||||
}
|
||||
@@ -268,21 +317,6 @@ export class ModelConstructor {
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* 外部指针工厂,创建marker
|
||||
* @param id
|
||||
* @param markerName
|
||||
* @param markerData
|
||||
* @param group
|
||||
* @param layout
|
||||
* @param target
|
||||
* @param options
|
||||
* @returns
|
||||
*/
|
||||
private createMarker(id: string, markerName: string, markerData: string | string[], group: string, layout: string, target: SVNode, options: MarkerOption): SVMarker {
|
||||
return new SVMarker(id, markerName, group, layout, markerData, target, options);;
|
||||
};
|
||||
|
||||
/**
|
||||
* 连线工厂,创建Link
|
||||
* @param linkName
|
||||
@@ -388,10 +422,19 @@ export class ModelConstructor {
|
||||
return counter <= 2;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
public getLayoutGroupTable(): LayoutGroupTable {
|
||||
return this.layoutGroupTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁
|
||||
*/
|
||||
destroy() {
|
||||
this.layoutGroupTable = null;
|
||||
this.prevSourcesStringMap = null;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
import G6 from '@antv/g6';
|
||||
|
||||
|
||||
export default G6.registerNode('array-node', {
|
||||
getAnchorPoints() {
|
||||
return [
|
||||
[0.5, 0],
|
||||
[1, 0.5],
|
||||
[0.5, 1],
|
||||
[0, 0.5]
|
||||
];
|
||||
}
|
||||
}, 'rect');
|
||||
@@ -1,7 +1,7 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { registerNode } from '@antv/g6';
|
||||
|
||||
|
||||
export default G6.registerNode('binary-tree-node', {
|
||||
export default registerNode('binary-tree-node', {
|
||||
draw(cfg, group) {
|
||||
cfg.size = cfg.size;
|
||||
|
||||
@@ -16,7 +16,7 @@ export default G6.registerNode('binary-tree-node', {
|
||||
height: height,
|
||||
stroke: cfg.style.stroke || '#333',
|
||||
cursor: cfg.style.cursor,
|
||||
fill: '#eee'
|
||||
fill: cfg.style.backgroundFill || '#eee'
|
||||
},
|
||||
name: 'wrapper'
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { registerNode, Util } from '@antv/g6';
|
||||
|
||||
|
||||
export default G6.registerNode('clen-queue-pointer', {
|
||||
export default registerNode('clen-queue-pointer', {
|
||||
draw(cfg, group) {
|
||||
let id = cfg.id as string;
|
||||
|
||||
@@ -55,10 +55,10 @@ export default G6.registerNode('clen-queue-pointer', {
|
||||
});
|
||||
|
||||
// rotate(text, angle, G6.Util.transform);
|
||||
translate(text, 0, -75, G6.Util.transform);
|
||||
translate(text, 0, -75, Util.transform);
|
||||
}
|
||||
rotate(keyShape, angle, G6.Util.transform);
|
||||
translate(keyShape, 0, -75, G6.Util.transform);
|
||||
rotate(keyShape, angle, Util.transform);
|
||||
translate(keyShape, 0, -75, Util.transform);
|
||||
|
||||
return keyShape;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { registerNode } from '@antv/g6';
|
||||
|
||||
|
||||
export default G6.registerNode('cursor', {
|
||||
export default registerNode('cursor', {
|
||||
draw(cfg, group) {
|
||||
const keyShape = group.addShape('path', {
|
||||
attrs: {
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
import G6 from '@antv/g6';
|
||||
|
||||
|
||||
export default G6.registerNode('indexed-node', {
|
||||
draw(cfg, group) {
|
||||
cfg.size = cfg.size || [30, 10];
|
||||
|
||||
const width = cfg.size[0],
|
||||
height = cfg.size[1],
|
||||
disable = cfg.disable === undefined ? false : cfg.disable;
|
||||
|
||||
const rect = group.addShape('rect', {
|
||||
attrs: {
|
||||
x: width / 2,
|
||||
y: height / 2,
|
||||
width: width,
|
||||
height: height,
|
||||
stroke: cfg.style.stroke || '#333',
|
||||
fill: disable ? '#ccc' : cfg.style.fill,
|
||||
cursor: cfg.style.cursor,
|
||||
},
|
||||
name: 'wrapper'
|
||||
});
|
||||
|
||||
if (cfg.label) {
|
||||
const style = (cfg.labelCfg && cfg.labelCfg.style) || {};
|
||||
group.addShape('text', {
|
||||
attrs: {
|
||||
x: width,
|
||||
y: height,
|
||||
textAlign: 'center',
|
||||
textBaseline: 'middle',
|
||||
text: cfg.label,
|
||||
fill: style.fill || '#000',
|
||||
fontSize: style.fontSize || 16
|
||||
},
|
||||
name: 'text'
|
||||
});
|
||||
}
|
||||
|
||||
const indexCfg = cfg.indexCfg;
|
||||
const offset = 20;
|
||||
const indexPositionMap: { [key: string]: (width: number, height: number) => { x: number, y: number } } = {
|
||||
top: (width: number, height: number) => ({ x: width, y: height / 2 - offset }),
|
||||
right: (width: number, height: number) => ({ x: width * 1.5 + offset, y: height }),
|
||||
bottom: (width: number, height: number) => ({ x: width, y: height * 1.5 + offset }),
|
||||
left: (width: number, height: number) => ({ x: width / 2 - offset, y: height })
|
||||
};
|
||||
|
||||
if (indexCfg !== undefined) {
|
||||
Object.keys(indexCfg).map(key => {
|
||||
let indexCfgItem = indexCfg[key];
|
||||
let position = indexCfgItem.position || 'bottom';
|
||||
let { x: indexX, y: indexY } = indexPositionMap[position](width, height);
|
||||
|
||||
group.addShape('text', {
|
||||
attrs: {
|
||||
x: indexX,
|
||||
y: indexY,
|
||||
textAlign: 'center',
|
||||
textBaseline: 'middle',
|
||||
text: indexCfgItem.value.toString(),
|
||||
fill: '#bbb',
|
||||
fontSize: 14,
|
||||
fontStyle: 'italic',
|
||||
...indexCfgItem.style
|
||||
},
|
||||
name: 'index-text'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return rect;
|
||||
},
|
||||
|
||||
getAnchorPoints() {
|
||||
return [
|
||||
[0.5, 0],
|
||||
[1, 0.5],
|
||||
[0.5, 1],
|
||||
[0, 0.5]
|
||||
];
|
||||
}
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { registerNode } from '@antv/g6';
|
||||
|
||||
|
||||
export default G6.registerNode('link-list-node', {
|
||||
export default registerNode('link-list-node', {
|
||||
draw(cfg, group) {
|
||||
cfg.size = cfg.size || [30, 10];
|
||||
|
||||
@@ -15,7 +15,7 @@ export default G6.registerNode('link-list-node', {
|
||||
width: width,
|
||||
height: height,
|
||||
stroke: cfg.style.stroke || '#333',
|
||||
fill: '#eee',
|
||||
fill: cfg.style.backgroundFill || '#eee',
|
||||
cursor: cfg.style.cursor
|
||||
},
|
||||
name: 'wrapper'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { registerNode } from '@antv/g6';
|
||||
|
||||
|
||||
export default G6.registerNode('pointer', {
|
||||
export default registerNode('pointer', {
|
||||
draw(cfg, group) {
|
||||
const keyShape = group.addShape('path', {
|
||||
attrs: {
|
||||
@@ -13,7 +13,7 @@ export default G6.registerNode('pointer', {
|
||||
});
|
||||
|
||||
if (cfg.label) {
|
||||
const style = (cfg.labelCfg && cfg.labelCfg.style) || {};
|
||||
const labelStyle = (cfg.labelCfg && cfg.labelCfg.style) || {};
|
||||
|
||||
const bgRect = group.addShape('rect', {
|
||||
attrs: {
|
||||
@@ -33,8 +33,8 @@ export default G6.registerNode('pointer', {
|
||||
textAlign: 'center',
|
||||
textBaseline: 'middle',
|
||||
text: cfg.label,
|
||||
fill: style.fill || '#999',
|
||||
fontSize: style.fontSize || 16
|
||||
fill: labelStyle.fill || '#999',
|
||||
fontSize: labelStyle.fontSize || 16
|
||||
},
|
||||
name: 'pointer-text-shape'
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { registerNode } from '@antv/g6';
|
||||
|
||||
|
||||
export default G6.registerNode('tri-tree-node', {
|
||||
export default registerNode('tri-tree-node', {
|
||||
draw(cfg, group) {
|
||||
cfg.size = cfg.size;
|
||||
|
||||
@@ -16,7 +16,7 @@ export default G6.registerNode('tri-tree-node', {
|
||||
height: height,
|
||||
stroke: cfg.style.stroke || '#333',
|
||||
cursor: cfg.style.cursor,
|
||||
fill: '#eee'
|
||||
fill: cfg.style.backgroundFill || '#eee'
|
||||
},
|
||||
name: 'wrapper'
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { registerNode } from '@antv/g6';
|
||||
|
||||
|
||||
|
||||
export default G6.registerNode('two-cell-node', {
|
||||
export default registerNode('two-cell-node', {
|
||||
draw(cfg, group) {
|
||||
cfg.size = cfg.size || [30, 10];
|
||||
|
||||
@@ -16,7 +16,7 @@ export default G6.registerNode('two-cell-node', {
|
||||
width: width,
|
||||
height: height,
|
||||
stroke: cfg.style.stroke,
|
||||
fill: '#eee'
|
||||
fill: cfg.style.backgroundFill || '#eee'
|
||||
},
|
||||
name: 'wrapper'
|
||||
});
|
||||
|
||||
+10
-11
@@ -1,21 +1,20 @@
|
||||
import { Engine } from "./engine";
|
||||
import { Bound } from "./Common/boundingRect";
|
||||
import { Group } from "./Common/group";
|
||||
import pointer from "./RegisteredShape/pointer";
|
||||
import G6, { Util } from '@antv/g6';
|
||||
import linkListNode from "./RegisteredShape/linkListNode";
|
||||
import binaryTreeNode from "./RegisteredShape/binaryTreeNode";
|
||||
import Pointer from "./RegisteredShape/pointer";
|
||||
import LinkListNode from "./RegisteredShape/linkListNode";
|
||||
import BinaryTreeNode from "./RegisteredShape/binaryTreeNode";
|
||||
import CLenQueuePointer from "./RegisteredShape/clenQueuePointer";
|
||||
import twoCellNode from "./RegisteredShape/twoCellNode";
|
||||
import TwoCellNode from "./RegisteredShape/twoCellNode";
|
||||
import ArrayNode from "./RegisteredShape/arrayNode";
|
||||
import Cursor from "./RegisteredShape/cursor";
|
||||
import { Vector } from "./Common/vector";
|
||||
import indexedNode from "./RegisteredShape/indexedNode";
|
||||
import { EngineOptions, LayoutCreator } from "./options";
|
||||
import { SVNode } from "./Model/SVNode";
|
||||
import { SourceNode } from "./sources";
|
||||
|
||||
|
||||
|
||||
export interface StructV {
|
||||
(DOMContainer: HTMLElement, engineOptions: EngineOptions): Engine;
|
||||
Group: typeof Group;
|
||||
@@ -51,12 +50,12 @@ SV.G6 = G6;
|
||||
|
||||
SV.registeredLayout = {};
|
||||
SV.registeredShape = [
|
||||
pointer,
|
||||
linkListNode,
|
||||
binaryTreeNode,
|
||||
twoCellNode,
|
||||
indexedNode,
|
||||
Pointer,
|
||||
LinkListNode,
|
||||
BinaryTreeNode,
|
||||
TwoCellNode,
|
||||
Cursor,
|
||||
ArrayNode,
|
||||
CLenQueuePointer,
|
||||
];
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Util } from '@antv/g6';
|
||||
import { Util, Item } from '@antv/g6';
|
||||
|
||||
|
||||
export type animationConfig = {
|
||||
duration: number;
|
||||
timingFunction: string;
|
||||
callback?: Function;
|
||||
callback?: () => void;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ export const Animations = {
|
||||
* @param G6Item
|
||||
* @param animationConfig
|
||||
*/
|
||||
APPEND(G6Item: any, animationConfig: animationConfig) {
|
||||
APPEND(G6Item: Item, animationConfig: animationConfig) {
|
||||
const type = G6Item.getType(),
|
||||
group = G6Item.getContainer(),
|
||||
Mat3 = Util.mat3,
|
||||
@@ -54,7 +54,7 @@ export const Animations = {
|
||||
* @param G6Item
|
||||
* @param animationConfig
|
||||
*/
|
||||
REMOVE(G6Item: any, animationConfig: animationConfig) {
|
||||
REMOVE(G6Item: Item, animationConfig: animationConfig) {
|
||||
const type = G6Item.getType(),
|
||||
group = G6Item.getContainer(),
|
||||
Mat3 = Util.mat3,
|
||||
@@ -84,7 +84,7 @@ export const Animations = {
|
||||
* @param G6Item
|
||||
* @param animationConfig
|
||||
*/
|
||||
FADE_IN(G6Item: any, animationConfig: animationConfig) {
|
||||
FADE_IN(G6Item: Item, animationConfig: animationConfig) {
|
||||
const group = G6Item.getContainer(),
|
||||
animateCfg = {
|
||||
duration: animationConfig.duration,
|
||||
|
||||
+110
-41
@@ -1,13 +1,13 @@
|
||||
import { IPoint } from '@antv/g6-core';
|
||||
import { Bound, BoundingRect } from '../Common/boundingRect';
|
||||
import { Group } from '../Common/group';
|
||||
import { Util } from '../Common/util';
|
||||
import { Vector } from '../Common/vector';
|
||||
import { Engine } from '../engine';
|
||||
import { LayoutGroupTable } from '../Model/modelConstructor';
|
||||
import { SVMarker } from '../Model/SVMarker';
|
||||
import { SVModel } from '../Model/SVModel';
|
||||
import { SVFreedLabel, SVLeakAddress, SVNode } from '../Model/SVNode';
|
||||
import { LayoutOptions, MarkerOption, ViewOptions } from '../options';
|
||||
import { SVAddressLabel, SVFreedLabel, SVIndexLabel, SVMarker } from '../Model/SVNodeAppendage';
|
||||
import { AddressLabelOption, IndexLabelOption, LayoutOptions, MarkerOption, ViewOptions } from '../options';
|
||||
import { ViewContainer } from './viewContainer';
|
||||
|
||||
|
||||
@@ -24,17 +24,37 @@ export class LayoutProvider {
|
||||
|
||||
|
||||
/**
|
||||
* 初始化布局参数
|
||||
* @param nodes
|
||||
* @param markers
|
||||
* 布局前处理
|
||||
* @param layoutGroupTable
|
||||
*/
|
||||
private initLayoutValue(nodes: SVNode[], markers: SVMarker[]) {
|
||||
[...nodes, ...markers].forEach(item => {
|
||||
private preLayoutProcess(layoutGroupTable: LayoutGroupTable) {
|
||||
const modelList = Util.convertGroupTable2ModelList(layoutGroupTable);
|
||||
|
||||
modelList.forEach(item => {
|
||||
item.preLayout = true;
|
||||
|
||||
item.set('rotation', item.get('rotation'));
|
||||
item.set({ x: 0, y: 0 });
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 布局后处理
|
||||
* @param layoutGroupTable
|
||||
*/
|
||||
private postLayoutProcess(layoutGroupTable: LayoutGroupTable) {
|
||||
const modelList = Util.convertGroupTable2ModelList(layoutGroupTable);
|
||||
|
||||
modelList.forEach(item => {
|
||||
item.preLayout = false;
|
||||
|
||||
// 用两个变量保存节点布局完成后的坐标,因为拖拽节点会改变节点的x,y坐标
|
||||
// 然后当节点移动到泄漏区的时候,不应该保持节点被拖拽后的状态,应该恢复到布局完成后的状态,不然就会很奇怪
|
||||
item.layoutX = item.get('x');
|
||||
item.layoutY = item.get('y');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 布局外部指针
|
||||
* @param marker
|
||||
@@ -89,7 +109,7 @@ export class LayoutProvider {
|
||||
*/
|
||||
private layoutFreedLabel(freedLabels: SVFreedLabel[]) {
|
||||
freedLabels.forEach(item => {
|
||||
const freedNodeBound = item.node.getBound();
|
||||
const freedNodeBound = item.target.getBound();
|
||||
|
||||
item.set({
|
||||
x: freedNodeBound.x + freedNodeBound.width / 2,
|
||||
@@ -99,18 +119,64 @@ export class LayoutProvider {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param indexLabels
|
||||
* @param indexLabelOptions
|
||||
*/
|
||||
private layoutIndexLabel(indexLabels: SVIndexLabel[], indexLabelOptions: { [key: string]: IndexLabelOption }) {
|
||||
const indexLabelPositionMap: { [key: string]: (nodeBound: BoundingRect, labelBound: BoundingRect, offset: number) => { x: number, y: number } } = {
|
||||
top: (nodeBound: BoundingRect, labelBound: BoundingRect, offset: number) => {
|
||||
return {
|
||||
x: nodeBound.x + nodeBound.width / 2,
|
||||
y: nodeBound.y - offset
|
||||
};
|
||||
},
|
||||
right: (nodeBound: BoundingRect, labelBound: BoundingRect, offset: number) => {
|
||||
return {
|
||||
x: nodeBound.x + nodeBound.width + offset,
|
||||
y: nodeBound.y + nodeBound.height / 2
|
||||
};
|
||||
},
|
||||
bottom: (nodeBound: BoundingRect, labelBound: BoundingRect, offset: number) => {
|
||||
return {
|
||||
x: nodeBound.x + nodeBound.width / 2,
|
||||
y: nodeBound.y + nodeBound.height + offset
|
||||
};
|
||||
},
|
||||
left: (nodeBound: BoundingRect, labelBound: BoundingRect, offset: number) => {
|
||||
return {
|
||||
x: nodeBound.x - labelBound.width - 2 * offset,
|
||||
y: nodeBound.y + nodeBound.height / 2
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
indexLabels.forEach(item => {
|
||||
const options: IndexLabelOption = indexLabelOptions[item.sourceType],
|
||||
nodeBound = item.target.getBound(),
|
||||
labelBound = item.getBound(),
|
||||
offset = options.offset ?? 20,
|
||||
position = options.position ?? 'bottom';
|
||||
|
||||
const pos = indexLabelPositionMap[position](nodeBound, labelBound, offset);
|
||||
item.set(pos);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 布局泄漏区节点上面的address label
|
||||
* @param leakAddress
|
||||
*/
|
||||
private layoutLeakAddress(leakAddress: SVLeakAddress[]) {
|
||||
private layoutAddressLabel(leakAddress: SVAddressLabel[], addressLabelOption: AddressLabelOption) {
|
||||
const offset = addressLabelOption.offset || 16;
|
||||
|
||||
leakAddress.forEach(item => {
|
||||
const nodeBound = item.node.getBound();
|
||||
const nodeBound = item.target.getBound();
|
||||
|
||||
item.set({
|
||||
x: nodeBound.x + nodeBound.width / 2,
|
||||
y: nodeBound.y - 16,
|
||||
size: [nodeBound.width, 0]
|
||||
y: nodeBound.y - offset
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -124,23 +190,28 @@ export class LayoutProvider {
|
||||
const modelGroupList: Group[] = [];
|
||||
|
||||
layoutGroupTable.forEach(group => {
|
||||
const options: LayoutOptions = group.options.layout,
|
||||
modelList: SVModel[] = group.modelList,
|
||||
const modelList: SVModel[] = group.modelList,
|
||||
modelGroup: Group = new Group();
|
||||
|
||||
const layoutOptions: LayoutOptions = group.options.layout;
|
||||
|
||||
modelList.forEach(item => {
|
||||
modelGroup.add(item);
|
||||
});
|
||||
|
||||
this.initLayoutValue(group.node, group.marker); // 初始化布局参数
|
||||
group.layoutCreator.layout(group.node, options); // 布局节点
|
||||
group.layoutCreator.layout(group.node, layoutOptions); // 布局节点
|
||||
modelGroupList.push(modelGroup);
|
||||
});
|
||||
|
||||
layoutGroupTable.forEach(group => {
|
||||
const markerOptions = group.options.marker || {},
|
||||
indexLabelOptions = group.options.indexLabel || {},
|
||||
addressLabelOption = group.options.addressLabel || {};
|
||||
|
||||
this.layoutIndexLabel(group.indexLabel, indexLabelOptions);
|
||||
this.layoutFreedLabel(group.freedLabel);
|
||||
this.layoutLeakAddress(group.leakAddress);
|
||||
this.layoutMarker(group.marker, group.options.marker); // 布局外部指针
|
||||
this.layoutAddressLabel(group.addressLabel, addressLabelOption);
|
||||
this.layoutMarker(group.marker, markerOptions); // 布局外部指针
|
||||
});
|
||||
|
||||
return modelGroupList;
|
||||
@@ -154,10 +225,17 @@ export class LayoutProvider {
|
||||
private layoutLeakModels(leakModels: SVModel[], accumulateLeakModels: SVModel[]) {
|
||||
const group: Group = new Group(),
|
||||
containerHeight = this.viewContainer.getG6Instance().getHeight(),
|
||||
leakAreaHeightRatio = this.engine.viewOptions.leakAreaHeight,
|
||||
leakAreaY = containerHeight * (1 - leakAreaHeightRatio),
|
||||
leakAreaHeight = this.engine.viewOptions.leakAreaHeight,
|
||||
leakAreaY = containerHeight - leakAreaHeight,
|
||||
xOffset = 50;
|
||||
|
||||
leakModels.forEach(item => {
|
||||
item.set({
|
||||
x: item.layoutX,
|
||||
y: item.layoutY
|
||||
});
|
||||
});
|
||||
|
||||
group.add(...leakModels);
|
||||
const currentLeakGroupBound: BoundingRect = group.getBound(),
|
||||
globalLeakGroupBound: BoundingRect = accumulateLeakModels.length ?
|
||||
@@ -181,8 +259,7 @@ export class LayoutProvider {
|
||||
prevBound: BoundingRect,
|
||||
bound: BoundingRect,
|
||||
boundList: BoundingRect[] = [],
|
||||
maxHeight: number = -Infinity,
|
||||
dx = 0, dy = 0;
|
||||
dx = 0;
|
||||
|
||||
// 左往右布局
|
||||
for (let i = 0; i < modelGroupList.length; i++) {
|
||||
@@ -196,10 +273,6 @@ export class LayoutProvider {
|
||||
dx = bound.x;
|
||||
}
|
||||
|
||||
if (bound.height > maxHeight) {
|
||||
maxHeight = bound.height;
|
||||
}
|
||||
|
||||
group.translate(dx, 0);
|
||||
Bound.translate(bound, dx, 0);
|
||||
boundList.push(bound);
|
||||
@@ -207,16 +280,6 @@ export class LayoutProvider {
|
||||
prevBound = bound;
|
||||
}
|
||||
|
||||
// 居中对齐布局
|
||||
for (let i = 0; i < modelGroupList.length; i++) {
|
||||
group = modelGroupList[i];
|
||||
bound = boundList[i];
|
||||
|
||||
dy = maxHeight / 2 - bound.height / 2;
|
||||
group.translate(0, dy);
|
||||
Bound.translate(bound, 0, dy);
|
||||
}
|
||||
|
||||
return wrapperGroup;
|
||||
}
|
||||
|
||||
@@ -229,10 +292,10 @@ export class LayoutProvider {
|
||||
private fitCenter(group: Group) {
|
||||
let width = this.viewContainer.getG6Instance().getWidth(),
|
||||
height = this.viewContainer.getG6Instance().getHeight(),
|
||||
leakAreaHeightRatio = this.engine.viewOptions.leakAreaHeight;
|
||||
leakAreaHeight = this.engine.viewOptions.leakAreaHeight;
|
||||
|
||||
if (this.viewContainer.hasLeak) {
|
||||
height = height * (1 - leakAreaHeightRatio);
|
||||
height = height - leakAreaHeight;
|
||||
}
|
||||
|
||||
const viewBound: BoundingRect = group.getBound(),
|
||||
@@ -245,20 +308,26 @@ export class LayoutProvider {
|
||||
group.translate(dx, dy);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 布局
|
||||
* @param layoutGroupTable
|
||||
* @param leakModels
|
||||
* @param hasLeak
|
||||
* @param needFitCenter
|
||||
*/
|
||||
public layoutAll(layoutGroupTable: LayoutGroupTable, accumulateLeakModels: SVModel[], leakModels: SVModel[]) {
|
||||
this.preLayoutProcess(layoutGroupTable);
|
||||
|
||||
const modelGroupList: Group[] = this.layoutModels(layoutGroupTable);
|
||||
const globalGroup: Group = this.layoutGroups(modelGroupList);
|
||||
const generalGroup: Group = this.layoutGroups(modelGroupList);
|
||||
|
||||
if (leakModels.length) {
|
||||
this.layoutLeakModels(leakModels, accumulateLeakModels);
|
||||
}
|
||||
|
||||
this.fitCenter(globalGroup);
|
||||
this.fitCenter(generalGroup);
|
||||
this.postLayoutProcess(layoutGroupTable);
|
||||
}
|
||||
}
|
||||
+34
-29
@@ -2,9 +2,9 @@ import { EventBus } from "../Common/eventBus";
|
||||
import { Util } from "../Common/util";
|
||||
import { Engine } from "../engine";
|
||||
import { SVLink } from "../Model/SVLink";
|
||||
import { SVMarker } from "../Model/SVMarker";
|
||||
import { SVModel } from "../Model/SVModel";
|
||||
import { SVLeakAddress, SVNode } from "../Model/SVNode";
|
||||
import { SVNode } from "../Model/SVNode";
|
||||
import { SVAddressLabel, SVMarker, SVNodeAppendage } from "../Model/SVNodeAppendage";
|
||||
import { Animations } from "./animation";
|
||||
import { Renderer } from "./renderer";
|
||||
|
||||
@@ -56,7 +56,7 @@ export class Reconcile {
|
||||
appendModels.forEach(item => {
|
||||
let removeIndex = accumulateLeakModels.findIndex(leakModel => item.id === leakModel.id);
|
||||
|
||||
if(removeIndex > -1) {
|
||||
if (removeIndex > -1) {
|
||||
accumulateLeakModels.splice(removeIndex, 1);
|
||||
}
|
||||
});
|
||||
@@ -81,18 +81,10 @@ export class Reconcile {
|
||||
item.leaked = true;
|
||||
leakModels.push(item);
|
||||
|
||||
if (item.marker) {
|
||||
item.marker.leaked = true;
|
||||
leakModels.push(item.marker);
|
||||
}
|
||||
|
||||
if(item.freedLabel) {
|
||||
item.marker.leaked = true;
|
||||
leakModels.push(item.freedLabel);
|
||||
}
|
||||
|
||||
item.leakAddress.leaked = true;
|
||||
leakModels.push(item.leakAddress);
|
||||
item.appendages.forEach(appendage => {
|
||||
appendage.leaked = true;
|
||||
leakModels.push(appendage);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -180,13 +172,13 @@ export class Reconcile {
|
||||
* @param modelList
|
||||
* @returns
|
||||
*/
|
||||
private getFreedModels(prevModelList: SVModel[], modelList: SVModel[]): SVNode[] {
|
||||
private getFreedModels(prevModelList: SVModel[], modelList: SVModel[]): SVNode[] {
|
||||
const freedNodes = modelList.filter(item => item instanceof SVNode && item.freed) as SVNode[];
|
||||
|
||||
|
||||
freedNodes.forEach(item => {
|
||||
const prev = prevModelList.find(prevModel => item.id === prevModel.id);
|
||||
|
||||
if(prev) {
|
||||
if (prev) {
|
||||
item.set('label', prev.get('label'));
|
||||
}
|
||||
});
|
||||
@@ -201,10 +193,10 @@ export class Reconcile {
|
||||
* @param continuousModels
|
||||
*/
|
||||
private handleContinuousModels(continuousModels: SVModel[]) {
|
||||
for(let i = 0; i < continuousModels.length; i++) {
|
||||
for (let i = 0; i < continuousModels.length; i++) {
|
||||
let model = continuousModels[i];
|
||||
|
||||
if(model instanceof SVNode) {
|
||||
if (model instanceof SVNode) {
|
||||
const group = model.G6Item.getContainer();
|
||||
group.attr({ opacity: 1 });
|
||||
}
|
||||
@@ -219,9 +211,19 @@ export class Reconcile {
|
||||
let { duration, timingFunction } = this.engine.animationOptions;
|
||||
|
||||
appendModels.forEach(item => {
|
||||
if(item instanceof SVLeakAddress) {
|
||||
const leakAddressG6Group = item.G6Item.getContainer();
|
||||
leakAddressG6Group.attr({ opacity: 0 });
|
||||
if (item instanceof SVNodeAppendage) {
|
||||
// 先不显示泄漏区节点上面的地址文本
|
||||
if (item instanceof SVAddressLabel) {
|
||||
// 先将透明度改为0,隐藏掉
|
||||
const AddressLabelG6Group = item.G6Item.getContainer();
|
||||
AddressLabelG6Group.attr({ opacity: 0 });
|
||||
}
|
||||
else {
|
||||
Animations.FADE_IN(item.G6Item, {
|
||||
duration,
|
||||
timingFunction
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
Animations.APPEND(item.G6Item, {
|
||||
@@ -258,7 +260,7 @@ export class Reconcile {
|
||||
let { duration, timingFunction } = this.engine.animationOptions;
|
||||
|
||||
leakModels.forEach(item => {
|
||||
if(item instanceof SVLeakAddress) {
|
||||
if (item instanceof SVAddressLabel) {
|
||||
Animations.FADE_IN(item.G6Item, {
|
||||
duration,
|
||||
timingFunction
|
||||
@@ -275,7 +277,7 @@ export class Reconcile {
|
||||
*/
|
||||
private handleAccumulateLeakModels(accumulateModels: SVModel[]) {
|
||||
accumulateModels.forEach(item => {
|
||||
if(item.generalStyle) {
|
||||
if (item.generalStyle) {
|
||||
item.set('style', { ...item.generalStyle });
|
||||
}
|
||||
});
|
||||
@@ -288,7 +290,7 @@ export class Reconcile {
|
||||
*/
|
||||
private handleFreedModels(freedModes: SVNode[]) {
|
||||
const { duration, timingFunction } = this.engine.animationOptions,
|
||||
alpha = 0.4;
|
||||
alpha = 0.4;
|
||||
|
||||
freedModes.forEach(item => {
|
||||
const nodeGroup = item.G6Item.getContainer();
|
||||
@@ -321,7 +323,7 @@ export class Reconcile {
|
||||
}
|
||||
|
||||
models.forEach(item => {
|
||||
if(item.generalStyle === undefined) {
|
||||
if (item.generalStyle === undefined) {
|
||||
item.generalStyle = Util.objectClone(item.G6ModelProps.style);
|
||||
}
|
||||
|
||||
@@ -387,6 +389,8 @@ export class Reconcile {
|
||||
ACCUMULATE_LEAK
|
||||
} = diffResult;
|
||||
|
||||
this.handleAccumulateLeakModels(ACCUMULATE_LEAK);
|
||||
|
||||
// 第一次渲染的时候不高亮变化的元素
|
||||
if (this.isFirstPatch === false) {
|
||||
this.handleChangeModels(UPDATE);
|
||||
@@ -397,10 +401,11 @@ export class Reconcile {
|
||||
this.handleAppendModels(APPEND);
|
||||
this.handleLeakModels(LEAKED);
|
||||
this.handleRemoveModels(REMOVE);
|
||||
this.handleAccumulateLeakModels(ACCUMULATE_LEAK);
|
||||
|
||||
if(this.isFirstPatch) {
|
||||
if (this.isFirstPatch) {
|
||||
this.isFirstPatch = false;
|
||||
}
|
||||
}
|
||||
|
||||
public destroy() { }
|
||||
}
|
||||
+28
-6
@@ -1,9 +1,8 @@
|
||||
import { Engine } from '../engine';
|
||||
import { SVModel } from '../Model/SVModel';
|
||||
import { Util } from '../Common/util';
|
||||
import G6 from '@antv/g6';
|
||||
import { Tooltip, Graph, GraphData } from '@antv/g6';
|
||||
import { InitViewBehaviors } from '../BehaviorHelper/initViewBehaviors';
|
||||
import { Graph, GraphData, IGroup } from '@antv/g6-pc';
|
||||
|
||||
|
||||
|
||||
@@ -28,7 +27,7 @@ export class Renderer {
|
||||
duration: number = this.engine.animationOptions.duration,
|
||||
timingFunction: string = this.engine.animationOptions.timingFunction;
|
||||
|
||||
const tooltip = new G6.Tooltip({
|
||||
const tooltip = new Tooltip({
|
||||
offsetX: 10,
|
||||
offsetY: 20,
|
||||
shouldBegin(event) {
|
||||
@@ -38,12 +37,12 @@ export class Renderer {
|
||||
itemTypes: ['node']
|
||||
});
|
||||
|
||||
this.shadowG6Instance = new G6.Graph({
|
||||
this.shadowG6Instance = new Graph({
|
||||
container: DOMContainer.cloneNode() as HTMLElement
|
||||
});
|
||||
|
||||
// 初始化g6实例
|
||||
this.g6Instance = new G6.Graph({
|
||||
this.g6Instance = new Graph({
|
||||
container: DOMContainer,
|
||||
width: DOMContainer.offsetWidth,
|
||||
height: DOMContainer.offsetHeight,
|
||||
@@ -55,7 +54,7 @@ export class Renderer {
|
||||
},
|
||||
fitView: false,
|
||||
modes: {
|
||||
default: InitViewBehaviors(this.engine.optionsTable)
|
||||
default: InitViewBehaviors()
|
||||
},
|
||||
plugins: [tooltip]
|
||||
});
|
||||
@@ -97,6 +96,7 @@ export class Renderer {
|
||||
this.shadowG6Instance.read(g6Data);
|
||||
renderModelList.forEach(item => {
|
||||
item.shadowG6Item = this.shadowG6Instance.findById(item.id);
|
||||
item.shadowG6Instance = this.shadowG6Instance;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -111,9 +111,13 @@ export class Renderer {
|
||||
this.g6Instance.changeData(renderData);
|
||||
|
||||
renderModelList.forEach(item => {
|
||||
item.g6Instance = this.g6Instance;
|
||||
item.G6Item = this.g6Instance.findById(item.id);
|
||||
item.G6Item['SVModel'] = item;
|
||||
});
|
||||
|
||||
this.g6Instance.getEdges().forEach(item => item.toFront());
|
||||
this.g6Instance.paint();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,6 +136,24 @@ export class Renderer {
|
||||
return this.g6Instance;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public refresh() {
|
||||
this.g6Instance.refresh();
|
||||
this.shadowG6Instance.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param width
|
||||
* @param height
|
||||
*/
|
||||
public changeSize(width: number, height: number) {
|
||||
this.g6Instance.changeSize(width, height);
|
||||
this.shadowG6Instance.changeSize(width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁
|
||||
*/
|
||||
|
||||
+47
-29
@@ -8,7 +8,9 @@ import { Reconcile } from "./reconcile";
|
||||
import { FixNodeMarkerDrag } from "../BehaviorHelper/fixNodeMarkerDrag";
|
||||
import { InitDragCanvasWithLeak } from "../BehaviorHelper/dragCanvasWithLeak";
|
||||
import { EventBus } from "../Common/eventBus";
|
||||
import { InitZoomCanvasWithLeak } from "../BehaviorHelper/zoomCanvasWithLeak";
|
||||
import { Group } from "../Common/group";
|
||||
import { Graph } from "_@antv_g6-pc@0.5.0@@antv/g6-pc";
|
||||
|
||||
|
||||
|
||||
@@ -40,17 +42,17 @@ export class ViewContainer {
|
||||
height = this.getG6Instance().getHeight(),
|
||||
{ drag, zoom } = this.engine.interactionOptions;
|
||||
|
||||
this.leakAreaY = height * (1 - leakAreaHeight);
|
||||
this.leakAreaY = height - leakAreaHeight;
|
||||
|
||||
if (drag) {
|
||||
InitDragCanvasWithLeak(this);
|
||||
}
|
||||
|
||||
if (zoom) {
|
||||
// InitZoomCanvas(g6Instance, g6GeneralGroup);
|
||||
// InitZoomCanvasWithLeak(this);
|
||||
}
|
||||
|
||||
FixNodeMarkerDrag(g6Instance, this.engine.optionsTable);
|
||||
FixNodeMarkerDrag(g6Instance);
|
||||
}
|
||||
|
||||
|
||||
@@ -60,17 +62,37 @@ export class ViewContainer {
|
||||
* 对主视图进行重新布局
|
||||
*/
|
||||
reLayout() {
|
||||
this.layoutProvider.layoutAll(this.prevLayoutGroupTable, [], this.accumulateLeakModels);
|
||||
const g6Instance = this.getG6Instance(),
|
||||
group = g6Instance.getGroup(),
|
||||
matrix = group.getMatrix();
|
||||
|
||||
if (matrix) {
|
||||
let dx = matrix[6],
|
||||
dy = matrix[7];
|
||||
|
||||
g6Instance.translate(-dx, -dy);
|
||||
}
|
||||
|
||||
this.layoutProvider.layoutAll(this.prevLayoutGroupTable, this.accumulateLeakModels, []);
|
||||
g6Instance.refresh();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取 g6 实例
|
||||
*/
|
||||
getG6Instance() {
|
||||
getG6Instance(): Graph {
|
||||
return this.renderer.getG6Instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取泄漏区里面的元素
|
||||
* @returns
|
||||
*/
|
||||
getAccumulateLeakModels(): SVModel[] {
|
||||
return this.accumulateLeakModels;
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新视图
|
||||
*/
|
||||
@@ -84,17 +106,20 @@ export class ViewContainer {
|
||||
* @param height
|
||||
*/
|
||||
resize(width: number, height: number) {
|
||||
this.renderer.getG6Instance().changeSize(width, height);
|
||||
const g6Instance = this.getG6Instance(),
|
||||
prevContainerHeight = g6Instance.getHeight(),
|
||||
globalGroup: Group = new Group();
|
||||
|
||||
const containerHeight = this.getG6Instance().getHeight(),
|
||||
leakAreaHeight = this.engine.viewOptions.leakAreaHeight,
|
||||
targetY = containerHeight * (1 - leakAreaHeight);
|
||||
globalGroup.add(...this.prevModelList, ...this.accumulateLeakModels);
|
||||
this.renderer.changeSize(width, height);
|
||||
|
||||
const accumulateLeakGroup = new Group();
|
||||
accumulateLeakGroup.add(...this.accumulateLeakModels);
|
||||
accumulateLeakGroup.translate(0, targetY - this.leakAreaY);
|
||||
this.leakAreaY = targetY;
|
||||
const containerHeight = g6Instance.getHeight(),
|
||||
dy = containerHeight - prevContainerHeight;
|
||||
|
||||
globalGroup.translate(0, dy);
|
||||
this.renderer.refresh();
|
||||
|
||||
this.leakAreaY += dy;
|
||||
EventBus.emit('onLeakAreaUpdate', {
|
||||
leakAreaY: this.leakAreaY,
|
||||
hasLeak: this.hasLeak
|
||||
@@ -131,7 +156,7 @@ export class ViewContainer {
|
||||
hasLeak: this.hasLeak
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
this.renderer.build(renderModelList); // 首先在离屏canvas渲染先
|
||||
this.layoutProvider.layoutAll(layoutGroupTable, this.accumulateLeakModels, diffResult.LEAKED); // 进行布局(设置model的x,y,样式等)
|
||||
|
||||
@@ -144,10 +169,6 @@ export class ViewContainer {
|
||||
|
||||
this.prevLayoutGroupTable = layoutGroupTable;
|
||||
this.prevModelList = modelList;
|
||||
|
||||
// modelList.forEach(item => {
|
||||
// console.log(item.getModelType(), item.getBound());
|
||||
// });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,6 +176,11 @@ export class ViewContainer {
|
||||
*/
|
||||
destroy() {
|
||||
this.renderer.destroy();
|
||||
this.reconcile.destroy();
|
||||
this.layoutProvider = null;
|
||||
this.prevLayoutGroupTable = null;
|
||||
this.prevModelList.length = 0;
|
||||
this.accumulateLeakModels.length = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -162,15 +188,9 @@ export class ViewContainer {
|
||||
|
||||
|
||||
/**
|
||||
* 把渲染前要触发的逻辑放在这里
|
||||
* 把渲染后要触发的逻辑放在这里
|
||||
*/
|
||||
private afterRender() {
|
||||
const g6Instance = this.renderer.getG6Instance();
|
||||
|
||||
// 把所有连线置顶
|
||||
g6Instance.getEdges().forEach(item => item.toFront());
|
||||
g6Instance.paint();
|
||||
|
||||
this.prevModelList.forEach(item => {
|
||||
if (item.leaked === false) {
|
||||
item.discarded = true;
|
||||
@@ -179,11 +199,9 @@ export class ViewContainer {
|
||||
}
|
||||
|
||||
/**
|
||||
* 把渲染后要触发的逻辑放在这里
|
||||
* 把渲染前要触发的逻辑放在这里
|
||||
*/
|
||||
private beforeRender() {
|
||||
|
||||
}
|
||||
private beforeRender() { }
|
||||
}
|
||||
|
||||
|
||||
|
||||
+77
-116
@@ -1,35 +1,32 @@
|
||||
import { Sources } from "./sources";
|
||||
import { ModelConstructor } from "./Model/modelConstructor";
|
||||
import { AnimationOptions, EngineOptions, InteractionOptions, LayoutGroupOptions, ViewOptions } from "./options";
|
||||
import { SV } from "./StructV";
|
||||
import { AnimationOptions, EngineOptions, InteractionOptions, LayoutGroupOptions, LayoutOptions, ViewOptions } from "./options";
|
||||
import { EventBus } from "./Common/eventBus";
|
||||
import { ViewContainer } from "./View/viewContainer";
|
||||
import { SVLink } from "./Model/SVLink";
|
||||
import { SVNode } from "./Model/SVNode";
|
||||
import { SVMarker } from "./Model/SVMarker";
|
||||
import { Util } from "./Common/util";
|
||||
import { SVModel } from "./Model/SVModel";
|
||||
|
||||
|
||||
export class Engine {
|
||||
export class Engine {
|
||||
private modelConstructor: ModelConstructor;
|
||||
private viewContainer: ViewContainer
|
||||
private prevStringSourceData: string;
|
||||
|
||||
private viewContainer: ViewContainer;
|
||||
private prevSource: Sources;
|
||||
private prevStringSource: string;
|
||||
|
||||
public engineOptions: EngineOptions;
|
||||
public viewOptions: ViewOptions;
|
||||
public animationOptions: AnimationOptions;
|
||||
public interactionOptions: InteractionOptions;
|
||||
|
||||
public optionsTable: { [key: string]: LayoutGroupOptions };
|
||||
|
||||
constructor(DOMContainer: HTMLElement, engineOptions: EngineOptions) {
|
||||
this.optionsTable = {};
|
||||
this.engineOptions = Object.assign({}, engineOptions);
|
||||
|
||||
this.viewOptions = Object.assign({
|
||||
fitCenter: true,
|
||||
fitView: false,
|
||||
groupPadding: 20,
|
||||
leakAreaHeight: 0.3,
|
||||
leakAreaHeight: 150,
|
||||
updateHighlight: '#fc5185'
|
||||
}, engineOptions.view);
|
||||
|
||||
@@ -46,62 +43,41 @@ export class Engine {
|
||||
selectNode: true
|
||||
}, engineOptions.interaction);
|
||||
|
||||
// 初始化布局器配置项
|
||||
Object.keys(SV.registeredLayout).forEach(layout => {
|
||||
if(this.optionsTable[layout] === undefined) {
|
||||
const options: LayoutGroupOptions = SV.registeredLayout[layout].defineOptions();
|
||||
this.optionsTable[layout] = options;
|
||||
}
|
||||
});
|
||||
|
||||
this.modelConstructor = new ModelConstructor(this);
|
||||
this.viewContainer = new ViewContainer(this, DOMContainer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 输入数据进行渲染
|
||||
* @param sourcesData
|
||||
* @param sources
|
||||
* @param force
|
||||
*/
|
||||
public render(sourceData: Sources) {
|
||||
if(sourceData === undefined || sourceData === null) {
|
||||
public render(source: Sources, force: boolean = false) {
|
||||
if (source === undefined || source === null) {
|
||||
return;
|
||||
}
|
||||
``
|
||||
let stringSource = JSON.stringify(source);
|
||||
if (force === false && this.prevStringSource === stringSource) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.viewContainer.getG6Instance().isAnimating()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let stringSourceData = JSON.stringify(sourceData);
|
||||
if(this.prevStringSourceData === stringSourceData) {
|
||||
return;
|
||||
}
|
||||
this.prevStringSourceData = stringSourceData;
|
||||
this.prevSource = source;
|
||||
this.prevStringSource = stringSource;
|
||||
|
||||
// 1 转换模型(data => model)
|
||||
const layoutGroupTable = this.modelConstructor.construct(sourceData);
|
||||
|
||||
const layoutGroupTable = this.modelConstructor.construct(source);
|
||||
|
||||
// 2 渲染(使用g6进行渲染)
|
||||
this.viewContainer.render(layoutGroupTable);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 重新布局
|
||||
*/
|
||||
public reLayout() {
|
||||
this.viewContainer.reLayout();
|
||||
|
||||
// layoutGroupTable.forEach(group => {
|
||||
// group.modelList.forEach(item => {
|
||||
// if(item instanceof SVLink) return;
|
||||
|
||||
// let model = item.G6Item.getModel(),
|
||||
// x = item.get('x'),
|
||||
// y = item.get('y');
|
||||
|
||||
// model.x = x;
|
||||
// model.y = y;
|
||||
// });
|
||||
// });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,97 +87,82 @@ export class Engine {
|
||||
return this.viewContainer.getG6Instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有 element
|
||||
* @param group
|
||||
*/
|
||||
public getNodes(group?: string): SVNode[] {
|
||||
const layoutGroupTable = this.modelConstructor.getLayoutGroupTable();
|
||||
|
||||
if(group && layoutGroupTable.has('group')) {
|
||||
return layoutGroupTable.get('group').node;
|
||||
}
|
||||
|
||||
const nodes: SVNode[] = [];
|
||||
layoutGroupTable.forEach(item => {
|
||||
nodes.push(...item.node);
|
||||
})
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有 marker
|
||||
* @param group
|
||||
*/
|
||||
public getMarkers(group?: string): SVMarker[] {
|
||||
const layoutGroupTable = this.modelConstructor.getLayoutGroupTable();
|
||||
|
||||
if(group && layoutGroupTable.has('group')) {
|
||||
return layoutGroupTable.get('group').marker;
|
||||
}
|
||||
|
||||
const markers: SVMarker[] = [];
|
||||
layoutGroupTable.forEach(item => {
|
||||
markers.push(...item.marker);
|
||||
})
|
||||
|
||||
return markers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有 link
|
||||
* @param group
|
||||
*/
|
||||
public getLinks(group?: string): SVLink[] {
|
||||
const layoutGroupTable = this.modelConstructor.getLayoutGroupTable();
|
||||
|
||||
if(group && layoutGroupTable.has('group')) {
|
||||
return layoutGroupTable.get('group').link;
|
||||
}
|
||||
|
||||
const links: SVLink[] = [];
|
||||
layoutGroupTable.forEach(item => {
|
||||
links.push(...item.link);
|
||||
})
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏某些组
|
||||
* @param groupNames
|
||||
*/
|
||||
public hideGroups(groupNames: string | string[]) {
|
||||
const names = Array.isArray(groupNames)? groupNames: [groupNames],
|
||||
instance = this.viewContainer.getG6Instance(),
|
||||
layoutGroupTable = this.modelConstructor.getLayoutGroupTable();
|
||||
const names = Array.isArray(groupNames) ? groupNames : [groupNames],
|
||||
instance = this.viewContainer.getG6Instance(),
|
||||
layoutGroupTable = this.modelConstructor.getLayoutGroupTable();
|
||||
|
||||
layoutGroupTable.forEach(item => {
|
||||
const hasName = names.find(name => name === item.layout);
|
||||
|
||||
if(hasName && !item.isHide) {
|
||||
if (hasName && !item.isHide) {
|
||||
item.modelList.forEach(model => instance.hideItem(model.G6Item));
|
||||
item.isHide = true;
|
||||
}
|
||||
|
||||
if(!hasName && item.isHide) {
|
||||
if (!hasName && item.isHide) {
|
||||
item.modelList.forEach(model => instance.showItem(model.G6Item));
|
||||
item.isHide = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public getAllModels(): SVModel[] {
|
||||
const modelList = Util.convertGroupTable2ModelList(this.modelConstructor.getLayoutGroupTable());
|
||||
const accumulateLeakModels = this.viewContainer.getAccumulateLeakModels();
|
||||
|
||||
return [...modelList, ...accumulateLeakModels];
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据配置变化更新视图
|
||||
* @param modelType
|
||||
* @returns
|
||||
*/
|
||||
public updateStyle(group: string, newOptions: LayoutGroupOptions) {
|
||||
const models = this.getAllModels(),
|
||||
layoutGroup = this.modelConstructor.getLayoutGroupTable().get(group);
|
||||
|
||||
layoutGroup.options = newOptions;
|
||||
models.forEach(item => {
|
||||
if (item.group !== group) {
|
||||
return;
|
||||
}
|
||||
|
||||
const modelType = item.getModelType(),
|
||||
optionsType = layoutGroup.options[modelType];
|
||||
|
||||
if (optionsType) {
|
||||
if (modelType === 'addressLabel') {
|
||||
item.updateG6ModelStyle(item.generateG6ModelProps(optionsType));
|
||||
}
|
||||
else {
|
||||
const targetModelOption = optionsType[item.sourceType];
|
||||
if (targetModelOption) {
|
||||
item.updateG6ModelStyle(item.generateG6ModelProps(targetModelOption));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用id查找某个节点
|
||||
* @param id
|
||||
*/
|
||||
public findElement(id: string) {
|
||||
const elements = this.getNodes();
|
||||
public findNode(id: string): SVNode {
|
||||
const modelList = this.getAllModels();
|
||||
const stringId = id.toString();
|
||||
const targetElement = elements.find(item => item.sourceId === stringId);
|
||||
const targetNode: SVNode = modelList.find(item => item instanceof SVNode && item.sourceId === stringId) as SVNode;
|
||||
|
||||
return targetElement;
|
||||
return targetNode;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -219,16 +180,16 @@ export class Engine {
|
||||
* @param callback
|
||||
*/
|
||||
public on(eventName: string, callback: Function) {
|
||||
if(typeof callback !== 'function') {
|
||||
if (typeof callback !== 'function') {
|
||||
return;
|
||||
}
|
||||
|
||||
if(eventName === 'onFreed' || eventName === 'onLeak') {
|
||||
if (eventName === 'onFreed' || eventName === 'onLeak') {
|
||||
EventBus.on(eventName, callback);
|
||||
return;
|
||||
}
|
||||
|
||||
if(eventName === 'onLeakAreaUpdate') {
|
||||
if (eventName === 'onLeakAreaUpdate') {
|
||||
EventBus.on(eventName, callback);
|
||||
return;
|
||||
}
|
||||
|
||||
+13
-8
@@ -22,10 +22,14 @@ export interface NodeLabelOption {
|
||||
};
|
||||
|
||||
|
||||
export interface NodeIndexOption extends NodeLabelOption {
|
||||
export interface AddressLabelOption {
|
||||
offset?: number;
|
||||
style?: Style;
|
||||
}
|
||||
|
||||
|
||||
export interface IndexLabelOption extends NodeLabelOption {
|
||||
position: 'top' | 'right' | 'bottom' | 'left';
|
||||
value: string;
|
||||
style: Style;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +53,6 @@ export interface NodeOption extends ModelOption {
|
||||
rotation: number;
|
||||
label: string | string[];
|
||||
anchorPoints: number[][];
|
||||
indexOptions: NodeIndexOption;
|
||||
labelOptions: NodeLabelOption;
|
||||
}
|
||||
|
||||
@@ -78,8 +81,10 @@ export interface LayoutOptions {
|
||||
|
||||
export interface LayoutGroupOptions {
|
||||
node: { [key: string]: NodeOption };
|
||||
link?: { [key: string]: LinkOption }
|
||||
marker?: { [key: string]: MarkerOption }
|
||||
link?: { [key: string]: LinkOption };
|
||||
marker?: { [key: string]: MarkerOption };
|
||||
addressLabel?: AddressLabelOption;
|
||||
indexLabel?: { [key: string]: IndexLabelOption };
|
||||
layout?: LayoutOptions;
|
||||
};
|
||||
|
||||
@@ -118,8 +123,8 @@ export interface EngineOptions {
|
||||
|
||||
|
||||
export interface LayoutCreator {
|
||||
defineOptions(): LayoutGroupOptions;
|
||||
sourcesPreprocess?(sources: SourceNode[], options: LayoutGroupOptions): SourceNode[];
|
||||
defineOptions(sourceData: SourceNode[]): LayoutGroupOptions;
|
||||
sourcesPreprocess?(sourceData: SourceNode[], options: LayoutGroupOptions): SourceNode[];
|
||||
defineLeakRule?(nodes: SVNode[]): SVNode[];
|
||||
layout(nodes: SVNode[], layoutOptions: LayoutOptions);
|
||||
[key: string]: any;
|
||||
|
||||
+4
-1
@@ -16,7 +16,10 @@ export interface SourceNode {
|
||||
|
||||
|
||||
export type Sources = {
|
||||
[key: string]: { data: SourceNode[]; layouter: string; }
|
||||
[key: string]: {
|
||||
data: SourceNode[];
|
||||
layouter: string;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user