UNPKG

cione-comp-lib

Version:

`$ yarn add cione-comp-lib` 或者 `$ npm i cione-comp-lib -S`

214 lines (213 loc) 6.18 kB
import Texture2D from "../../../../claygl/src/Texture2D.mjs"; import Texture from "../../../../claygl/src/Texture.mjs"; import forceAtlas2Worker from "./forceAtlas2Worker.mjs"; var workerUrl = forceAtlas2Worker.toString(); workerUrl = workerUrl.slice(workerUrl.indexOf("{") + 1, workerUrl.lastIndexOf("}")); var defaultConfigs = { barnesHutOptimize: true, barnesHutTheta: 1.5, repulsionByDegree: true, linLogMode: false, strongGravityMode: false, gravity: 1, scaling: 1, edgeWeightInfluence: 1, jitterTolerence: 0.1, preventOverlap: false, dissuadeHubs: false, gravityCenter: null }; var ForceAtlas2 = function(options) { for (var name in defaultConfigs) { this[name] = defaultConfigs[name]; } if (options) { for (var name in options) { this[name] = options[name]; } } this._nodes = []; this._edges = []; this._disposed = false; this._positionTex = new Texture2D({ type: Texture.FLOAT, flipY: false, minFilter: Texture.NEAREST, magFilter: Texture.NEAREST }); }; ForceAtlas2.prototype.initData = function(nodes, edges) { var bb = new Blob([workerUrl]); var blobURL = window.URL.createObjectURL(bb); this._worker = new Worker(blobURL); this._worker.onmessage = this._$onupdate.bind(this); this._nodes = nodes; this._edges = edges; this._frame = 0; var nNodes = nodes.length; var nEdges = edges.length; var positionArr = new Float32Array(nNodes * 2); var massArr = new Float32Array(nNodes); var sizeArr = new Float32Array(nNodes); var edgeArr = new Float32Array(nEdges * 2); var edgeWeightArr = new Float32Array(nEdges); for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; positionArr[i * 2] = node.x; positionArr[i * 2 + 1] = node.y; massArr[i] = node.mass == null ? 1 : node.mass; sizeArr[i] = node.size == null ? 1 : node.size; } for (var i = 0; i < edges.length; i++) { var edge = edges[i]; var source = edge.node1; var target = edge.node2; edgeArr[i * 2] = source; edgeArr[i * 2 + 1] = target; edgeWeightArr[i] = edge.weight == null ? 1 : edge.weight; } var textureWidth = Math.ceil(Math.sqrt(nodes.length)); var textureHeight = textureWidth; var pixels = new Float32Array(textureWidth * textureHeight * 4); var positionTex = this._positionTex; positionTex.width = textureWidth; positionTex.height = textureHeight; positionTex.pixels = pixels; this._worker.postMessage({ cmd: "init", nodesPosition: positionArr, nodesMass: massArr, nodesSize: sizeArr, edges: edgeArr, edgesWeight: edgeWeightArr }); this._globalSpeed = Infinity; }; ForceAtlas2.prototype.updateOption = function(options) { var config = {}; for (var name in defaultConfigs) { config[name] = defaultConfigs[name]; } var nodes = this._nodes; var edges = this._edges; var nNodes = nodes.length; if (nNodes > 5e4) { config.jitterTolerence = 10; } else if (nNodes > 5e3) { config.jitterTolerence = 1; } else { config.jitterTolerence = 0.1; } if (nNodes > 100) { config.scaling = 2; } else { config.scaling = 10; } if (nNodes > 1e3) { config.barnesHutOptimize = true; } else { config.barnesHutOptimize = false; } if (options) { for (var name in defaultConfigs) { if (options[name] != null) { config[name] = options[name]; } } } if (!config.gravityCenter) { var min = [Infinity, Infinity]; var max = [-Infinity, -Infinity]; for (var i = 0; i < nodes.length; i++) { min[0] = Math.min(nodes[i].x, min[0]); min[1] = Math.min(nodes[i].y, min[1]); max[0] = Math.max(nodes[i].x, max[0]); max[1] = Math.max(nodes[i].y, max[1]); } config.gravityCenter = [(min[0] + max[0]) * 0.5, (min[1] + max[1]) * 0.5]; } for (var i = 0; i < edges.length; i++) { var node1 = edges[i].node1; var node2 = edges[i].node2; nodes[node1].degree = (nodes[node1].degree || 0) + 1; nodes[node2].degree = (nodes[node2].degree || 0) + 1; } if (this._worker) { this._worker.postMessage({ cmd: "updateConfig", config }); } }; ForceAtlas2.prototype.update = function(renderer, steps, cb) { if (steps == null) { steps = 1; } steps = Math.max(steps, 1); this._frame += steps; this._onupdate = cb; if (this._worker) { this._worker.postMessage({ cmd: "update", steps: Math.round(steps) }); } }; ForceAtlas2.prototype._$onupdate = function(e) { if (this._disposed) { return; } var positionArr = new Float32Array(e.data.buffer); this._globalSpeed = e.data.globalSpeed; this._positionArr = positionArr; this._updateTexture(positionArr); this._onupdate && this._onupdate(); }; ForceAtlas2.prototype.getNodePositionTexture = function() { return this._positionTex; }; ForceAtlas2.prototype.getNodeUV = function(nodeIndex, uv) { uv = uv || []; var textureWidth = this._positionTex.width; var textureHeight = this._positionTex.height; uv[0] = nodeIndex % textureWidth / (textureWidth - 1); uv[1] = Math.floor(nodeIndex / textureWidth) / (textureHeight - 1); return uv; }; ForceAtlas2.prototype.getNodes = function() { return this._nodes; }; ForceAtlas2.prototype.getEdges = function() { return this._edges; }; ForceAtlas2.prototype.isFinished = function(maxSteps) { return this._frame > maxSteps; }; ForceAtlas2.prototype.getNodePosition = function(renderer, out) { if (!out) { out = new Float32Array(this._nodes.length * 2); } if (this._positionArr) { for (var i = 0; i < this._positionArr.length; i++) { out[i] = this._positionArr[i]; } } return out; }; ForceAtlas2.prototype._updateTexture = function(positionArr) { var pixels = this._positionTex.pixels; var offset = 0; for (var i = 0; i < positionArr.length; ) { pixels[offset++] = positionArr[i++]; pixels[offset++] = positionArr[i++]; pixels[offset++] = 1; pixels[offset++] = 1; } this._positionTex.dirty(); }; ForceAtlas2.prototype.dispose = function(renderer) { this._disposed = true; this._worker = null; }; var ForceAtlas2$1 = ForceAtlas2; export { ForceAtlas2$1 as default };