Merge branch 'main' of https://gitlab.com/phenomLi/StructV2 into main

This commit is contained in:
黎智洲 2021-12-22 21:57:34 +08:00
commit 2cdc35ff30
17 changed files with 14316 additions and 123 deletions

View File

@ -14,6 +14,10 @@ SV.registerLayout('Array', {
return sources;
},
defineLeakRule(nodes) {
return [];
},
defineOptions() {
return {
node: {
@ -49,6 +53,9 @@ SV.registerLayout('Array', {
indexLabel: {
index: { position: 'bottom' },
indexRight: { position: 'right' }
},
behavior: {
dragNode: false
}
};
},

View File

@ -42,10 +42,10 @@ SV.registerLayout('LinkList', {
}
},
loopNext: {
type: 'arc',
curveOffset: 50,
type: 'quadratic',
curveOffset: -100,
sourceAnchor: 2,
targetAnchor: 4,
targetAnchor: 7,
style: {
stroke: '#333',
endArrow: 'default',

View File

@ -13,11 +13,15 @@ SV.registerLayout('Stack', {
return sources;
},
defineLeakRule(nodes) {
return [];
},
defineOptions() {
return {
element: {
default: {
type: 'indexed-node',
type: 'array-node',
label: '[id]',
size: [60, 30],
style: {
@ -49,6 +53,9 @@ SV.registerLayout('Stack', {
}
}
},
indexLabel: {
index: { position: 'left' }
},
behavior: {
dragNode: false
}

View File

@ -99,84 +99,123 @@
let data = [{
Array: {
data: [
{ id: 1, data: 1, child: [2, 3], external: 'exx' },
{ id: 2, data: 2 },
{ id: 3, data: 3, child: [6, 4] },
{ id: 4, data: 4 },
{ id: 6, data: 6 }
],
layouter: 'BinaryTree'
},
L: {
data: [
{ id: 11, data: 11, next: 22, external: 'tt' },
{ id: 22, data: 22, next: 33 },
{ id: 33, data: 33, next: 44 },
{ id: 44, data: 44, freed: true }
],
layouter: 'LinkList'
},
"ChainHashTable": {
"sqStack0": {
"data": [
{
"type": "head",
"id": "0x618090",
"data": "T"
"id": "0x617eb5",
"data": "",
"index": 5,
"cursor": "top"
},
{
"type": "node",
"id": "0x618030",
"data": "N"
"id": "0x617eb4",
"data": "2",
"index": 4
},
{
"type": "head",
"id": "0x6180f0",
"data": "U",
"start": "node#0x618030"
"id": "0x617eb3",
"data": "6",
"index": 3
},
{
"type": "node",
"id": "0x617ff0",
"data": "V",
"next": "node#0x617fd0"
"id": "0x617eb2",
"data": "7",
"index": 2
},
{
"type": "node",
"id": "0x617fd0",
"data": "A"
"id": "0x617eb1",
"data": "9",
"index": 1
},
{
"type": "head",
"id": "0x618010",
"data": "O",
"start": "node#0x617ff0"
},
{
"type": "node",
"id": "0x618050",
"data": "K"
},
{
"type": "head",
"id": "0x6180b0",
"data": "R",
"start": "node#0x618050"
"id": "0x617eb0",
"data": "1",
"index": 0,
"external": "S"
}
],
"layouter": "ChainHashTable"
"layouter": "Stack"
}
}, {
Array: {
data: [
{ id: 1, data: 1, child: [2, 3], external: 'exx' },
{ id: 2, data: 2 },
{ id: 3, data: 3, child: [6, 7] },
{ id: 7, data: 7 }
"sqStack0": {
"data": [
{
"id": "0x617eb4",
"data": "2",
"index": 4,
"cursor": "top",
"type": "default"
},
{
"id": "0x617eb3",
"data": "6",
"index": 3,
"type": "default"
},
{
"id": "0x617eb2",
"data": "7",
"index": 2,
"type": "default"
},
{
"id": "0x617eb1",
"data": "9",
"index": 1,
"type": "default"
},
{
"id": "0x617eb0",
"data": "1",
"index": 0,
"bottomExternal": "S",
"type": "default"
}
],
layouter: 'BinaryTree'
"layouter": "Stack"
}
}, {
"data": [
{
"id": "0x617eb5",
"data": "",
"index": 5,
"cursor": "top",
"type": "default"
},
{
"id": "0x617eb4",
"data": "2",
"index": 4,
"type": "default"
},
{
"id": "0x617eb3",
"data": "6",
"index": 3,
"type": "default"
},
{
"id": "0x617eb2",
"data": "7",
"index": 2,
"type": "default"
},
{
"id": "0x617eb1",
"data": "9",
"index": 1,
"type": "default"
},
{
"id": "0x617eb0",
"data": "1",
"index": 0,
"bottomExternal": "S",
"type": "default"
}
],
"layouter": "Stack"
}];

14127
dist/sv.js vendored

File diff suppressed because one or more lines are too long

View File

@ -102,6 +102,10 @@ export function SolveNodeAppendagesDrag(viewContainer: ViewContainer) {
node.appendages.forEach(item => {
item.setSelectedState(false);
item.set({
x: item.G6Item.getModel().x,
y: item.G6Item.getModel().y
});
});
}
@ -110,6 +114,15 @@ export function SolveNodeAppendagesDrag(viewContainer: ViewContainer) {
x: item.G6Item.getModel().x,
y: item.G6Item.getModel().y
});
if(item instanceof SVNode) {
item.appendages.forEach(appendage => {
appendage.set({
x: appendage.G6Item.getModel().x,
y: appendage.G6Item.getModel().y
});
});
}
});
});
}

View File

@ -47,6 +47,28 @@ export const Util = {
return res;
},
/**
*
* @param list
* @param category
* @returns
*/
groupBy<T>(list: T[], category: string): { [key: string]: T[] } {
const result = {} as { [key: string]: T[] };
list.forEach(item => {
let value = item[category];
if(result[value] === undefined) {
result[value] = [];
}
result[value].push(item);
});
return result;
},
/**
*
* @param assertFn

View File

@ -129,7 +129,7 @@ export class SVModel {
}
};
this.G6ModelProps = merge(this.G6ModelProps, newG6ModelProps);
this.G6ModelProps = merge.recursive(this.G6ModelProps, newG6ModelProps);
if (this.G6Item) {
this.g6Instance.updateItem(this.G6Item, this.G6ModelProps);

View File

@ -177,6 +177,6 @@ export class SVMarker extends SVNodeAppendage {
public getLabelSizeRadius(): number {
const { width, height } = this.shadowG6Item.getContainer().getChildren()[2].getBBox();
return width > height ? width : height;
return Math.max(width, height);
}
};

View File

@ -217,10 +217,10 @@ export class ModelConstructor {
value = node[name];
// 若没有指针字段的结点则跳过
if (!value) continue;
if (value === undefined || value === null) continue;
let id = `${group}.${name}#${value}`,
indexLabel = new SVIndexLabel(id, name, group, layout, value, node, indexLabelOptions[name]);
indexLabel = new SVIndexLabel(id, name, group, layout, value.toString(), node, indexLabelOptions[name]);
indexLabelList.push(indexLabel);
}

View File

@ -20,7 +20,7 @@ export default Util.registerShape('cursor', {
x: 0,
y: 0,
text: cfg.label,
fill: '#fafafa',
fill: 'transparent',
radius: 2,
},
name: 'bgRect'

View File

@ -64,7 +64,8 @@ export default Util.registerShape('link-list-node', {
[1, 0.5],
[5 / 6, 1],
[0.5, 1],
[0, 0.5]
[0, 0.5],
[1 / 3, 1]
];
}
}, 'rect');

View File

@ -14,6 +14,7 @@ import { EngineOptions, LayoutCreator } from "./options";
import { SourceNode } from "./sources";
import { Util } from "./Common/util";
import { SVModel } from "./Model/SVModel";
import { SVNode } from "./Model/SVNode";
@ -72,7 +73,7 @@ SV.registerLayout = function(name: string, layoutCreator: LayoutCreator) {
}
if(typeof layoutCreator.defineLeakRule !== 'function') {
layoutCreator.defineLeakRule = function(models: SVModel[]): SVModel[] {
layoutCreator.defineLeakRule = function(models: SVNode[]): SVNode[] {
return models;
}
}

View File

@ -146,7 +146,7 @@ export class LayoutProvider {
},
left: (nodeBound: BoundingRect, labelBound: BoundingRect, offset: number) => {
return {
x: nodeBound.x - labelBound.width - 2 * offset,
x: nodeBound.x - labelBound.width - offset,
y: nodeBound.y + nodeBound.height / 2
};
}
@ -227,8 +227,11 @@ export class LayoutProvider {
containerHeight = this.viewContainer.getG6Instance().getHeight(),
leakAreaHeight = this.engine.viewOptions.leakAreaHeight,
leakAreaY = containerHeight - leakAreaHeight,
xOffset = 50;
xOffset = 60;
let prevBound: BoundingRect;
// 避免在泄漏前拖拽节点导致的位置变化,先把节点位置重置为布局后的标准位置
leakModels.forEach(item => {
item.set({
x: item.layoutX,
@ -236,17 +239,26 @@ export class LayoutProvider {
});
});
group.add(...leakModels);
const currentLeakGroupBound: BoundingRect = group.getBound(),
globalLeakGroupBound: BoundingRect = accumulateLeakModels.length ?
Bound.union(...accumulateLeakModels.map(item => item.getBound())) :
{ x: 0, y: leakAreaY, width: 0, height: 0 };
const globalLeakGroupBound: BoundingRect = accumulateLeakModels.length ?
Bound.union(...accumulateLeakModels.map(item => item.getBound())) :
{ x: 0, y: leakAreaY, width: 0, height: 0 };
const { x: groupX, y: groupY } = currentLeakGroupBound,
dx = globalLeakGroupBound.x + globalLeakGroupBound.width + xOffset - groupX,
dy = globalLeakGroupBound.y - groupY;
const layoutGroups = Util.groupBy(leakModels, 'group');
Object.keys(layoutGroups).forEach(key => {
group.add(...layoutGroups[key]);
group.translate(dx, dy);
const currentBound: BoundingRect = group.getBound(),
prevBoundEnd = prevBound? prevBound.x + prevBound.width: 0,
{ x: groupX, y: groupY } = currentBound,
dx = globalLeakGroupBound.x + globalLeakGroupBound.width + prevBoundEnd + xOffset - groupX,
dy = globalLeakGroupBound.y - groupY;
group.translate(dx, dy);
group.clear();
Bound.translate(currentBound, dx, dy);
prevBound = currentBound;
});
}
/**

View File

@ -1,6 +1,7 @@
import { EventBus } from "../Common/eventBus";
import { Util } from "../Common/util";
import { Engine } from "../engine";
import { LayoutGroupTable } from "../Model/modelConstructor";
import { SVLink } from "../Model/SVLink";
import { SVModel } from "../Model/SVModel";
import { SVNode } from "../Model/SVNode";
@ -66,16 +67,31 @@ export class Reconcile {
/**
*
* @param layoutGroupTable
* @param prevModelList
* @param modelList
* @returns
*/
private getLeakModels(prevModelList: SVModel[], modelList: SVModel[]): SVModel[] {
const potentialLeakModels: SVModel[] = prevModelList.filter(item =>
private getLeakModels(layoutGroupTable: LayoutGroupTable, prevModelList: SVModel[], modelList: SVModel[]): SVModel[] {
let potentialLeakModels: SVModel[] = prevModelList.filter(item =>
!modelList.find(model => model.id === item.id) && !item.freed
);
const leakModels: SVModel[] = [];
// 先把节点拿出来
const potentialLeakNodes = potentialLeakModels.filter(item => item.isNode()) as SVNode[],
groups = Util.groupBy<SVNode>(potentialLeakNodes, 'group');
// 再把非节点的model拿出来
potentialLeakModels = potentialLeakModels.filter(item => item.isNode() === false);
Object.keys(groups).forEach(key => {
const leakRule = layoutGroupTable.get(key).layoutCreator.defineLeakRule;
if(leakRule && typeof leakRule === 'function') {
potentialLeakModels.push(...leakRule(groups[key]));
}
});
potentialLeakModels.forEach(item => {
if (item instanceof SVNode) {
item.leaked = true;
@ -345,14 +361,15 @@ export class Reconcile {
/**
* diff
* @param prevLayoutGroupTable
* @param layoutGroupTable
* @param accumulateLeakModels
* @param prevModelList
* @param modelList
* @param accumulateLeakModels
* @returns
*/
public diff(prevModelList: SVModel[], modelList: SVModel[], accumulateLeakModels: SVModel[]): DiffResult {
public diff(layoutGroupTable: LayoutGroupTable, prevModelList: SVModel[], modelList: SVModel[], accumulateLeakModels: SVModel[]): DiffResult {
const continuousModels: SVModel[] = this.getContinuousModels(prevModelList, modelList);
const leakModels: SVModel[] = this.getLeakModels(prevModelList, modelList);
const leakModels: SVModel[] = this.getLeakModels(layoutGroupTable, prevModelList, modelList);
const appendModels: SVModel[] = this.getAppendModels(prevModelList, modelList, accumulateLeakModels);
const removeModels: SVModel[] = this.getRemoveModels(prevModelList, modelList);
const updateModels: SVModel[] = [

View File

@ -20,7 +20,7 @@ export class ViewContainer {
private reconcile: Reconcile;
public renderer: Renderer;
private prevLayoutGroupTable: LayoutGroupTable;
private layoutGroupTable: LayoutGroupTable;
private prevModelList: SVModel[];
private accumulateLeakModels: SVModel[];
@ -37,7 +37,7 @@ export class ViewContainer {
this.layoutProvider = new LayoutProvider(engine, this);
this.renderer = new Renderer(engine, DOMContainer, behaviorsModes);
this.reconcile = new Reconcile(engine, this.renderer);
this.prevLayoutGroupTable = new Map();
this.layoutGroupTable = new Map();
this.prevModelList = [];
this.accumulateLeakModels = [];
this.hasLeak = false; // 判断是否已经发生过泄漏
@ -75,7 +75,7 @@ export class ViewContainer {
g6Instance.translate(-dx, -dy);
}
this.layoutProvider.layoutAll(this.prevLayoutGroupTable, this.accumulateLeakModels, []);
this.layoutProvider.layoutAll(this.layoutGroupTable, this.accumulateLeakModels, []);
g6Instance.refresh();
}
@ -99,7 +99,7 @@ export class ViewContainer {
*
*/
getLayoutGroupTable(): LayoutGroupTable {
return this.prevLayoutGroupTable;
return this.layoutGroupTable;
}
/**
@ -142,7 +142,7 @@ export class ViewContainer {
*/
render(layoutGroupTable: LayoutGroupTable) {
const modelList = Util.convertGroupTable2ModelList(layoutGroupTable),
diffResult = this.reconcile.diff(this.prevModelList, modelList, this.accumulateLeakModels),
diffResult = this.reconcile.diff(this.layoutGroupTable, this.prevModelList, modelList, this.accumulateLeakModels),
renderModelList = [
...modelList,
...diffResult.REMOVE,
@ -176,7 +176,7 @@ export class ViewContainer {
this.accumulateLeakModels.push(...diffResult.LEAKED); // 对泄漏节点进行累积
this.prevLayoutGroupTable = layoutGroupTable;
this.layoutGroupTable = layoutGroupTable;
this.prevModelList = modelList;
}
@ -187,9 +187,10 @@ export class ViewContainer {
this.renderer.destroy();
this.reconcile.destroy();
this.layoutProvider = null;
this.prevLayoutGroupTable = null;
this.layoutGroupTable = null;
this.prevModelList.length = 0;
this.accumulateLeakModels.length = 0;
this.brushSelectedModels.length = 0;
}

View File

@ -129,7 +129,7 @@ export interface EngineOptions {
export interface LayoutCreator {
defineOptions(sourceData: SourceNode[]): LayoutGroupOptions;
sourcesPreprocess?(sourceData: SourceNode[], options: LayoutGroupOptions): SourceNode[];
defineLeakRule?(models: SVModel[]): SVModel[];
defineLeakRule?(models: SVNode[]): SVNode[];
layout(nodes: SVNode[], layoutOptions: LayoutOptions);
[key: string]: any;
}