StructV2/src/View/viewManager.ts
Phenom 2baed0b927 Revert "Merge branch 'feat-stage' into 'main'"
This reverts merge request !1
2021-12-02 09:22:05 +00:00

292 lines
8.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Engine } from "../engine";
import { Element, Link, Marker, Model } from "../Model/modelData";
import { EngineOptions } from "../options";
import { Container } from "./container/container";
import { SV } from '../StructV';
import { MainContainer } from "./container/main";
import { FreedContainer } from "./container/freed";
import { LeakContainer } from "./container/leak";
import { Layouter } from "./layouter";
import { LayoutGroup, LayoutGroupTable } from "../Model/modelConstructor";
import { Util } from "../Common/util";
import { EventBus } from "../Common/eventBus";
export class ViewManager {
private engine: Engine;
private layouter: Layouter;
private mainContainer: Container;
private freedContainer: FreedContainer;
private leakContainer: LeakContainer;
private prevLayoutGroupTable: LayoutGroupTable;
private prevModelList: Model[];
private prevFreedElements: Element[];
private shadowG6Instance;
constructor(engine: Engine, DOMContainer: HTMLElement) {
this.engine = engine;
this.layouter = new Layouter(engine);
this.mainContainer = new MainContainer(engine, DOMContainer, { tooltip: true });
this.prevLayoutGroupTable = new Map();
this.prevFreedElements = [];
this.prevModelList = [];
const options: EngineOptions = this.engine.engineOptions;
if (options.freedContainer) {
this.freedContainer = new FreedContainer(engine, options.freedContainer, { fitCenter: true, tooltip: true });
}
if (options.leakContainer) {
this.leakContainer = new LeakContainer(engine, options.leakContainer, { fitCenter: true, tooltip: false });
}
this.shadowG6Instance = new SV.G6.Graph({
container: DOMContainer.cloneNode()
});
}
/**
* 对每一个 model 在离屏 Canvas 上构建 G6 item用作布局
* @param constructList
*/
private build(modelList: Model[]) {
modelList.forEach(item => {
const type = item instanceof Link ? 'edge' : 'node';
this.shadowG6Instance.addItem(type, item.cloneProps());
item.shadowG6Item = this.shadowG6Instance.findById(item.id);
});
}
/**
*
* @param freedElement
* @param prevLayoutGroup
*/
private handleFreedLabel(freedElement: Element[], prevLayoutGroup: LayoutGroup) {
if (prevLayoutGroup === undefined) {
return;
}
const prevElementList: Element[] = prevLayoutGroup.element;
freedElement.map(item => {
let prevElement = prevElementList.find(el => el.id === item.id),
prevLabel = prevElement.get('label') ?? '';
item.set('label', prevLabel);
});
}
/**
* 获取被 free 的节点
* @param layoutGroupTable
* @returns
*/
private getFreedModelList(prevLayoutGroupTable: LayoutGroupTable, layoutGroupTable: LayoutGroupTable): Model[] {
let freedElements: Element[] = [],
freedMarkers: Marker[] = [],
removeModels: Model[] = [],
freedGroupName: string;
layoutGroupTable.forEach((group, key) => {
let targetElements: Element[] = group.element.filter(item => item.freed);
targetElements.forEach(fItem => {
removeModels.push(
...Util.removeFromList(group.element, item => item.id === fItem.id),
...Util.removeFromList(group.link, item => item.element.id === fItem.id || item.target.id === fItem.id),
...Util.removeFromList(group.marker, item => item.target.id === fItem.id),
);
});
removeModels.map(model => {
Util.removeFromList(group.modelList, item => item.id === model.id);
});
// 找出最新的freed节点防止上一次的freed节点被遗留在该次渲染此时会出现大于一个freed节点
targetElements.forEach(item => {
if (this.prevFreedElements.find(prevEle => prevEle.id === item.id) === undefined) {
freedGroupName = key;
freedElements.push(item);
}
});
});
freedElements.map(item => {
const markers = Object.keys(item.markers).map(name => item.markers[name]);
freedMarkers.push(...markers);
});
this.handleFreedLabel(freedElements, prevLayoutGroupTable.get(freedGroupName));
this.prevFreedElements = freedElements;
const freedItems = [...freedElements, ...freedMarkers];
freedItems.forEach(item => {
item.set('style', {
fill: '#ccc'
});
});
return freedItems;
}
/**
* 获取被泄露的节点
* @param prevModelList
* @param modelList
* @returns
*/
private getLeakModelList(prevModelList: Model[], modelList: Model[]): Model[] {
const leakModelList: Model[] = prevModelList.filter(item => !modelList.find(n => n.id === item.id)),
elements: Element[] = <Element[]>leakModelList.filter(item => item instanceof Element && item.freed === false),
links: Link[] = <Link[]>leakModelList.filter(item => item instanceof Link),
elementIds: string[] = [],
res: Model[] = [];
elements.forEach(item => {
elementIds.push(item.id);
item.set('style', {
fill: '#ccc'
});
});
for (let i = 0; i < links.length; i++) {
let sourceId = links[i].element.id,
targetId = links[i].target.id;
links[i].set('style', {
stroke: '#333'
});
if (elementIds.find(item => item === sourceId) === undefined || elementIds.find(item => item === targetId) === undefined) {
links.splice(i, 1);
i--;
}
}
res.push(...elements, ...links);
res.map(item => {
item.isLeak = true;
});
return res;
}
// ----------------------------------------------------------------------------------------------
/**
* 对主视图进行重新布局
* @param layoutGroupTable
*/
reLayout(layoutGroupTable: LayoutGroupTable) {
this.layouter.layoutAll(this.mainContainer, layoutGroupTable);
}
/**
* 获取 g6 实例
*/
getG6Instance() {
return this.mainContainer.getG6Instance();
}
/**
* 刷新视图
*/
refresh() {
this.mainContainer.getG6Instance().refresh();
}
/**
* 重新调整容器尺寸
* @param containerName
* @param width
* @param height
*/
resize(containerName: string, width: number, height: number) {
if (containerName === 'main') {
this.mainContainer.getG6Instance().changeSize(width, height);
}
if (containerName === 'freed') {
this.freedContainer.getG6Instance().changeSize(width, height);
}
if (containerName === 'leak') {
this.leakContainer.getG6Instance().changeSize(width, height);
}
}
/**
* 渲染所有视图
* @param models
* @param layoutFn
*/
renderAll(layoutGroupTable: LayoutGroupTable) {
this.shadowG6Instance.clear();
let modelList = Util.convertGroupTable2ModelList(layoutGroupTable),
leakModelList = [],
freedModelList = [];
this.build(modelList);
if (this.leakContainer) {
leakModelList = this.getLeakModelList(this.prevModelList, modelList);
this.build(leakModelList);
}
// 进行布局设置model的xy
this.layouter.layoutAll(this.mainContainer, layoutGroupTable);
freedModelList = this.getFreedModelList(this.prevLayoutGroupTable, layoutGroupTable);
if (this.freedContainer && freedModelList.length) {
this.freedContainer.fitCenter(freedModelList);
this.freedContainer.render(freedModelList);
EventBus.emit('onFreed', freedModelList);
}
// 从新获取一次因为第一次获取没有把freed节点筛选出去
modelList = Util.convertGroupTable2ModelList(layoutGroupTable);
this.mainContainer.render(modelList);
if (this.leakContainer && leakModelList.length) {
this.leakContainer.render(leakModelList);
EventBus.emit('onLeak', leakModelList);
}
this.prevLayoutGroupTable = layoutGroupTable;
this.prevModelList = modelList;
}
/**
* 销毁
*/
destroy() {
this.shadowG6Instance.destroy();
this.mainContainer.destroy();
this.freedContainer && this.freedContainer.destroy();
this.leakContainer && this.leakContainer.destroy();
}
}