@antv/g6-pc
Version:
A Graph Visualization Framework in JavaScript
781 lines (780 loc) • 30.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _tslib = require("tslib");
var _g6Core = require("@antv/g6-core");
var _layout = require("../../layout");
var _layout2 = require("../../layout/worker/layout");
var _layoutConst = require("../../layout/worker/layoutConst");
var _gpu = require("../../util/gpu");
var _util = require("@antv/util");
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
// eslint-disable-next-line @typescript-eslint/no-implied-eval
var mockRaf = function mockRaf(cb) {
return setTimeout(cb, 16);
};
var mockCaf = function mockCaf(reqId) {
return clearTimeout(reqId);
};
var helper = {
// pollyfill
requestAnimationFrame: function requestAnimationFrame(callback) {
var fn = typeof window !== 'undefined' ? window.requestAnimationFrame || window.webkitRequestAnimationFrame || mockRaf : mockRaf;
return fn(callback);
},
cancelAnimationFrame: function cancelAnimationFrame(requestId) {
var fn = typeof window !== 'undefined' ? window.cancelAnimationFrame || window.webkitCancelAnimationFrame || mockCaf : mockCaf;
return fn(requestId);
}
};
var GPU_LAYOUT_NAMES = ['fruchterman', 'gForce'];
var LAYOUT_PIPES_ADJUST_NAMES = ['force', 'grid', 'circular'];
var LayoutController = /** @class */function (_super) {
(0, _tslib.__extends)(LayoutController, _super);
// the configurations of the layout
// private layoutCfg: any; // LayoutOptions
// the type name of the layout
// private layoutType: string;
// private data: GraphData;
// private layoutMethods: typeof Layout;
function LayoutController(graph) {
var _this = _super.call(this, graph) || this;
_this.graph = graph;
_this.layoutCfg = graph.get('layout') || {};
_this.layoutType = _this.getLayoutType();
_this.worker = null;
_this.workerData = {};
_this.initLayout();
return _this;
}
// eslint-disable-next-line class-methods-use-this
LayoutController.prototype.initLayout = function () {
// no data before rendering
};
// get layout worker and create one if not exists
LayoutController.prototype.getWorker = function () {
if (this.worker) {
return this.worker;
}
if (typeof Worker === 'undefined') {
// 如果当前浏览器不支持 web worker,则不使用 web worker
console.warn('Web worker is not supported in current browser.');
this.worker = null;
} else {
this.worker = (0, _layout2.LayoutWorker)(this.layoutCfg.workerScriptURL);
}
return this.worker;
};
// stop layout worker
LayoutController.prototype.stopWorker = function () {
var workerData = this.workerData;
if (!this.worker) {
return;
}
this.worker.terminate();
this.worker = null;
// 重新开始新的布局之前,先取消之前布局的requestAnimationFrame。
if (workerData.requestId) {
helper.cancelAnimationFrame(workerData.requestId);
workerData.requestId = null;
}
if (workerData.requestId2) {
helper.cancelAnimationFrame(workerData.requestId2);
workerData.requestId2 = null;
}
};
LayoutController.prototype.execLayoutMethod = function (layoutCfg, order) {
var _this = this;
return new Promise(function (reslove, reject) {
return (0, _tslib.__awaiter)(_this, void 0, void 0, function () {
var graph, layoutType, onTick_1, animate_1, isDefaultAnimateLayout_1, tick, enableTick, layoutMethod, onTick_2, tick, layoutData;
return (0, _tslib.__generator)(this, function (_a) {
switch (_a.label) {
case 0:
graph = this.graph;
if (!graph || graph.get('destroyed')) return [2 /*return*/];
layoutType = layoutCfg.type;
// 每个布局方法都需要注册
layoutCfg.onLayoutEnd = function () {
graph.emit('aftersublayout', {
type: layoutType
});
reslove();
};
// 若用户指定开启 gpu,且当前浏览器支持 webgl,且该算法存在 GPU 版本(目前仅支持 fruchterman 和 gForce),使用 gpu 版本的布局
if (layoutType && this.isGPU) {
if (!hasGPUVersion(layoutType)) {
console.warn("The '".concat(layoutType, "' layout does not support GPU calculation for now, it will run in CPU."));
} else {
layoutType = "".concat(layoutType, "-gpu");
}
}
if (_g6Core.Util.isForce(layoutType)) {
onTick_1 = layoutCfg.onTick, animate_1 = layoutCfg.animate;
isDefaultAnimateLayout_1 = animate_1 === undefined && (layoutType === 'force' || layoutType === 'force2');
tick = function tick() {
if (onTick_1) {
onTick_1();
}
if (animate_1 || isDefaultAnimateLayout_1) graph.refreshPositions();
};
layoutCfg.tick = tick;
} else if (layoutType === 'comboForce' || layoutType === 'comboCombined') {
layoutCfg.comboTrees = graph.get('comboTrees');
}
enableTick = false;
try {
layoutMethod = new _layout.Layout[layoutType](layoutCfg);
if (this.layoutMethods[order]) {
this.layoutMethods[order].destroy();
}
this.layoutMethods[order] = layoutMethod;
} catch (e) {
console.warn("The layout method: '".concat(layoutType, "' does not exist! Please specify it first."));
reject();
}
// 是否需要迭代的方式完成布局。这里是来自布局对象的实例属性,是由布局的定义者在布局类定义的。
enableTick = layoutMethod.enableTick;
if (enableTick) {
onTick_2 = layoutCfg.onTick;
tick = function tick() {
if (onTick_2) {
onTick_2();
}
graph.refreshPositions();
};
layoutMethod.tick = tick;
}
layoutData = this.filterLayoutData(this.data, layoutCfg);
addLayoutOrder(layoutData, order);
layoutMethod.init(layoutData);
// 若存在节点没有位置信息,且没有设置 layout,在 initPositions 中 random 给出了所有节点的位置,不需要再次执行 random 布局
// 所有节点都有位置信息,且指定了 layout,则执行布局(代表不是第一次进行布局)
graph.emit('beforesublayout', {
type: layoutType
});
return [4 /*yield*/, layoutMethod.execute()];
case 1:
_a.sent();
if (layoutMethod.isCustomLayout && layoutCfg.onLayoutEnd) layoutCfg.onLayoutEnd();
return [2 /*return*/];
}
});
});
});
};
LayoutController.prototype.updateLayoutMethod = function (layoutMethod, layoutCfg) {
var _this = this;
return new Promise(function (reslove, reject) {
return (0, _tslib.__awaiter)(_this, void 0, void 0, function () {
var graph, layoutType, onTick_3, animate_2, isDefaultAnimateLayout_2, tick, layoutData;
return (0, _tslib.__generator)(this, function (_a) {
switch (_a.label) {
case 0:
graph = this.graph;
layoutType = layoutCfg === null || layoutCfg === void 0 ? void 0 : layoutCfg.type;
// 每个布局方法都需要注册
layoutCfg.onLayoutEnd = function () {
graph.emit('aftersublayout', {
type: layoutType
});
reslove();
};
if (_g6Core.Util.isForce(layoutType)) {
onTick_3 = layoutCfg.onTick, animate_2 = layoutCfg.animate;
isDefaultAnimateLayout_2 = animate_2 === undefined && (layoutType === 'force' || layoutType === 'force2');
tick = function tick() {
onTick_3 === null || onTick_3 === void 0 ? void 0 : onTick_3();
if (animate_2 || isDefaultAnimateLayout_2) graph.refreshPositions();
};
layoutCfg.tick = tick;
}
layoutData = this.filterLayoutData(this.data, layoutCfg);
layoutMethod.init(layoutData);
layoutMethod.updateCfg(layoutCfg);
graph.emit('beforesublayout', {
type: layoutType
});
return [4 /*yield*/, layoutMethod.execute()];
case 1:
_a.sent();
if (layoutMethod.isCustomLayout && layoutCfg.onLayoutEnd) layoutCfg.onLayoutEnd();
return [2 /*return*/];
}
});
});
});
};
/**
* @param {function} success callback
* @return {boolean} 是否使用web worker布局
*/
LayoutController.prototype.layout = function (success) {
var _this = this;
var _a;
var graph = this.graph;
if (!graph || graph.get('destroyed')) return;
this.data = this.setDataFromGraph();
var _b = this.data,
nodes = _b.nodes,
hiddenNodes = _b.hiddenNodes;
if (!nodes) {
return false;
}
var width = graph.get('width');
var height = graph.get('height');
var layoutCfg = {};
Object.assign(layoutCfg, {
width: width,
height: height,
center: [width / 2, height / 2]
}, this.layoutCfg);
this.layoutCfg = layoutCfg;
var layoutType = layoutCfg.type;
var prevHasNodes = false;
(_a = this.layoutMethods) === null || _a === void 0 ? void 0 : _a.forEach(function (method) {
var _a;
return prevHasNodes = !!((_a = method.nodes) === null || _a === void 0 ? void 0 : _a.length) || prevHasNodes;
});
var preLayoutTypes = this.destoryLayoutMethods();
graph.emit('beforelayout');
var start = Promise.resolve();
// 增量情况下(上一次的布局与当前布局一致),上一次有节点,使用 treakInit
if (prevHasNodes && layoutType && (preLayoutTypes === null || preLayoutTypes === void 0 ? void 0 : preLayoutTypes.length) === 1 && preLayoutTypes[0] === layoutType) {
this.tweakInit();
} else {
// 初始化位置,若配置了 preset,则使用 preset 的参数生成布局作为预布局,否则使用 grid
start = this.initPositions(layoutCfg.center, nodes);
}
// init hidden nodes
var initHiddenPromise = this.initPositions(layoutCfg.center, hiddenNodes);
initHiddenPromise.then();
// 若用户指定开启 gpu,且当前浏览器支持 webgl,且该算法存在 GPU 版本(目前仅支持 fruchterman 和 gForce),使用 gpu 版本的布局
this.isGPU = getGPUEnabled(layoutCfg, layoutType);
// 在 onAllLayoutEnd 中执行用户自定义 onLayoutEnd,触发 afterlayout、更新节点位置、fitView/fitCenter、触发 afterrender
var onLayoutEnd = layoutCfg.onLayoutEnd,
layoutEndFormatted = layoutCfg.layoutEndFormatted,
adjust = layoutCfg.adjust;
if (!layoutEndFormatted) {
layoutCfg.layoutEndFormatted = true;
layoutCfg.onAllLayoutEnd = function () {
return (0, _tslib.__awaiter)(_this, void 0, void 0, function () {
return (0, _tslib.__generator)(this, function (_a) {
switch (_a.label) {
case 0:
// 执行用户自定义 onLayoutEnd
if (onLayoutEnd) {
onLayoutEnd(nodes);
}
// 更新节点位置
this.refreshLayout();
if (!(adjust && layoutCfg.pipes)) return [3 /*break*/, 2];
return [4 /*yield*/, this.adjustPipesBox(this.data, adjust)];
case 1:
_a.sent();
this.refreshLayout();
_a.label = 2;
case 2:
// 触发 afterlayout
graph.emit('afterlayout');
return [2 /*return*/];
}
});
});
};
}
this.stopWorker();
if (layoutCfg.workerEnabled && this.layoutWithWorker(this.data, success)) {
// 如果启用布局web worker并且浏览器支持web worker,用web worker布局。否则回退到不用web worker布局。
return true;
}
var hasLayout = false;
if (layoutCfg.type) {
hasLayout = true;
start = start.then(function () {
return (0, _tslib.__awaiter)(_this, void 0, void 0, function () {
return (0, _tslib.__generator)(this, function (_a) {
switch (_a.label) {
case 0:
return [4 /*yield*/, this.execLayoutMethod(layoutCfg, 0)];
case 1:
return [2 /*return*/, _a.sent()];
}
});
});
});
} else if (layoutCfg.pipes) {
hasLayout = true;
layoutCfg.pipes.forEach(function (cfg, index) {
start = start.then(function () {
return (0, _tslib.__awaiter)(_this, void 0, void 0, function () {
return (0, _tslib.__generator)(this, function (_a) {
switch (_a.label) {
case 0:
return [4 /*yield*/, this.execLayoutMethod(cfg, index)];
case 1:
return [2 /*return*/, _a.sent()];
}
});
});
});
});
}
if (hasLayout) {
// 最后统一在外部调用onAllLayoutEnd
start.then(function () {
if (layoutCfg.onAllLayoutEnd) layoutCfg.onAllLayoutEnd();
// 在执行 execute 后立即执行 success,且在 timeBar 中有 throttle,可以防止 timeBar 监听 afterrender 进行 changeData 后 layout,从而死循环
// 对于 force 一类布局完成后的 fitView 需要用户自己在 onLayoutEnd 中配置
if (success) success();
}).catch(function (error) {
console.warn('graph layout failed,', error);
});
} else {
// 无布局配置
graph.refreshPositions();
success === null || success === void 0 ? void 0 : success();
}
return false;
};
/**
* 增量数据初始化位置
*/
LayoutController.prototype.tweakInit = function () {
var _a = this,
data = _a.data,
graph = _a.graph;
var nodes = data.nodes,
edges = data.edges;
if (!(nodes === null || nodes === void 0 ? void 0 : nodes.length)) return;
var positionMap = {};
nodes.forEach(function (node) {
var x = node.x,
y = node.y;
if (!isNaN(x) && !isNaN(y)) {
positionMap[node.id] = {
x: x,
y: y
};
// 有位置信息,则是原有节点,增加 mass
node.mass = node.mass || 2;
}
});
edges.forEach(function (edge) {
var source = edge.source,
target = edge.target;
var sourcePosition = positionMap[source];
var targetPosition = positionMap[target];
if (!sourcePosition && targetPosition) {
positionMap[source] = {
x: targetPosition.x + (Math.random() - 0.5) * 80,
y: targetPosition.y + (Math.random() - 0.5) * 80
};
} else if (!targetPosition && sourcePosition) {
positionMap[target] = {
x: sourcePosition.x + (Math.random() - 0.5) * 80,
y: sourcePosition.y + (Math.random() - 0.5) * 80
};
}
});
var width = graph.get('width');
var height = graph.get('height');
nodes.forEach(function (node) {
var position = positionMap[node.id] || {
x: width / 2 + (Math.random() - 0.5) * 20,
y: height / 2 + (Math.random() - 0.5) * 20
};
node.x = position.x;
node.y = position.y;
});
};
LayoutController.prototype.initWithPreset = function (hasPresetCallback, noPresetCallback) {
var _this = this;
return new Promise(function (resolve, reject) {
return (0, _tslib.__awaiter)(_this, void 0, void 0, function () {
var _a, layoutCfg, data, preset, isGPU, layoutType, presetLayout;
return (0, _tslib.__generator)(this, function (_b) {
switch (_b.label) {
case 0:
_a = this, layoutCfg = _a.layoutCfg, data = _a.data;
preset = layoutCfg.preset;
if (!(preset === null || preset === void 0 ? void 0 : preset.type) || !_layout.Layout[preset === null || preset === void 0 ? void 0 : preset.type]) {
noPresetCallback === null || noPresetCallback === void 0 ? void 0 : noPresetCallback();
resolve();
return [2 /*return*/, false];
}
isGPU = getGPUEnabled(preset, preset.type);
layoutType = isGPU ? "".concat(preset.type, "-gpu") : preset.type;
presetLayout = new _layout.Layout[layoutType](preset);
delete layoutCfg.preset;
presetLayout.init(data);
return [4 /*yield*/, presetLayout.execute()];
case 1:
_b.sent();
hasPresetCallback === null || hasPresetCallback === void 0 ? void 0 : hasPresetCallback();
resolve();
return [2 /*return*/, true];
}
});
});
});
};
/**
* layout with web worker
* @param {object} data graph data
* @return {boolean} 是否支持web worker
*/
LayoutController.prototype.layoutWithWorker = function (data, success) {
var _this = this;
var _a = this,
layoutCfg = _a.layoutCfg,
graph = _a.graph;
var worker = this.getWorker();
// 每次worker message event handler调用之间的共享数据,会被修改。
var workerData = this.workerData;
if (!worker) {
return false;
}
workerData.requestId = null;
workerData.requestId2 = null;
workerData.currentTick = null;
workerData.currentTickData = null;
graph.emit('beforelayout');
var start = Promise.resolve();
var hasLayout = false;
if (layoutCfg.type) {
hasLayout = true;
start = start.then(function () {
return _this.runWebworker(worker, data, layoutCfg);
});
} else if (layoutCfg.pipes) {
hasLayout = true;
var _loop_1 = function _loop_1(cfg) {
start = start.then(function () {
return _this.runWebworker(worker, data, cfg);
});
};
for (var _i = 0, _b = layoutCfg.pipes; _i < _b.length; _i++) {
var cfg = _b[_i];
_loop_1(cfg);
}
}
if (hasLayout) {
// 最后统一在外部调用onAllLayoutEnd
start.then(function () {
if (layoutCfg.onAllLayoutEnd) layoutCfg.onAllLayoutEnd();
success === null || success === void 0 ? void 0 : success();
}).catch(function (error) {
console.error('layout failed', error);
});
}
return true;
};
LayoutController.prototype.runWebworker = function (worker, allData, layoutCfg) {
var _this = this;
var isGPU = this.isGPU;
var data = this.filterLayoutData(allData, layoutCfg);
var nodes = data.nodes,
edges = data.edges;
var offScreenCanvas = document.createElement('canvas');
var gpuWorkerAbility = isGPU && typeof window !== 'undefined' &&
// eslint-disable-next-line @typescript-eslint/dot-notation
window.navigator && !navigator["gpu"] &&
// WebGPU 还不支持 OffscreenCanvas
'OffscreenCanvas' in window && 'transferControlToOffscreen' in offScreenCanvas;
// NOTE: postMessage的message参数里面不能包含函数,否则postMessage会报错,
// 例如:'function could not be cloned'。
// 详情参考:https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
// 所以这里需要把过滤layoutCfg里的函数字段过滤掉。
var filteredLayoutCfg = filterObject(layoutCfg, function (value) {
return typeof value !== 'function';
});
if (!gpuWorkerAbility) {
worker.postMessage({
type: _layoutConst.LAYOUT_MESSAGE.RUN,
nodes: nodes,
edges: edges,
layoutCfg: filteredLayoutCfg
});
} else {
var offscreen = offScreenCanvas.transferControlToOffscreen();
// filteredLayoutCfg.canvas = offscreen;
filteredLayoutCfg.type = "".concat(filteredLayoutCfg.type, "-gpu");
worker.postMessage({
type: _layoutConst.LAYOUT_MESSAGE.GPURUN,
nodes: nodes,
edges: edges,
layoutCfg: filteredLayoutCfg,
canvas: offscreen
}, [offscreen]);
}
return new Promise(function (reslove, reject) {
worker.onmessage = function (event) {
_this.handleWorkerMessage(reslove, reject, event, data, layoutCfg);
};
});
};
// success callback will be called when updating graph positions for the first time.
LayoutController.prototype.handleWorkerMessage = function (reslove, reject, event, data, layoutCfg) {
var _a = this,
graph = _a.graph,
workerData = _a.workerData;
var eventData = event.data;
var type = eventData.type;
var onTick = function onTick() {
if (layoutCfg.onTick) {
layoutCfg.onTick();
}
};
switch (type) {
case _layoutConst.LAYOUT_MESSAGE.TICK:
workerData.currentTick = eventData.currentTick;
workerData.currentTickData = eventData;
if (!workerData.requestId) {
workerData.requestId = helper.requestAnimationFrame(function requestId() {
updateLayoutPosition(data, eventData);
graph.refreshPositions();
onTick();
if (eventData.currentTick === eventData.totalTicks) {
// 如果是最后一次tick
reslove();
} else if (workerData.currentTick === eventData.totalTicks) {
// 注意这里workerData.currentTick可能已经不再是前面赋值时候的值了,
// 因为在requestAnimationFrame等待时间里,可能产生新的tick。
// 如果当前tick不是最后一次tick,并且所有的tick消息都已发出来了,那么需要用最后一次tick的数据再刷新一次。
workerData.requestId2 = helper.requestAnimationFrame(function requestId2() {
updateLayoutPosition(data, workerData.currentTickData);
graph.refreshPositions();
workerData.requestId2 = null;
onTick();
reslove();
});
}
workerData.requestId = null;
});
}
break;
case _layoutConst.LAYOUT_MESSAGE.END:
// 如果没有tick消息(非力导布局)
if (workerData.currentTick == null) {
updateLayoutPosition(data, eventData);
reslove();
}
break;
case _layoutConst.LAYOUT_MESSAGE.GPUEND:
// 如果没有tick消息(非力导布局)
if (workerData.currentTick == null) {
updateGPUWorkerLayoutPosition(data, eventData);
reslove();
}
break;
case _layoutConst.LAYOUT_MESSAGE.ERROR:
console.warn('Web-Worker layout error!', eventData.message);
reject();
break;
default:
reject();
break;
}
};
// 更新布局参数
LayoutController.prototype.updateLayoutCfg = function (cfg) {
var _this = this;
var _a = this,
graph = _a.graph,
layoutMethods = _a.layoutMethods;
if (!graph || graph.get('destroyed')) return;
// disableTriggerLayout 不触发重新布局,仅更新参数
var disableTriggerLayout = cfg.disableTriggerLayout,
otherCfg = (0, _tslib.__rest)(cfg, ["disableTriggerLayout"]);
var layoutCfg = (0, _util.mix)({}, this.layoutCfg, otherCfg);
this.layoutCfg = layoutCfg;
// disableTriggerLayout 不触发重新布局,仅更新参数
if (disableTriggerLayout) {
return;
}
if (!(layoutMethods === null || layoutMethods === void 0 ? void 0 : layoutMethods.length)) {
this.layout();
return;
}
this.data = this.setDataFromGraph();
this.stopWorker();
if (otherCfg.workerEnabled && this.layoutWithWorker(this.data, null)) {
// 如果启用布局web worker并且浏览器支持web worker,用web worker布局。否则回退到不用web worker布局。
return;
}
graph.emit('beforelayout');
var start = Promise.resolve();
var hasLayout = false;
if ((layoutMethods === null || layoutMethods === void 0 ? void 0 : layoutMethods.length) === 1) {
hasLayout = true;
start = start.then(function () {
return (0, _tslib.__awaiter)(_this, void 0, void 0, function () {
return (0, _tslib.__generator)(this, function (_a) {
switch (_a.label) {
case 0:
return [4 /*yield*/, this.updateLayoutMethod(layoutMethods[0], layoutCfg)];
case 1:
return [2 /*return*/, _a.sent()];
}
});
});
});
} else if (layoutMethods === null || layoutMethods === void 0 ? void 0 : layoutMethods.length) {
hasLayout = true;
layoutMethods.forEach(function (layoutMethod, index) {
var currentCfg = layoutCfg.pipes[index];
start = start.then(function () {
return (0, _tslib.__awaiter)(_this, void 0, void 0, function () {
return (0, _tslib.__generator)(this, function (_a) {
switch (_a.label) {
case 0:
return [4 /*yield*/, this.updateLayoutMethod(layoutMethod, currentCfg)];
case 1:
return [2 /*return*/, _a.sent()];
}
});
});
});
});
}
if (hasLayout) {
start.then(function () {
if (layoutCfg.onAllLayoutEnd) layoutCfg.onAllLayoutEnd();
}).catch(function (error) {
console.warn('layout failed', error);
});
}
};
LayoutController.prototype.adjustPipesBox = function (data, adjust) {
var _this = this;
return new Promise(function (resolve) {
var nodes = data.nodes;
if (!(nodes === null || nodes === void 0 ? void 0 : nodes.length)) {
resolve();
}
if (!LAYOUT_PIPES_ADJUST_NAMES.includes(adjust)) {
console.warn("The adjust type ".concat(adjust, " is not supported yet, please assign it with 'force', 'grid', or 'circular'."));
resolve();
}
var layoutCfg = {
center: _this.layoutCfg.center,
nodeSize: function nodeSize(d) {
return Math.max(d.height, d.width);
},
preventOverlap: true,
onLayoutEnd: function onLayoutEnd() {}
};
// 计算出大单元
var _a = _this.getLayoutBBox(nodes),
groupNodes = _a.groupNodes,
layoutNodes = _a.layoutNodes;
var preNodes = (0, _util.clone)(layoutNodes);
// 根据大单元坐标的变化,调整这里面每个小单元nodes
layoutCfg.onLayoutEnd = function () {
layoutNodes === null || layoutNodes === void 0 ? void 0 : layoutNodes.forEach(function (ele, index) {
var _a, _b, _c;
var dx = ele.x - ((_a = preNodes[index]) === null || _a === void 0 ? void 0 : _a.x);
var dy = ele.y - ((_b = preNodes[index]) === null || _b === void 0 ? void 0 : _b.y);
(_c = groupNodes[index]) === null || _c === void 0 ? void 0 : _c.forEach(function (n) {
n.x += dx;
n.y += dy;
});
});
resolve();
};
var layoutMethod = new _layout.Layout[adjust](layoutCfg);
layoutMethod.layout({
nodes: layoutNodes
});
});
};
LayoutController.prototype.destroy = function () {
this.destoryLayoutMethods();
var worker = this.worker;
if (worker) {
worker.terminate();
this.worker = null;
}
this.destroyed = true;
this.graph.set('layout', undefined);
this.layoutCfg = undefined;
this.layoutType = undefined;
this.layoutMethods = undefined;
this.graph = null;
};
return LayoutController;
}(_g6Core.AbstractLayout);
var _default = exports.default = LayoutController;
function updateLayoutPosition(data, layoutData) {
var nodes = data.nodes;
var layoutNodes = layoutData.nodes;
var nodeLength = nodes.length;
for (var i = 0; i < nodeLength; i++) {
var node = nodes[i];
node.x = layoutNodes[i].x;
node.y = layoutNodes[i].y;
}
}
function filterObject(collection, callback) {
var result = {};
if (collection && _typeof(collection) === 'object') {
Object.keys(collection).forEach(function (key) {
if (collection.hasOwnProperty(key) && callback(collection[key])) {
result[key] = collection[key];
}
});
return result;
}
return collection;
}
function updateGPUWorkerLayoutPosition(data, layoutData) {
var nodes = data.nodes;
var vertexEdgeData = layoutData.vertexEdgeData;
var nodeLength = nodes.length;
for (var i = 0; i < nodeLength; i++) {
var node = nodes[i];
var x = vertexEdgeData[4 * i];
var y = vertexEdgeData[4 * i + 1];
node.x = x;
node.y = y;
}
}
function addLayoutOrder(data, order) {
var _a;
if (!((_a = data === null || data === void 0 ? void 0 : data.nodes) === null || _a === void 0 ? void 0 : _a.length)) {
return;
}
var nodes = data.nodes;
nodes.forEach(function (node) {
node.layoutOrder = order;
});
}
function hasGPUVersion(layoutName) {
return GPU_LAYOUT_NAMES.includes(layoutName);
}
function getGPUEnabled(layoutCfg, layoutType) {
var type = layoutType;
// 防止用户直接用 -gpu 结尾指定布局
if (layoutType && layoutType.split('-')[1] === 'gpu') {
type = layoutType.split('-')[0];
layoutCfg.gpuEnabled = true;
}
var enableGPU = false;
if (layoutCfg.gpuEnabled) {
enableGPU = true;
// 打开下面语句将会导致 webworker 报找不到 window
if (!(0, _gpu.gpuDetector)().webgl) {
console.warn("Your browser does not support webGL or GPGPU. The layout will run in CPU.");
enableGPU = false;
}
}
// the layout does not support GPU, will run in CPU
if (enableGPU && !hasGPUVersion(type)) {
console.warn("The '".concat(type, "' layout does not support GPU calculation for now, it will run in CPU."));
enableGPU = false;
}
return enableGPU;
}