From 40976cf3eee14ab9dde0fcf8765b97ce0a00e7a3 Mon Sep 17 00:00:00 2001 From: Yanyan-Wang Date: Mon, 30 Oct 2023 13:50:45 +0800 Subject: [PATCH] perf: incremental force --- packages/g6/src/item/node.ts | 2 +- packages/g6/src/runtime/controller/item.ts | 1 - packages/g6/src/runtime/controller/layout.ts | 352 ++++++++++++------ packages/g6/src/runtime/graph.ts | 39 +- .../src/stdlib/plugin/lodController/index.ts | 16 +- packages/g6/src/types/graph.ts | 73 ++-- packages/g6/src/types/layout.ts | 37 +- packages/g6/tests/demo/item/label.ts | 118 ++++-- 8 files changed, 404 insertions(+), 234 deletions(-) diff --git a/packages/g6/src/item/node.ts b/packages/g6/src/item/node.ts index ceafeda3d0..4e6602882f 100644 --- a/packages/g6/src/item/node.ts +++ b/packages/g6/src/item/node.ts @@ -186,7 +186,7 @@ export default class Node extends Item { { group: position } as any, // targetStylesMap this.shapeMap, // shapeMap undefined, - [group, labelGroup], + [group], 'update', [], this.animateFrameListener, diff --git a/packages/g6/src/runtime/controller/item.ts b/packages/g6/src/runtime/controller/item.ts index 40a8aaafed..5222743c40 100644 --- a/packages/g6/src/runtime/controller/item.ts +++ b/packages/g6/src/runtime/controller/item.ts @@ -1847,7 +1847,6 @@ export class ItemController { (model, canceled) => { this.graph.hideItem(model.id, { disableAnimate: canceled }); }, - undefined, ); } diff --git a/packages/g6/src/runtime/controller/layout.ts b/packages/g6/src/runtime/controller/layout.ts index 911e4b6155..2d88ac6db2 100644 --- a/packages/g6/src/runtime/controller/layout.ts +++ b/packages/g6/src/runtime/controller/layout.ts @@ -108,17 +108,30 @@ export class LayoutController { animationEffectTiming = { duration: 1000, } as Partial, + preset, execute, ...rest } = options; + // preset layout + const nodesWithPosition = await this.presetLayout( + layoutData, + nodeSize, + width, + height, + center, + preset, + params, + layoutGraphCore, + ); + // It will ignore some layout options such as `type` and `workerEnabled`. positions = await execute(layoutGraphCore, { nodeSize, width, height, center, - getMass: this.genericGetMass(options), + getMass: this.genericGetMass(options, nodesWithPosition), preset: layoutNodes.map((node) => { const { x, y, z } = node.data; if (isNaN(x) || isNaN(y)) return; @@ -147,131 +160,237 @@ export class LayoutController { ); } } else { - const { - type = 'grid', - animated = false, - animationEffectTiming = { - duration: 1000, - } as Partial, - iterations = 300, - ...rest - } = options; - let { workerEnabled = false } = options; + const { preset } = options; - // Find built-in layout algorithms. - const layoutCtor = stdLib.layouts[type] || registery.useLib.layouts[type]; - if (!layoutCtor) { - throw new Error(`Unknown layout algorithm: ${type}`); - } - - if (isTreeLayout(options)) { - // tree layout type - await this.handleTreeLayout( - type, - { - nodeSize, - width, - height, - center, - ...rest, - }, - animationEffectTiming, - graphCore, - layoutData, - animate, - ); - return; - } - - // Initialize layout. - const useCache = layoutCtor === Extensions.DagreLayout; - const layout = new layoutCtor({ + // preset layout + const nodesWithPosition = await this.presetLayout( + layoutData, nodeSize, width, height, center, - getMass: this.genericGetMass(options), - preset: useCache - ? layoutNodes - .map((node) => { - const { x, y, z } = node.data; - if (isNaN(x) || isNaN(y)) return; - const presetNode = { x, y, z }; - const otherProps = this.previousNodes?.get(node.id) || {}; - ['_order', 'layer'].forEach((field) => { - presetNode[field] = node.data.hasOwnProperty(field) - ? node.data[field] - : otherProps[field]; - }); - return { - id: node.id, - data: { - ...otherProps, - ...presetNode, - }, - }; - }) - .filter(Boolean) - : undefined, - ...rest, - }); - this.currentLayout = layout; + preset, + params, + layoutGraphCore, + ); - // CustomLayout is not workerized. - if (!isLayoutWorkerized(options)) { - workerEnabled = false; - // TODO: console.warn(); - } - - if (workerEnabled) { - /** - * Run algorithm in WebWorker, `animated` option will be ignored. - */ - const supervisor = new Supervisor(layoutGraphCore, layout, { - iterations, - }); - this.currentSupervisor = supervisor; - positions = await supervisor.execute(); - } else { - // e.g. Force layout - if (isLayoutWithIterations(layout)) { - if (animated) { - /** - * `onTick` will get triggered in this case. - */ - positions = await layout.execute(layoutGraphCore, { - onTick: (positionsOnTick: LayoutMapping) => { - // Display the animated process of layout. - this.updateNodesPosition(positionsOnTick); - this.graph.emit('tick', positionsOnTick); - }, - }); - } else { - /** - * Manually step simulation in a sync way. `onTick` won't get triggered in this case, - * there will be no animation either. - */ - layout.execute(layoutGraphCore); - layout.stop(); - positions = layout.tick(iterations); - } - } else { - positions = await layout.execute(layoutGraphCore); - - if (animated) { - await this.animateLayoutWithoutIterations( - positions, - animationEffectTiming, - ); - } - } - } + // layout + // TODO: input positions affect the layout + positions = await this.layoutOnce( + layoutData, + nodeSize, + width, + height, + center, + options, + params, + layoutGraphCore, + nodesWithPosition, + ); } // Update nodes' positions. this.updateNodesPosition(positions, animate); } + private layoutOnce = async ( + layoutData, + nodeSize, + width, + height, + center, + options, + params, + layoutGraphCore, + nodesWithPosition?, + ) => { + let positions: LayoutMapping; + const { graphCore, animate = true } = params; + const { nodes: layoutNodes } = layoutData; + const { + type = 'grid', + animated = false, + animationEffectTiming = { + duration: 1000, + } as Partial, + iterations = 300, + ...rest + } = options; + let { workerEnabled = false } = options; + + // Find built-in layout algorithms. + const layoutCtor = stdLib.layouts[type] || registery.useLib.layouts[type]; + if (!layoutCtor) { + throw new Error(`Unknown layout algorithm: ${type}`); + } + + if (isTreeLayout(options)) { + // tree layout type + await this.handleTreeLayout( + type, + { + nodeSize, + width, + height, + center, + ...rest, + }, + animationEffectTiming, + graphCore, + layoutData, + animate, + ); + return; + } + + // Initialize layout. + const useCache = layoutCtor === Extensions.DagreLayout; + const layout = new layoutCtor({ + nodeSize, + width, + height, + center, + getMass: this.genericGetMass(options, nodesWithPosition), + preset: useCache + ? layoutNodes + .map((node) => { + const { x, y, z } = node.data; + if (isNaN(x) || isNaN(y)) return; + const presetNode = { x, y, z }; + const otherProps = this.previousNodes?.get(node.id) || {}; + ['_order', 'layer'].forEach((field) => { + presetNode[field] = node.data.hasOwnProperty(field) + ? node.data[field] + : otherProps[field]; + }); + return { + id: node.id, + data: { + ...otherProps, + ...presetNode, + }, + }; + }) + .filter(Boolean) + : undefined, + ...rest, + }); + this.currentLayout = layout; + + // CustomLayout is not workerized. + if (!isLayoutWorkerized(options)) { + workerEnabled = false; + // TODO: console.warn(); + } + + if (workerEnabled) { + /** + * Run algorithm in WebWorker, `animated` option will be ignored. + */ + const supervisor = new Supervisor(layoutGraphCore, layout, { + iterations, + }); + this.currentSupervisor = supervisor; + positions = await supervisor.execute(); + } else { + // e.g. Force layout + if (isLayoutWithIterations(layout)) { + if (animated) { + /** + * `onTick` will get triggered in this case. + */ + positions = await layout.execute(layoutGraphCore, { + onTick: (positionsOnTick: LayoutMapping) => { + // Display the animated process of layout. + this.updateNodesPosition(positionsOnTick); + this.graph.emit('tick', positionsOnTick); + }, + }); + } else { + /** + * Manually step simulation in a sync way. `onTick` won't get triggered in this case, + * there will be no animation either. + */ + layout.execute(layoutGraphCore); + layout.stop(); + positions = layout.tick(iterations); + } + } else { + positions = await layout.execute(layoutGraphCore); + + if (animated) { + await this.animateLayoutWithoutIterations( + positions, + animationEffectTiming, + ); + } + } + } + return positions; + }; + + private presetLayout = async ( + layoutData, + nodeSize, + width, + height, + center, + preset, + params, + layoutGraphCore, + ) => { + // preset has higher priority than the positions in data + if (preset?.type) { + const presetPositions = await this.layoutOnce( + layoutData, + nodeSize, + width, + height, + center, + preset, + params, + layoutGraphCore, + ); + presetPositions.nodes.forEach((node) => { + layoutGraphCore.updateNodeData(node.id, { + x: node.data.x, + y: node.data.y, + }); + }); + return; + } + + const nodeWithPostions = new Map(); + // find the neighbors' mean center as the initial position for the nodes + layoutData.nodes.forEach((node) => { + const { x, y } = node.data; + if (isNaN(x) || isNaN(y)) { + const meanCenter = { x: 0, y: 0, z: 0, count: 0 }; + layoutGraphCore.getNeighbors(node.id).forEach((neighbor) => { + const { x: nx, y: ny, z: nz } = neighbor.data; + if (!isNaN(nx) && !isNaN(ny)) { + meanCenter.x += nx; + meanCenter.y += ny; + meanCenter.z += nz; + meanCenter.count++; + } + }); + if (meanCenter.count) { + node.data.x = meanCenter.x / meanCenter.count + Math.random(); + node.data.y = meanCenter.y / meanCenter.count + Math.random(); + node.data.z = meanCenter.z / meanCenter.count + Math.random(); + } else { + node.data.x = center[0] + Math.random(); + node.data.y = center[1] + Math.random(); + node.data.z = Math.random(); + } + } else { + nodeWithPostions.set(node.id, 1); + } + }); + return nodeWithPostions; + }; + async handleTreeLayout( type, options, @@ -450,14 +569,15 @@ export class LayoutController { await this.currentAnimation.finished; } - private genericGetMass = (options) => { + private genericGetMass = (options, nodesWithPosition) => { const { getMass: propGetMass } = options; return (node) => { const propGetMassVal = propGetMass?.(node); if (!isNaN(propGetMassVal)) return propGetMassVal; const { x, y, mass } = node.data; if (!isNaN(mass)) return mass; - if (!isNaN(x) && !isNaN(y)) return 100; + if (nodesWithPosition?.get(node.id)) return 10; + if (!nodesWithPosition && !isNaN(x) && !isNaN(y)) return 10; return 1; }; }; diff --git a/packages/g6/src/runtime/graph.ts b/packages/g6/src/runtime/graph.ts index 9bd803b298..9c79eb1ac9 100644 --- a/packages/g6/src/runtime/graph.ts +++ b/packages/g6/src/runtime/graph.ts @@ -1258,7 +1258,8 @@ export class Graph } /** * Remove one or more node/edge/combo data from the graph. - * @param item the item to be removed + * @param itemType the type the item(s) to be removed. + * @param id the id or the ids' array of the items to be removed. * @returns whether success * @group Data */ @@ -1360,8 +1361,8 @@ export class Graph } /** * Update one or more node/edge/combo data on the graph. - * @param {ITEM_TYPE} itemType 'node' | 'edge' | 'combo' - * @param models new configurations for every node/edge/combo, which has id field to indicate the specific item + * @param {ITEM_TYPE} itemType the type the item(s) to be udated. + * @param models update configs. * @group Data */ public updateData( @@ -1421,7 +1422,10 @@ export class Graph /** * Update one or more nodes' positions, * do not update other styles which leads to better performance than updating positions by updateData. - * @param models new configurations with x and y for every node, which has id field to indicate the specific item + * @param models new configurations with x and y for every node, which has id field to indicate the specific item. + * @param upsertAncestors whether update the ancestors in combo tree. + * @param disableAnimate whether disable the animation for this call. + * @param callback callback function after update nodes done. * @group Data */ public updateNodePosition( @@ -1436,7 +1440,6 @@ export class Graph model: NodeModel | EdgeModel | ComboModel, canceled?: boolean, ) => void, - stack?: boolean, ) { return this.updatePosition( 'node', @@ -1444,7 +1447,6 @@ export class Graph upsertAncestors, disableAnimate, callback, - stack, ); } @@ -1452,7 +1454,10 @@ export class Graph * Update one or more combos' positions, * do not update other styles which leads to better performance than updating positions by updateData. * In fact, it changes the succeed nodes positions to affect the combo's position, but not modify the combo's position directly. - * @param models new configurations with x and y for every combo, which has id field to indicate the specific item + * @param models new configurations with x and y for every combo, which has id field to indicate the specific item. + * @param upsertAncestors whether update the ancestors in combo tree. + * @param disableAnimate whether disable the animation for this call. + * @param callback callback function after update combos done. * @group Data */ public updateComboPosition( @@ -1464,7 +1469,6 @@ export class Graph upsertAncestors?: boolean, disableAnimate = false, callback?: (model: NodeModel | EdgeModel | ComboModel) => void, - stack?: boolean, ) { return this.updatePosition( 'combo', @@ -1472,7 +1476,6 @@ export class Graph upsertAncestors, disableAnimate, callback, - stack, ); } @@ -1503,7 +1506,6 @@ export class Graph model: NodeModel | EdgeModel | ComboModel, canceled?: boolean, ) => void, - stack?: boolean, ) { const modelArr = isArray(models) ? models : [models]; const { graphCore } = this.dataController; @@ -1865,7 +1867,8 @@ export class Graph /** * Add a new combo to the graph, and update the structure of the existed child in childrenIds to be the children of the new combo. * Different from addData with combo type, this API update the succeeds' combo tree strucutres in the same time. - * @param model combo user data + * @param model combo user data. + * @param childrenIds the ids of the children nodes / combos to move into the new combo. * @returns whether success * @group Combo */ @@ -1913,7 +1916,7 @@ export class Graph } /** * Collapse a combo. - * @param comboId combo ids + * @param comboId combo id or ids' array. * @group Combo */ public collapseCombo(comboIds: ID | ID[]) { @@ -1933,7 +1936,7 @@ export class Graph } /** * Expand a combo. - * @param combo combo ids + * @param comboId combo id or ids' array. * @group Combo */ public expandCombo(comboIds: ID | ID[]) { @@ -1956,7 +1959,11 @@ export class Graph * Move one or more combos a distance (dx, dy) relatively, * do not update other styles which leads to better performance than updating positions by updateData. * In fact, it changes the succeed nodes positions to affect the combo's position, but not modify the combo's position directly. - * @param models new configurations with x and y for every combo, which has id field to indicate the specific item + * @param models new configurations with x and y for every combo, which has id field to indicate the specific item. + * @param dx the distance alone x-axis to move the combo. + * @param dy the distance alone y-axis to move the combo. + * @param upsertAncestors whether update the ancestors in the combo tree. + * @param callback callback function after move combo done. * @group Combo */ public moveCombo( @@ -2909,7 +2916,7 @@ export class Graph * @returns * @group Tree */ - public collapse(ids: ID | ID[], disableAnimate = false, stack?: boolean) { + public collapse(ids: ID | ID[], disableAnimate = false) { this.hooks.treecollapseexpand.emit({ ids: isArray(ids) ? ids : [ids], action: 'collapse', @@ -2925,7 +2932,7 @@ export class Graph * @returns * @group Tree */ - public expand(ids: ID | ID[], disableAnimate = false, stack?: boolean) { + public expand(ids: ID | ID[], disableAnimate = false) { this.hooks.treecollapseexpand.emit({ ids: isArray(ids) ? ids : [ids], action: 'expand', diff --git a/packages/g6/src/stdlib/plugin/lodController/index.ts b/packages/g6/src/stdlib/plugin/lodController/index.ts index d89d55a194..d71fb77049 100644 --- a/packages/g6/src/stdlib/plugin/lodController/index.ts +++ b/packages/g6/src/stdlib/plugin/lodController/index.ts @@ -425,12 +425,12 @@ export class LodController extends Base { } return false; }; - const inBoundary = (rowIdx, colIdx) => { + const outBoundary = (rowIdx, colIdx) => { return ( - rowIdx < canvasCellRowRange[0] || - rowIdx > canvasCellRowRange[1] || - colIdx < canvasCellColRange[0] || - colIdx > canvasCellColRange[1] + rowIdx <= canvasCellRowRange[0] || + rowIdx >= canvasCellRowRange[1] || + colIdx <= canvasCellColRange[0] || + colIdx >= canvasCellColRange[1] ); }; const newlyOutView = []; @@ -456,7 +456,7 @@ export class LodController extends Base { outView.push(model); if (!previousOutView.has(model.id)) newlyOutView.push(model); } - } else if (inBoundary(rowIdx, colIdx)) { + } else if (outBoundary(rowIdx, colIdx)) { outView.push(model); if (!previousOutView.has(model.id)) newlyOutView.push(model); } else { @@ -478,7 +478,7 @@ export class LodController extends Base { } else { outViewNew.push(model); } - } else if (inBoundary(rowIdx, colIdx)) { + } else if (outBoundary(rowIdx, colIdx)) { outViewNew.push(model); } else { inView.push(model); @@ -500,7 +500,7 @@ export class LodController extends Base { } else { inViewNew.push(model); } - } else if (inBoundary(rowIdx, colIdx)) { + } else if (outBoundary(rowIdx, colIdx)) { outView.push(model); newlyOutView.push(model); } else { diff --git a/packages/g6/src/types/graph.ts b/packages/g6/src/types/graph.ts index dc84e7e205..c7f0a5d5ce 100644 --- a/packages/g6/src/types/graph.ts +++ b/packages/g6/src/types/graph.ts @@ -194,7 +194,6 @@ export interface IGraph< * Add one or more node/edge/combo data to the graph. * @param itemType item type * @param model user data - * @param stack whether push this operation to stack * @returns whehter success * @group Data */ @@ -207,7 +206,6 @@ export interface IGraph< | NodeUserModel[] | EdgeUserModel[] | ComboUserModel[], - stack?: boolean, ) => | NodeModel | EdgeModel @@ -217,17 +215,16 @@ export interface IGraph< | ComboModel[]; /** * Remove one or more node/edge/combo data from the graph. - * @param item the item to be removed - * @param stack whether push this operation to stack + * @param itemType the type the item(s) to be removed. + * @param id the id or the ids' array of the items to be removed. * @returns whehter success * @group Data */ - removeData: (itemType: ITEM_TYPE, id: ID | ID[], stack?: boolean) => void; + removeData: (itemType: ITEM_TYPE, id: ID | ID[]) => void; /** * Update one or more node/edge/combo data on the graph. - * @param item the item to be updated - * @param model update configs - * @param {boolean} stack whether push this operation to stack + * @param itemType the type the item(s) to be udated. + * @param models update configs. * @group Data */ updateData: ( @@ -241,7 +238,6 @@ export interface IGraph< | Partial[] | Partial[] >, - stack?: boolean, ) => | NodeModel | EdgeModel @@ -253,8 +249,10 @@ export interface IGraph< /** * Update one or more nodes' positions, * do not update other styles which leads to better performance than updating positions by updateData. - * @param models new configurations with x and y for every node, which has id field to indicate the specific item - * @param {boolean} stack whether push this operation into graph's stack, true by default + * @param models new configurations with x and y for every node, which has id field to indicate the specific item. + * @param upsertAncestors whether update the ancestors in combo tree. + * @param disableAnimate whether disable the animation for this call. + * @param callback callback function after update nodes done. * @group Data */ updateNodePosition: ( @@ -269,14 +267,15 @@ export interface IGraph< model: NodeModel | EdgeModel | ComboModel, canceled?: boolean, ) => void, - stack?: boolean, ) => NodeModel | ComboModel | NodeModel[] | ComboModel[]; /** * Update one or more combos' positions, it is achieved by move the succeed nodes. * Do not update other styles which leads to better performance than updating positions by updateData. - * @param models new configurations with x and y for every combo, which has id field to indicate the specific item - * @param {boolean} stack whether push this operation into graph's stack, true by default + * @param models new configurations with x and y for every combo, which has id field to indicate the specific item. + * @param upsertAncestors whether update the ancestors in combo tree. + * @param disableAnimate whether disable the animation for this call. + * @param callback callback function after update combos done. * @group Data */ updateComboPosition: ( @@ -288,15 +287,17 @@ export interface IGraph< upsertAncestors?: boolean, disableAnimate?: boolean, callback?: (model: NodeModel | EdgeModel | ComboModel) => void, - stack?: boolean, ) => NodeModel | ComboModel | NodeModel[] | ComboModel[]; /** * Move one or more combos a distance (dx, dy) relatively, * do not update other styles which leads to better performance than updating positions by updateData. * In fact, it changes the succeed nodes positions to affect the combo's position, but not modify the combo's position directly. - * @param models new configurations with x and y for every combo, which has id field to indicate the specific item - * @param {boolean} stack whether push this operation into graph's stack, true by default + * @param models new configurations with x and y for every combo, which has id field to indicate the specific item. + * @param dx the distance alone x-axis to move the combo. + * @param dy the distance alone y-axis to move the combo. + * @param upsertAncestors whether update the ancestors in the combo tree. + * @param callback callback function after move combo done. * @group Data */ moveCombo: ( @@ -308,7 +309,6 @@ export interface IGraph< model: NodeModel | EdgeModel | ComboModel, canceled?: boolean, ) => void, - stack?: boolean, ) => ComboModel[]; // ===== view operations ===== @@ -515,14 +515,14 @@ export interface IGraph< * @returns * @group Item */ - frontItem: (ids: ID | ID[], stack?: boolean) => void; + frontItem: (ids: ID | ID[]) => void; /** * Make the item(s) to the back. * @param ids the item id(s) to back * @returns * @group Item */ - backItem: (ids: ID | ID[], stack?: boolean) => void; + backItem: (ids: ID | ID[]) => void; /** * Set state for the item(s). * @param ids the id(s) for the item(s) to be set @@ -531,12 +531,7 @@ export interface IGraph< * @returns * @group Item */ - setItemState: ( - ids: ID | ID[], - state: string, - value: boolean, - stack?: boolean, - ) => void; + setItemState: (ids: ID | ID[], state: string, value: boolean) => void; /** * Get the state value for an item. * @param id the id for the item @@ -559,7 +554,7 @@ export interface IGraph< * @returns * @group Item */ - clearItemState: (ids: ID | ID[], states?: string[], stack?: boolean) => void; + clearItemState: (ids: ID | ID[], states?: string[]) => void; /** * Get the rendering bbox for a node / edge / combo, or the graph (when the id is not assigned). @@ -591,29 +586,25 @@ export interface IGraph< /** * Add a new combo to the graph, and update the structure of the existed child in childrenIds to be the children of the new combo. * Different from addData with combo type, this API update the succeeds' combo tree strucutres in the same time. - * @param model combo user data - * @param stack whether push this operation to stack + * @param model combo user data. + * @param childrenIds the ids of the children nodes / combos to move into the new combo. * @returns whether success * @group Combo */ - addCombo: ( - model: ComboUserModel, - childrenIds: ID[], - stack?: boolean, - ) => ComboModel; + addCombo: (model: ComboUserModel, childrenIds: ID[]) => ComboModel; /** * Collapse a combo. - * @param comboId combo id or item + * @param comboId combo id or ids' array. * @group Combo */ - collapseCombo: (comboIds: ID | ID[], stack?: boolean) => void; + collapseCombo: (comboIds: ID | ID[]) => void; /** * Expand a combo. * @group Combo - * @param combo combo ID 或 combo 实例 + * @param comboId combo id or ids' array. * @group Combo */ - expandCombo: (comboIds: ID | ID[], stack?: boolean) => void; + expandCombo: (comboIds: ID | ID[]) => void; // ===== layout ===== /** @@ -829,18 +820,16 @@ export interface IGraph< * Collapse sub tree(s). * @param ids Root id(s) of the sub trees. * @param disableAnimate Whether disable the animations for this operation. - * @param stack Whether push this operation to stack. * @returns * @group Tree */ - collapse: (ids: ID | ID[], disableAnimate?: boolean, stack?: boolean) => void; + collapse: (ids: ID | ID[], disableAnimate?: boolean) => void; /** * Expand sub tree(s). * @param ids Root id(s) of the sub trees. * @param disableAnimate Whether disable the animations for this operation. - * @param stack Whether push this operation to stack. * @returns * @group Tree */ - expand: (ids: ID | ID[], disableAnimate?: boolean, stack?: boolean) => void; + expand: (ids: ID | ID[], disableAnimate?: boolean) => void; } diff --git a/packages/g6/src/types/layout.ts b/packages/g6/src/types/layout.ts index 11cb1ad404..f97150d91b 100644 --- a/packages/g6/src/types/layout.ts +++ b/packages/g6/src/types/layout.ts @@ -40,19 +40,7 @@ type Workerized = { iterations?: number; }; -export type ImmediatelyInvokedLayoutOptions = { - /** - * like an IIFE. - */ - execute: (graph: GraphCore, options?: any) => Promise; -} & Animatable; - -type CustomLayout = { - type: string; - [option: string]: any; -}; - -export type StandardLayoutOptions = ( +type PureLayoutOptions = | CircularLayout | RandomLayout | ConcentricLayout @@ -63,10 +51,27 @@ export type StandardLayoutOptions = ( | D3ForceLayout | ForceLayout | ForceAtlas2 - | CustomLayout -) & + | CustomLayout; + +export type ImmediatelyInvokedLayoutOptions = { + /** + * like an IIFE. + */ + execute: (graph: GraphCore, options?: any) => Promise; +} & Animatable & { + preset?: PureLayoutOptions; + }; + +type CustomLayout = { + type: string; + [option: string]: any; +}; + +export type StandardLayoutOptions = PureLayoutOptions & Animatable & - Workerized; + Workerized & { + preset?: PureLayoutOptions; + }; export type LayoutOptions = | StandardLayoutOptions diff --git a/packages/g6/tests/demo/item/label.ts b/packages/g6/tests/demo/item/label.ts index 5fc035959c..067e1ddab9 100644 --- a/packages/g6/tests/demo/item/label.ts +++ b/packages/g6/tests/demo/item/label.ts @@ -1711,6 +1711,10 @@ const data = { ], }; data.edges.forEach((edge, i) => (edge.id = 'edge' + i)); +data.nodes.forEach((node) => { + delete node.data.x; + delete node.data.y; +}); export default ( context: TestCaseContext, @@ -1770,37 +1774,42 @@ export default ( data: { ...model.data, animates: { - buildIn: [ + update: [ { - fields: ['opacity'], - duration: 1000, - delay: 1000 + Math.random() * 1000, - }, - ], - hide: [ - { - fields: ['opacity'], - duration: 1000, - shapeId: 'labelShape', - }, - { - fields: ['opaicty'], - duration: 1000, - shapeId: 'labelBackgroundShape', - }, - ], - show: [ - { - fields: ['opacity'], - duration: 1000, - shapeId: 'labelShape', - }, - { - fields: ['opaicty'], - duration: 1000, - shapeId: 'labelBackgroundShape', + fields: ['x', 'y'], }, ], + // buildIn: [ + // { + // fields: ['opacity'], + // duration: 1000, + // delay: 1000 + Math.random() * 1000, + // }, + // ], + // hide: [ + // { + // fields: ['opacity'], + // duration: 1000, + // shapeId: 'labelShape', + // }, + // { + // fields: ['opaicty'], + // duration: 1000, + // shapeId: 'labelBackgroundShape', + // }, + // ], + // show: [ + // { + // fields: ['opacity'], + // duration: 1000, + // shapeId: 'labelShape', + // }, + // { + // fields: ['opaicty'], + // duration: 1000, + // shapeId: 'labelBackgroundShape', + // }, + // ], }, labelShape: { position: 'bottom', @@ -1822,6 +1831,18 @@ export default ( }, }; }, + layout: { + preset: { + type: 'concentric', + }, + type: 'force', + linkDistance: 100, + edgeStrength: 1000, + nodeStrength: 2000, + // animated: true, + // maxSpeed: 10000, + // minMovement: 0.1, + }, }); graph.on('canvas:click', (e) => { @@ -1834,21 +1855,50 @@ export default ( // y: y + 10, // }, // }); - // graph.updateData('node', { // id: 'node1', // data: { // label: 'changedasdfasfasdfasdf', // }, // }); - // graph.downloadFullImage(); - // graph.fitView(); - // graph.changeData(data); - graph.hideItem('Marius'); - graph.showItem('Marius'); + // graph.hideItem('Marius'); + // graph.showItem('Marius'); + + console.log( + 'click', + graph.itemController.itemMap.get('Gervais')?.labelGroup, + ); + }); + let allData = { ...data }; + graph.on('node:click', (e) => { + const newNodes = []; + for (let i = 0; i < 5; i++) { + newNodes.push({ + id: `node-${e.itemId}-${i}`, + data: {}, + }); + } + const newEdges = []; + for (let i = 0; i < 5; i++) { + newEdges.push({ + id: `newedge-${e.itemId}-${i}`, + source: e.itemId, + target: `node-${e.itemId}-${i}`, + data: {}, + }); + } + console.log('chagneDat'); + allData = { + nodes: allData.nodes.concat(newNodes), + edges: allData.edges.concat(newEdges), + }; + graph.changeData(allData, 'mergeReplace', false); + graph.layout({ + preset: {}, + }); }); return graph; };