echarts-graph-modularity
Advanced tools
Comparing version 1.1.0 to 2.0.0
@@ -10,1009 +10,34 @@ (function webpackUniversalModuleDefinition(root, factory) { | ||
root["echarts-graph-modularity"] = factory(root["echarts"]); | ||
})(this, function(__WEBPACK_EXTERNAL_MODULE_9__) { | ||
return /******/ (function(modules) { // webpackBootstrap | ||
/******/ // The module cache | ||
/******/ var installedModules = {}; | ||
/******/ | ||
/******/ // The require function | ||
/******/ function __webpack_require__(moduleId) { | ||
/******/ | ||
/******/ // Check if module is in cache | ||
/******/ if(installedModules[moduleId]) { | ||
/******/ return installedModules[moduleId].exports; | ||
/******/ } | ||
/******/ // Create a new module (and put it into the cache) | ||
/******/ var module = installedModules[moduleId] = { | ||
/******/ i: moduleId, | ||
/******/ l: false, | ||
/******/ exports: {} | ||
/******/ }; | ||
/******/ | ||
/******/ // Execute the module function | ||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); | ||
/******/ | ||
/******/ // Flag the module as loaded | ||
/******/ module.l = true; | ||
/******/ | ||
/******/ // Return the exports of the module | ||
/******/ return module.exports; | ||
/******/ } | ||
/******/ | ||
/******/ | ||
/******/ // expose the modules object (__webpack_modules__) | ||
/******/ __webpack_require__.m = modules; | ||
/******/ | ||
/******/ // expose the module cache | ||
/******/ __webpack_require__.c = installedModules; | ||
/******/ | ||
/******/ // define getter function for harmony exports | ||
/******/ __webpack_require__.d = function(exports, name, getter) { | ||
/******/ if(!__webpack_require__.o(exports, name)) { | ||
/******/ Object.defineProperty(exports, name, { | ||
/******/ configurable: false, | ||
/******/ enumerable: true, | ||
/******/ get: getter | ||
/******/ }); | ||
/******/ } | ||
/******/ }; | ||
/******/ | ||
/******/ // getDefaultExport function for compatibility with non-harmony modules | ||
/******/ __webpack_require__.n = function(module) { | ||
/******/ var getter = module && module.__esModule ? | ||
/******/ function getDefault() { return module['default']; } : | ||
/******/ function getModuleExports() { return module; }; | ||
/******/ __webpack_require__.d(getter, 'a', getter); | ||
/******/ return getter; | ||
/******/ }; | ||
/******/ | ||
/******/ // Object.prototype.hasOwnProperty.call | ||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; | ||
/******/ | ||
/******/ // __webpack_public_path__ | ||
/******/ __webpack_require__.p = ""; | ||
/******/ | ||
/******/ // Load entry module and return exports | ||
/******/ return __webpack_require__(__webpack_require__.s = 0); | ||
/******/ }) | ||
/************************************************************************/ | ||
/******/ ([ | ||
/* 0 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
})(self, function(__WEBPACK_EXTERNAL_MODULE_echarts_lib_echarts__) { | ||
return /******/ (() => { // webpackBootstrap | ||
/******/ var __webpack_modules__ = ({ | ||
module.exports = __webpack_require__(1); | ||
/***/ "./index.js": | ||
/*!******************!*\ | ||
!*** ./index.js ***! | ||
\******************/ | ||
/***/ ((module, __unused_webpack_exports, __webpack_require__) => { | ||
/***/ }), | ||
/* 1 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
module.exports = __webpack_require__(/*! ./src/main */ "./src/main.js"); | ||
var Modularity = __webpack_require__(2); | ||
var echarts = __webpack_require__(9); | ||
var createNGraph = __webpack_require__(10); | ||
function createModularityVisual(chartType) { | ||
return function (ecModel, api) { | ||
var paletteScope = {}; | ||
ecModel.eachSeriesByType(chartType, function (seriesModel) { | ||
var modularityOpt = seriesModel.get('modularity'); | ||
if (modularityOpt) { | ||
var graph = seriesModel.getGraph(); | ||
var idIndexMap = {}; | ||
var ng = createNGraph(); | ||
graph.data.each(function (idx) { | ||
var node = graph.getNodeByIndex(idx); | ||
idIndexMap[node.id] = idx; | ||
ng.addNode(node.id); | ||
return node.id; | ||
}); | ||
graph.edgeData.each('value', function (val, idx) { | ||
var edge = graph.getEdgeByIndex(idx); | ||
ng.addLink(edge.node1.id, edge.node2.id); | ||
return { | ||
source: edge.node1.id, | ||
target: edge.node2.id, | ||
value: val | ||
}; | ||
}); | ||
var modularity = new Modularity(seriesModel.get('modularity.resolution') || 1); | ||
var result = modularity.execute(ng); | ||
console.log(result); | ||
for (var id in result) { | ||
var comm = result[id]; | ||
graph.data.setItemVisual(idIndexMap[id], 'color', seriesModel.getColorFromPalette(comm, paletteScope)); | ||
} | ||
graph.edgeData.each(function (idx) { | ||
var itemModel = graph.edgeData.getItemModel(idx); | ||
var edge = graph.getEdgeByIndex(idx); | ||
var color = itemModel.get('lineStyle.normal.color'); | ||
switch (color) { | ||
case 'source': | ||
color = edge.node1.getVisual('color'); | ||
break; | ||
case 'target': | ||
color = edge.node2.getVisual('color'); | ||
break; | ||
} | ||
if (color != null) { | ||
edge.setVisual('color', color); | ||
} | ||
}); | ||
} | ||
}); | ||
}; | ||
} | ||
echarts.registerVisual(echarts.PRIORITY.VISUAL.CHART + 1, createModularityVisual('graph')); | ||
echarts.registerVisual(echarts.PRIORITY.VISUAL.CHART + 1, createModularityVisual('graphGL')); | ||
/***/ }), | ||
/* 2 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
/* | ||
Copyright 2008-2011 Gephi | ||
Authors : Patick J. McSweeney <pjmcswee@syr.edu>, Sebastien Heymann <seb@gephi.org> | ||
Website : http://www.gephi.org | ||
/***/ "./node_modules/ngraph.centrality/index.js": | ||
/*!*************************************************!*\ | ||
!*** ./node_modules/ngraph.centrality/index.js ***! | ||
\*************************************************/ | ||
/***/ ((module, __unused_webpack_exports, __webpack_require__) => { | ||
This file is part of Gephi. | ||
module.exports.degree = __webpack_require__(/*! ./src/degree.js */ "./node_modules/ngraph.centrality/src/degree.js"); | ||
module.exports.betweenness = __webpack_require__(/*! ./src/betweenness.js */ "./node_modules/ngraph.centrality/src/betweenness.js"); | ||
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. | ||
Copyright 2011 Gephi Consortium. All rights reserved. | ||
The contents of this file are subject to the terms of either the GNU | ||
General Public License Version 3 only ("GPL") or the Common | ||
Development and Distribution License("CDDL") (collectively, the | ||
"License"). You may not use this file except in compliance with the | ||
License. You can obtain a copy of the License at | ||
http://gephi.org/about/legal/license-notice/ | ||
or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the | ||
specific language governing permissions and limitations under the | ||
License. When distributing the software, include this License Header | ||
Notice in each file and include the License files at | ||
/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the | ||
License Header, with the fields enclosed by brackets [] replaced by | ||
your own identifying information: | ||
"Portions Copyrighted [year] [name of copyright owner]" | ||
If you wish your version of this file to be governed by only the CDDL | ||
or only the GPL Version 3, indicate your decision by adding | ||
"[Contributor] elects to include this software in this distribution | ||
under the [CDDL or GPL Version 3] license." If you do not indicate a | ||
single choice of license, a recipient has the option to distribute | ||
your version of this file under either the CDDL, the GPL Version 3 or | ||
to extend the choice of license to its licensees as provided above. | ||
However, if you add GPL Version 3 code and therefore, elected the GPL | ||
Version 3 license, then the option applies only if the new code is | ||
made subject to such option by the copyright holder. | ||
Contributor(s): Thomas Aynaud <taynaud@gmail.com> | ||
Portions Copyrighted 2011 Gephi Consortium. | ||
*/ | ||
var CommunityStructure = __webpack_require__(3) | ||
, centrality = __webpack_require__(6) | ||
; | ||
/** | ||
* @constructor | ||
*/ | ||
function Modularity (resolution, useWeight) { | ||
this.isRandomized = false; | ||
this.useWeight = useWeight; | ||
this.resolution = resolution || 1.; | ||
/** | ||
* @type {CommunityStructure} | ||
*/ | ||
this.structure = null; | ||
} | ||
/** | ||
* @param {IGraph} graph | ||
*/ | ||
Modularity.prototype.execute = function (graph/*, AttributeModel attributeModel*/) { | ||
this.structure = new CommunityStructure(graph, this.useWeight); | ||
var comStructure = new Array(graph.getNodesCount()); | ||
var computedModularityMetrics = this.computeModularity( | ||
graph | ||
, this.structure | ||
, comStructure | ||
, this.resolution | ||
, this.isRandomized | ||
, this.useWeight | ||
); | ||
var result = {}; | ||
this.structure.map.forEach(function (i, node) { | ||
result[node] = comStructure[i]; | ||
}); | ||
return result; | ||
}; | ||
/** | ||
* | ||
* @param {IGraph} graph | ||
* @param {CommunityStructure} theStructure | ||
* @param {Array.<Number>} comStructure | ||
* @param {Number} currentResolution | ||
* @param {Boolean} randomized | ||
* @param {Boolean} weighted | ||
* @returns {Object.<String, Number>} | ||
*/ | ||
Modularity.prototype.computeModularity = function(graph, theStructure, comStructure, currentResolution, randomized, weighted) { | ||
function getRandomInt(min, max) { | ||
return Math.floor(Math.random() * (max - min)) + min; | ||
} | ||
var totalWeight = theStructure.graphWeightSum; | ||
var nodeDegrees = theStructure.weights.slice(); | ||
var /** @type {Object.<String, Number>} */ results = Object.create(null); | ||
var someChange = true; | ||
while (someChange) { | ||
someChange = false; | ||
var localChange = true; | ||
while (localChange) { | ||
localChange = false; | ||
var start = 0; | ||
if (randomized) { | ||
//start = Math.abs(rand.nextInt()) % theStructure.N; | ||
start = getRandomInt(0,theStructure.N); | ||
} | ||
var step = 0; | ||
for (var i = start; step < theStructure.N; i = (i + 1) % theStructure.N) { | ||
step++; | ||
var bestCommunity = this.updateBestCommunity(theStructure, i, currentResolution); | ||
if ((theStructure.nodeCommunities[i] != bestCommunity) && (bestCommunity != null)) { | ||
theStructure.moveNodeTo(i, bestCommunity); | ||
localChange = true; | ||
} | ||
} | ||
someChange = localChange || someChange; | ||
} | ||
if (someChange) { | ||
theStructure.zoomOut(); | ||
} | ||
} | ||
this.fillComStructure(graph, theStructure, comStructure); | ||
/* | ||
//TODO: uncomment when finalQ will be implemented | ||
var degreeCount = this.fillDegreeCount(graph, theStructure, comStructure, nodeDegrees, weighted); | ||
var computedModularity = this._finalQ(comStructure, degreeCount, graph, theStructure, totalWeight, 1., weighted); | ||
var computedModularityResolution = this._finalQ(comStructure, degreeCount, graph, theStructure, totalWeight, currentResolution, weighted); | ||
results["modularity"] = computedModularity; | ||
results["modularityResolution"] = computedModularityResolution; | ||
*/ | ||
return results; | ||
}; | ||
/** | ||
* @param {CommunityStructure} theStructure | ||
* @param {Number} i | ||
* @param {Number} currentResolution | ||
* @returns {Community} | ||
*/ | ||
Modularity.prototype.updateBestCommunity = function(theStructure, i, currentResolution) { | ||
var best = this.q(i, theStructure.nodeCommunities[i], theStructure, currentResolution); | ||
var bestCommunity = theStructure.nodeCommunities[i]; | ||
//var /*Set<Community>*/ iter = theStructure.nodeConnectionsWeight[i].keySet(); | ||
theStructure.nodeConnectionsWeight[i].forEach(function (_$$val, com) { | ||
var qValue = this.q(i, com, theStructure, currentResolution); | ||
if (qValue > best) { | ||
best = qValue; | ||
bestCommunity = com; | ||
} | ||
}, this); | ||
return bestCommunity; | ||
}; | ||
/** | ||
* | ||
* @param {IGraph} graph | ||
* @param {CommunityStructure} theStructure | ||
* @param {Array.<Number>} comStructure | ||
* @returns {Array.<Number>} | ||
*/ | ||
Modularity.prototype.fillComStructure = function(graph, theStructure, comStructure) { | ||
var count = 0; | ||
theStructure.communities.forEach(function (com) { | ||
com.nodes.forEach(function (node) { | ||
var hidden = theStructure.invMap.get(node); | ||
hidden.nodes.forEach( function (nodeInt){ | ||
comStructure[nodeInt] = count; | ||
}); | ||
}); | ||
count++; | ||
}); | ||
return comStructure; | ||
}; | ||
/** | ||
* @param {IGraph} graph | ||
* @param {CommunityStructure} theStructure | ||
* @param {Array.<Number>} comStructure | ||
* @param {Array.<Number>} nodeDegrees | ||
* @param {Boolean} weighted | ||
* @returns {Array.<Number>} | ||
*/ | ||
Modularity.prototype.fillDegreeCount = function(graph, theStructure, comStructure, nodeDegrees, weighted) { | ||
var degreeCount = new Array(theStructure.communities.length); | ||
var degreeCentrality = centrality.degree(graph); | ||
graph.forEachNode(function(node){ | ||
var index = theStructure.map.get(node); | ||
if (weighted) { | ||
degreeCount[comStructure[index]] += nodeDegrees[index]; | ||
} else { | ||
degreeCount[comStructure[index]] += degreeCentrality[node.id]; | ||
} | ||
}); | ||
return degreeCount; | ||
}; | ||
/** | ||
* | ||
* @param {Array.<Number>} struct | ||
* @param {Array.<Number>} degrees | ||
* @param {IGraph} graph | ||
* @param {CommunityStructure} theStructure | ||
* @param {Number} totalWeight | ||
* @param {Number} usedResolution | ||
* @param {Boolean} weighted | ||
* @returns {Number} | ||
*/ | ||
Modularity.prototype._finalQ = function(struct, degrees, graph, theStructure, totalWeight, usedResolution, weighted) { | ||
//TODO: rewrite for wighted version of algorithm | ||
throw new Error("not implemented properly"); | ||
var res = 0; | ||
var internal = new Array(degrees.length); | ||
graph.forEachNode(function(n){ | ||
var n_index = theStructure.map.get(n); | ||
graph.forEachLinkedNode(n.id, function(neighbor){ | ||
if (n == neighbor) { | ||
return; | ||
} | ||
var neigh_index = theStructure.map.get(neighbor); | ||
if (struct[neigh_index] == struct[n_index]) { | ||
if (weighted) { | ||
//throw new Error("weighted aren't implemented"); | ||
//internal[struct[neigh_index]] += graph.getEdge(n, neighbor).getWeight(); | ||
} else { | ||
internal[struct[neigh_index]]++; | ||
} | ||
} | ||
}.bind(this), false); | ||
}.bind(this)); | ||
for (var i = 0; i < degrees.length; i++) { | ||
internal[i] /= 2.0; | ||
res += usedResolution * (internal[i] / totalWeight) - Math.pow(degrees[i] / (2 * totalWeight), 2);//HERE | ||
} | ||
return res; | ||
}; | ||
/** | ||
* | ||
* @param {Number} nodeId | ||
* @param {Community} community | ||
* @param {CommunityStructure} theStructure | ||
* @param {Number} currentResolution | ||
* @returns {Number} | ||
*/ | ||
Modularity.prototype.q = function(nodeId, community, theStructure, currentResolution) { | ||
var edgesToFloat = theStructure.nodeConnectionsWeight[nodeId].get(community); | ||
var edgesTo = 0; | ||
if (edgesToFloat != null) { | ||
edgesTo = edgesToFloat; | ||
} | ||
var weightSum = community.weightSum; | ||
var nodeWeight = theStructure.weights[nodeId]; | ||
var qValue = currentResolution * edgesTo - (nodeWeight * weightSum) / (2.0 * theStructure.graphWeightSum); | ||
if ((theStructure.nodeCommunities[nodeId] == community) && (theStructure.nodeCommunities[nodeId].size() > 1)) { | ||
qValue = currentResolution * edgesTo - (nodeWeight * (weightSum - nodeWeight)) / (2.0 * theStructure.graphWeightSum); | ||
} | ||
if ((theStructure.nodeCommunities[nodeId] == community) && (theStructure.nodeCommunities[nodeId].size() == 1)) { | ||
qValue = 0.; | ||
} | ||
return qValue; | ||
}; | ||
module.exports = Modularity; | ||
/***/ }), | ||
/* 3 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
"use strict"; | ||
/***/ "./node_modules/ngraph.centrality/src/betweenness.js": | ||
/*!***********************************************************!*\ | ||
!*** ./node_modules/ngraph.centrality/src/betweenness.js ***! | ||
\***********************************************************/ | ||
/***/ ((module) => { | ||
var Community = __webpack_require__(4) | ||
, ModEdge = __webpack_require__(5) | ||
; | ||
/** | ||
* | ||
* @param {IGraph} graph | ||
* @param useWeight | ||
* @param {CommunityStructure} structure | ||
* @constructor | ||
*/ | ||
function CommunityStructure(graph, useWeight) { | ||
//this.graph = graph; | ||
this.N = graph.getNodesCount(); | ||
this.graphWeightSum = 0; | ||
this.structure = this; | ||
/** @type {Map.<Number, Community>} */ | ||
this.invMap = new Map(); | ||
/** @type {Array.< Map.<Community, Number> >} */ | ||
this.nodeConnectionsWeight = new Array(this.N); | ||
/** @type {Array.< Map.<Community, Number> >} */ | ||
this.nodeConnectionsCount = new Array(this.N); | ||
/** @type {Array.<Community>} */ | ||
this.nodeCommunities = new Array(this.N); | ||
/** @type {Map.<Node, Number>} */ | ||
this.map = new Map(); | ||
/** @type {Array.< Array.<ModEdge> >} */ | ||
this.topology = new Array(this.N); | ||
for (var i = 0; i < this.N; i++) this.topology[i] = []; | ||
/** @type {Array.<Community>} */ | ||
this.communities = []; | ||
/**@type {Array.<Number>} */ | ||
this.weights = new Array(this.N); | ||
var index = 0; | ||
graph.forEachNode(function (node) { | ||
this.map.set(node.id, index); | ||
this.nodeCommunities[index] = new Community(this); | ||
this.nodeConnectionsWeight[index] = new Map(); | ||
this.nodeConnectionsCount[index] = new Map(); | ||
this.weights[index] = 0; | ||
this.nodeCommunities[index].seed(index); | ||
var hidden = new Community(this); | ||
hidden.nodes.add(index); | ||
this.invMap.set(index, hidden); | ||
this.communities.push(this.nodeCommunities[index]); | ||
index++; | ||
}.bind(this)); | ||
graph.forEachLink(function (link) { | ||
var node_index = this.map.get(link.fromId) | ||
, neighbor_index = this.map.get(link.toId) | ||
, weight = 1 | ||
; | ||
if (node_index === neighbor_index) { | ||
return; | ||
} | ||
if (useWeight) { | ||
weight = link.data.weight; | ||
} | ||
this.setUpLink(node_index, neighbor_index, weight); | ||
this.setUpLink(neighbor_index, node_index, weight); | ||
}.bind(this)); | ||
this.graphWeightSum /= 2.0; | ||
} | ||
CommunityStructure.prototype.setUpLink = function (node_index, neighbor_index, weight) { | ||
this.weights[node_index] += weight; | ||
var /** @type {ModEdge} */ me = new ModEdge(node_index, neighbor_index, weight); | ||
this.topology[node_index].push(me); | ||
var /** @type {Community} **/ adjCom = this.nodeCommunities[neighbor_index]; | ||
this.nodeConnectionsWeight[node_index].set(adjCom, weight); | ||
this.nodeConnectionsCount[node_index].set(adjCom, 1); | ||
this.nodeCommunities[node_index].connectionsWeight.set(adjCom, weight); | ||
this.nodeCommunities[node_index].connectionsCount.set(adjCom, 1); | ||
this.nodeConnectionsWeight[neighbor_index].set(this.nodeCommunities[node_index], weight); | ||
this.nodeConnectionsCount[neighbor_index].set(this.nodeCommunities[node_index], 1); | ||
this.nodeCommunities[neighbor_index].connectionsWeight.set(this.nodeCommunities[node_index], weight); | ||
this.nodeCommunities[neighbor_index].connectionsCount.set(this.nodeCommunities[node_index], 1); | ||
this.graphWeightSum += weight; | ||
}; | ||
/** | ||
* @param {Number} node | ||
* @param {Community} to | ||
*/ | ||
CommunityStructure.prototype.addNodeTo = function (node, to) { | ||
to.add(node); | ||
this.nodeCommunities[node] = to; | ||
var nodeTopology = this.topology[node]; | ||
for (var topologyKey in nodeTopology) { | ||
//noinspection JSUnfilteredForInLoop | ||
var /** @type {ModEdge} */ e = nodeTopology[topologyKey]; | ||
var neighbor = e.target; | ||
//Remove Node Connection to this community | ||
var neighEdgesTo = this.nodeConnectionsWeight[neighbor].get(to); | ||
if (neighEdgesTo === undefined) { | ||
this.nodeConnectionsWeight[neighbor].set(to, e.weight); | ||
} else { | ||
this.nodeConnectionsWeight[neighbor].set(to, neighEdgesTo + e.weight); | ||
} | ||
var neighCountEdgesTo = this.nodeConnectionsCount[neighbor].get(to); | ||
if (neighCountEdgesTo === undefined) { | ||
this.nodeConnectionsCount[neighbor].set(to, 1); | ||
} else { | ||
this.nodeConnectionsCount[neighbor].set(to, neighCountEdgesTo + 1); | ||
} | ||
var /** @type {Community} */ adjCom = this.nodeCommunities[neighbor]; | ||
var wEdgesto = adjCom.connectionsWeight.get(to); | ||
if (wEdgesto === undefined) { | ||
adjCom.connectionsWeight.set(to, e.weight); | ||
} else { | ||
adjCom.connectionsWeight.set(to, wEdgesto + e.weight); | ||
} | ||
var cEdgesto = adjCom.connectionsCount.get(to); | ||
if (cEdgesto === undefined) { | ||
adjCom.connectionsCount.set(to, 1); | ||
} else { | ||
adjCom.connectionsCount.set(to, cEdgesto + 1); | ||
} | ||
var nodeEdgesTo = this.nodeConnectionsWeight[node].get(adjCom); | ||
if (nodeEdgesTo === undefined) { | ||
this.nodeConnectionsWeight[node].set(adjCom, e.weight); | ||
} else { | ||
this.nodeConnectionsWeight[node].set(adjCom, nodeEdgesTo + e.weight); | ||
} | ||
var nodeCountEdgesTo = this.nodeConnectionsCount[node].get(adjCom); | ||
if (nodeCountEdgesTo === undefined) { | ||
this.nodeConnectionsCount[node].set(adjCom, 1); | ||
} else { | ||
this.nodeConnectionsCount[node].set(adjCom, nodeCountEdgesTo + 1); | ||
} | ||
if (to != adjCom) { | ||
var comEdgesto = to.connectionsWeight.get(adjCom); | ||
if (comEdgesto === undefined) { | ||
to.connectionsWeight.set(adjCom, e.weight); | ||
} else { | ||
to.connectionsWeight.set(adjCom, comEdgesto + e.weight); | ||
} | ||
var comCountEdgesto = to.connectionsCount.get(adjCom); | ||
if (comCountEdgesto === undefined) { | ||
to.connectionsCount.set(adjCom, 1); | ||
} else { | ||
to.connectionsCount.set(adjCom, comCountEdgesto + 1); | ||
} | ||
} | ||
} | ||
}; | ||
/** | ||
* @param {Number} node | ||
* @param {Community} source | ||
*/ | ||
CommunityStructure.prototype.removeNodeFrom = function (node, source) { | ||
var community = this.nodeCommunities[node]; | ||
var nodeTopology = this.topology[node]; | ||
for (var topologyKey in nodeTopology) { | ||
//noinspection JSUnfilteredForInLoop | ||
var /** @type {ModEdge} */ e = nodeTopology[topologyKey]; | ||
var neighbor = e.target; | ||
//Remove Node Connection to this community | ||
var edgesTo = this.nodeConnectionsWeight[neighbor].get(community); | ||
var countEdgesTo = this.nodeConnectionsCount[neighbor].get(community); | ||
if ((countEdgesTo - 1) == 0) { | ||
this.nodeConnectionsWeight[neighbor].delete(community); | ||
this.nodeConnectionsCount[neighbor].delete(community); | ||
} else { | ||
this.nodeConnectionsWeight[neighbor].set(community, edgesTo - e.weight); | ||
this.nodeConnectionsCount[neighbor].set(community, countEdgesTo - 1); | ||
} | ||
//Remove Adjacency Community's connection to this community | ||
var adjCom = this.nodeCommunities[neighbor]; | ||
var oEdgesto = adjCom.connectionsWeight.get(community); | ||
var oCountEdgesto = adjCom.connectionsCount.get(community); | ||
if ((oCountEdgesto - 1) == 0) { | ||
adjCom.connectionsWeight.delete(community); | ||
adjCom.connectionsCount.delete(community); | ||
} else { | ||
adjCom.connectionsWeight.set(community, oEdgesto - e.weight); | ||
adjCom.connectionsCount.set(community, oCountEdgesto - 1); | ||
} | ||
if (node == neighbor) { | ||
continue; | ||
} | ||
if (adjCom != community) { | ||
var comEdgesto = community.connectionsWeight.get(adjCom); | ||
var comCountEdgesto = community.connectionsCount.get(adjCom); | ||
if (comCountEdgesto - 1 == 0) { | ||
community.connectionsWeight.delete(adjCom); | ||
community.connectionsCount.delete(adjCom); | ||
} else { | ||
community.connectionsWeight.set(adjCom, comEdgesto - e.weight); | ||
community.connectionsCount.set(adjCom, comCountEdgesto - 1); | ||
} | ||
} | ||
var nodeEdgesTo = this.nodeConnectionsWeight[node].get(adjCom); | ||
var nodeCountEdgesTo = this.nodeConnectionsCount[node].get(adjCom); | ||
if ((nodeCountEdgesTo - 1) == 0) { | ||
this.nodeConnectionsWeight[node].delete(adjCom); | ||
this.nodeConnectionsCount[node].delete(adjCom); | ||
} else { | ||
this.nodeConnectionsWeight[node].set(adjCom, nodeEdgesTo - e.weight); | ||
this.nodeConnectionsCount[node].set(adjCom, nodeCountEdgesTo - 1); | ||
} | ||
} | ||
source.remove(node); | ||
}; | ||
/** | ||
* @param {Number} node | ||
* @param {Community} to | ||
*/ | ||
CommunityStructure.prototype.moveNodeTo = function (node, to) { | ||
var source = this.nodeCommunities[node]; | ||
this.removeNodeFrom(node, source); | ||
this.addNodeTo(node, to); | ||
}; | ||
CommunityStructure.prototype.zoomOut = function () { | ||
var realCommunities = this.communities.reduce(function (arr, value) { | ||
arr.push(value); | ||
return arr; | ||
}, []); | ||
var M = realCommunities.length; // size | ||
var /** @type Array.< Array.<ModEdge> > */ newTopology = new Array(M); | ||
var index = 0; | ||
this.nodeCommunities = new Array(M); | ||
this.nodeConnectionsWeight = new Array(M); | ||
this.nodeConnectionsCount = new Array(M); | ||
var /** @type Map.<Number, Community>*/ newInvMap = new Map(); | ||
realCommunities.forEach(function (com) { | ||
var weightSum = 0; | ||
this.nodeConnectionsWeight[index] = new Map(); | ||
this.nodeConnectionsCount[index] = new Map(); | ||
newTopology[index] = []; | ||
this.nodeCommunities[index] = new Community(com); | ||
//var iter = com.connectionsWeight.keySet(); | ||
var hidden = new Community(this.structure); | ||
com.nodes.forEach(function (nodeInt) { | ||
var oldHidden = this.invMap.get(nodeInt); | ||
oldHidden.nodes.forEach(hidden.nodes.add.bind(hidden.nodes)); | ||
}, this); | ||
newInvMap.set(index, hidden); | ||
com.connectionsWeight.forEach(function (weight, adjCom) { | ||
var target = realCommunities.indexOf(adjCom); | ||
if (!~target) return; | ||
if (target == index) { | ||
weightSum += 2. * weight; | ||
} else { | ||
weightSum += weight; | ||
} | ||
var e = new ModEdge(index, target, weight); | ||
newTopology[index].push(e); | ||
}, this); | ||
this.weights[index] = weightSum; | ||
this.nodeCommunities[index].seed(index); | ||
index++; | ||
}.bind(this)); | ||
this.communities = []; | ||
for (var i = 0; i < M; i++) { | ||
var com = this.nodeCommunities[i]; | ||
this.communities.push(com); | ||
for (var ei in newTopology[i]) { | ||
//noinspection JSUnfilteredForInLoop | ||
var e = newTopology[i][ei]; | ||
this.nodeConnectionsWeight[i].set(this.nodeCommunities[e.target], e.weight); | ||
this.nodeConnectionsCount[i].set(this.nodeCommunities[e.target], 1); | ||
com.connectionsWeight.set(this.nodeCommunities[e.target], e.weight); | ||
com.connectionsCount.set(this.nodeCommunities[e.target], 1); | ||
} | ||
} | ||
this.N = M; | ||
this.topology = newTopology; | ||
this.invMap = newInvMap; | ||
}; | ||
module.exports = CommunityStructure; | ||
/***/ }), | ||
/* 4 */ | ||
/***/ (function(module, exports) { | ||
/** | ||
* @param {CommunityStructure|Community} com | ||
* @constructor | ||
*/ | ||
function Community(com) { | ||
/** @type {CommunityStructure} */ | ||
this.structure = com.structure ? com.structure : com; | ||
/** @type {Map.<Community, Number>} */ | ||
this.connectionsWeight = new Map(); | ||
/** @type {Map.<Community, Number>} */ | ||
this.connectionsCount = new Map(); | ||
/** @type {Set.<Number>} */ | ||
this.nodes = new Set; | ||
this.weightSum = 0; | ||
} | ||
/** | ||
* @public | ||
* @returns {Number} | ||
*/ | ||
Community.prototype.size = function() { | ||
return this.nodes.size; | ||
}; | ||
/** | ||
* @param {Number} node | ||
*/ | ||
Community.prototype.seed = function(node) { | ||
this.nodes.add(node); | ||
this.weightSum += this.structure.weights[node]; | ||
}; | ||
/** | ||
* @param {Number} nodeId | ||
* @returns {boolean} | ||
*/ | ||
Community.prototype.add = function(nodeId) { | ||
this.nodes.add(nodeId); | ||
this.weightSum += this.structure.weights[nodeId]; | ||
return true; | ||
}; | ||
/** | ||
* @param {Number} node | ||
* @returns {boolean} | ||
*/ | ||
Community.prototype.remove = function(node) { | ||
var result = this.nodes.delete(node); | ||
this.weightSum -= this.structure.weights[node]; | ||
if (!this.nodes.size) { | ||
var index = this.structure.communities.indexOf(this); | ||
delete this.structure.communities[index]; | ||
} | ||
return result; | ||
}; | ||
module.exports = Community; | ||
/***/ }), | ||
/* 5 */ | ||
/***/ (function(module, exports) { | ||
/** | ||
* | ||
* @param s | ||
* @param t | ||
* @param w | ||
* @constructor | ||
*/ | ||
function ModEdge(s, t, w) { | ||
/** @type {Number} */ | ||
this.source = s; | ||
/** @type {Number} */ | ||
this.target = t; | ||
/** @type {Number} */ | ||
this.weight = w; | ||
} | ||
module.exports = ModEdge; | ||
/***/ }), | ||
/* 6 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
module.exports.degree = __webpack_require__(7); | ||
module.exports.betweenness = __webpack_require__(8); | ||
/***/ }), | ||
/* 7 */ | ||
/***/ (function(module, exports) { | ||
module.exports = degree; | ||
/** | ||
* Calculates graph nodes degree centrality (in/out or both). | ||
* | ||
* @see http://en.wikipedia.org/wiki/Centrality#Degree_centrality | ||
* | ||
* @param {ngraph.graph} graph object for which we are calculating centrality. | ||
* @param {string} [kind=both] What kind of degree centrality needs to be calculated: | ||
* 'in' - calculate in-degree centrality | ||
* 'out' - calculate out-degree centrality | ||
* 'inout' - (default) generic degree centrality is calculated | ||
*/ | ||
function degree(graph, kind) { | ||
var getNodeDegree, | ||
sortedDegrees = [], | ||
result = Object.create(null), | ||
nodeDegree; | ||
kind = (kind || 'both').toLowerCase(); | ||
if (kind === 'both' || kind === 'inout') { | ||
getNodeDegree = inoutDegreeCalculator; | ||
} else if (kind === 'in') { | ||
getNodeDegree = inDegreeCalculator; | ||
} else if (kind === 'out') { | ||
getNodeDegree = outDegreeCalculator; | ||
} else { | ||
throw new Error('Expected centrality degree kind is: in, out or both'); | ||
} | ||
graph.forEachNode(calculateNodeDegree); | ||
return result; | ||
function calculateNodeDegree(node) { | ||
var links = graph.getLinks(node.id); | ||
result[node.id] = getNodeDegree(links, node.id); | ||
} | ||
} | ||
function inDegreeCalculator(links, nodeId) { | ||
var total = 0; | ||
if (!links) return total; | ||
for (var i = 0; i < links.length; i += 1) { | ||
total += (links[i].toId === nodeId) ? 1 : 0; | ||
} | ||
return total; | ||
} | ||
function outDegreeCalculator(links, nodeId) { | ||
var total = 0; | ||
if (!links) return total; | ||
for (var i = 0; i < links.length; i += 1) { | ||
total += (links[i].fromId === nodeId) ? 1 : 0; | ||
} | ||
return total; | ||
} | ||
function inoutDegreeCalculator(links) { | ||
if (!links) return 0; | ||
return links.length; | ||
} | ||
/***/ }), | ||
/* 8 */ | ||
/***/ (function(module, exports) { | ||
module.exports = betweennes; | ||
@@ -1092,3 +117,2 @@ | ||
var v = Q.shift(); | ||
var dedup = Object.create(null); | ||
S.push(v); | ||
@@ -1132,11 +156,180 @@ graph.forEachLinkedNode(v, toId, oriented); | ||
/***/ }), | ||
/* 9 */ | ||
/***/ (function(module, exports) { | ||
module.exports = __WEBPACK_EXTERNAL_MODULE_9__; | ||
/***/ "./node_modules/ngraph.centrality/src/degree.js": | ||
/*!******************************************************!*\ | ||
!*** ./node_modules/ngraph.centrality/src/degree.js ***! | ||
\******************************************************/ | ||
/***/ ((module) => { | ||
module.exports = degree; | ||
/** | ||
* Calculates graph nodes degree centrality (in/out or both). | ||
* | ||
* @see http://en.wikipedia.org/wiki/Centrality#Degree_centrality | ||
* | ||
* @param {ngraph.graph} graph object for which we are calculating centrality. | ||
* @param {string} [kind=both] What kind of degree centrality needs to be calculated: | ||
* 'in' - calculate in-degree centrality | ||
* 'out' - calculate out-degree centrality | ||
* 'inout' - (default) generic degree centrality is calculated | ||
*/ | ||
function degree(graph, kind) { | ||
var getNodeDegree; | ||
var result = Object.create(null); | ||
kind = (kind || 'both').toLowerCase(); | ||
if (kind === 'both' || kind === 'inout') { | ||
getNodeDegree = inoutDegreeCalculator; | ||
} else if (kind === 'in') { | ||
getNodeDegree = inDegreeCalculator; | ||
} else if (kind === 'out') { | ||
getNodeDegree = outDegreeCalculator; | ||
} else { | ||
throw new Error('Expected centrality degree kind is: in, out or both'); | ||
} | ||
graph.forEachNode(calculateNodeDegree); | ||
return result; | ||
function calculateNodeDegree(node) { | ||
var links = graph.getLinks(node.id); | ||
result[node.id] = getNodeDegree(links, node.id); | ||
} | ||
} | ||
function inDegreeCalculator(links, nodeId) { | ||
var total = 0; | ||
if (!links) return total; | ||
for (var i = 0; i < links.length; i += 1) { | ||
total += (links[i].toId === nodeId) ? 1 : 0; | ||
} | ||
return total; | ||
} | ||
function outDegreeCalculator(links, nodeId) { | ||
var total = 0; | ||
if (!links) return total; | ||
for (var i = 0; i < links.length; i += 1) { | ||
total += (links[i].fromId === nodeId) ? 1 : 0; | ||
} | ||
return total; | ||
} | ||
function inoutDegreeCalculator(links) { | ||
if (!links) return 0; | ||
return links.length; | ||
} | ||
/***/ }), | ||
/* 10 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
/***/ "./node_modules/ngraph.events/index.js": | ||
/*!*********************************************!*\ | ||
!*** ./node_modules/ngraph.events/index.js ***! | ||
\*********************************************/ | ||
/***/ ((module) => { | ||
module.exports = function(subject) { | ||
validateSubject(subject); | ||
var eventsStorage = createEventsStorage(subject); | ||
subject.on = eventsStorage.on; | ||
subject.off = eventsStorage.off; | ||
subject.fire = eventsStorage.fire; | ||
return subject; | ||
}; | ||
function createEventsStorage(subject) { | ||
// Store all event listeners to this hash. Key is event name, value is array | ||
// of callback records. | ||
// | ||
// A callback record consists of callback function and its optional context: | ||
// { 'eventName' => [{callback: function, ctx: object}] } | ||
var registeredEvents = Object.create(null); | ||
return { | ||
on: function (eventName, callback, ctx) { | ||
if (typeof callback !== 'function') { | ||
throw new Error('callback is expected to be a function'); | ||
} | ||
var handlers = registeredEvents[eventName]; | ||
if (!handlers) { | ||
handlers = registeredEvents[eventName] = []; | ||
} | ||
handlers.push({callback: callback, ctx: ctx}); | ||
return subject; | ||
}, | ||
off: function (eventName, callback) { | ||
var wantToRemoveAll = (typeof eventName === 'undefined'); | ||
if (wantToRemoveAll) { | ||
// Killing old events storage should be enough in this case: | ||
registeredEvents = Object.create(null); | ||
return subject; | ||
} | ||
if (registeredEvents[eventName]) { | ||
var deleteAllCallbacksForEvent = (typeof callback !== 'function'); | ||
if (deleteAllCallbacksForEvent) { | ||
delete registeredEvents[eventName]; | ||
} else { | ||
var callbacks = registeredEvents[eventName]; | ||
for (var i = 0; i < callbacks.length; ++i) { | ||
if (callbacks[i].callback === callback) { | ||
callbacks.splice(i, 1); | ||
} | ||
} | ||
} | ||
} | ||
return subject; | ||
}, | ||
fire: function (eventName) { | ||
var callbacks = registeredEvents[eventName]; | ||
if (!callbacks) { | ||
return subject; | ||
} | ||
var fireArguments; | ||
if (arguments.length > 1) { | ||
fireArguments = Array.prototype.splice.call(arguments, 1); | ||
} | ||
for(var i = 0; i < callbacks.length; ++i) { | ||
var callbackInfo = callbacks[i]; | ||
callbackInfo.callback.apply(callbackInfo.ctx, fireArguments); | ||
} | ||
return subject; | ||
} | ||
}; | ||
} | ||
function validateSubject(subject) { | ||
if (!subject) { | ||
throw new Error('Eventify cannot use falsy object as events subject'); | ||
} | ||
var reservedWords = ['on', 'fire', 'off']; | ||
for (var i = 0; i < reservedWords.length; ++i) { | ||
if (subject.hasOwnProperty(reservedWords[i])) { | ||
throw new Error("Subject cannot be eventified, since it already has property '" + reservedWords[i] + "'"); | ||
} | ||
} | ||
} | ||
/***/ }), | ||
/***/ "./node_modules/ngraph.graph/index.js": | ||
/*!********************************************!*\ | ||
!*** ./node_modules/ngraph.graph/index.js ***! | ||
\********************************************/ | ||
/***/ ((module, __unused_webpack_exports, __webpack_require__) => { | ||
/** | ||
@@ -1155,3 +348,3 @@ * @fileOverview Contains definition of the core graph object. | ||
var eventify = __webpack_require__(11); | ||
var eventify = __webpack_require__(/*! ngraph.events */ "./node_modules/ngraph.events/index.js"); | ||
@@ -1723,97 +916,936 @@ /** | ||
/***/ }), | ||
/* 11 */ | ||
/***/ (function(module, exports) { | ||
module.exports = function(subject) { | ||
validateSubject(subject); | ||
/***/ "./node_modules/ngraph.modularity/Community.js": | ||
/*!*****************************************************!*\ | ||
!*** ./node_modules/ngraph.modularity/Community.js ***! | ||
\*****************************************************/ | ||
/***/ ((module) => { | ||
var eventsStorage = createEventsStorage(subject); | ||
subject.on = eventsStorage.on; | ||
subject.off = eventsStorage.off; | ||
subject.fire = eventsStorage.fire; | ||
return subject; | ||
/** | ||
* @param {CommunityStructure|Community} com | ||
* @constructor | ||
*/ | ||
function Community(com) { | ||
/** @type {CommunityStructure} */ | ||
this.structure = com.structure ? com.structure : com; | ||
/** @type {Map.<Community, Number>} */ | ||
this.connectionsWeight = new Map(); | ||
/** @type {Map.<Community, Number>} */ | ||
this.connectionsCount = new Map(); | ||
/** @type {Set.<Number>} */ | ||
this.nodes = new Set; | ||
this.weightSum = 0; | ||
} | ||
/** | ||
* @public | ||
* @returns {Number} | ||
*/ | ||
Community.prototype.size = function() { | ||
return this.nodes.size; | ||
}; | ||
function createEventsStorage(subject) { | ||
// Store all event listeners to this hash. Key is event name, value is array | ||
// of callback records. | ||
// | ||
// A callback record consists of callback function and its optional context: | ||
// { 'eventName' => [{callback: function, ctx: object}] } | ||
var registeredEvents = Object.create(null); | ||
return { | ||
on: function (eventName, callback, ctx) { | ||
if (typeof callback !== 'function') { | ||
throw new Error('callback is expected to be a function'); | ||
} | ||
var handlers = registeredEvents[eventName]; | ||
if (!handlers) { | ||
handlers = registeredEvents[eventName] = []; | ||
} | ||
handlers.push({callback: callback, ctx: ctx}); | ||
/** | ||
* @param {Number} node | ||
*/ | ||
Community.prototype.seed = function(node) { | ||
return subject; | ||
}, | ||
this.nodes.add(node); | ||
this.weightSum += this.structure.weights[node]; | ||
off: function (eventName, callback) { | ||
var wantToRemoveAll = (typeof eventName === 'undefined'); | ||
if (wantToRemoveAll) { | ||
// Killing old events storage should be enough in this case: | ||
registeredEvents = Object.create(null); | ||
return subject; | ||
} | ||
}; | ||
if (registeredEvents[eventName]) { | ||
var deleteAllCallbacksForEvent = (typeof callback !== 'function'); | ||
if (deleteAllCallbacksForEvent) { | ||
delete registeredEvents[eventName]; | ||
/** | ||
* @param {Number} nodeId | ||
* @returns {boolean} | ||
*/ | ||
Community.prototype.add = function(nodeId) { | ||
this.nodes.add(nodeId); | ||
this.weightSum += this.structure.weights[nodeId]; | ||
return true; | ||
}; | ||
/** | ||
* @param {Number} node | ||
* @returns {boolean} | ||
*/ | ||
Community.prototype.remove = function(node) { | ||
var result = this.nodes.delete(node); | ||
this.weightSum -= this.structure.weights[node]; | ||
if (!this.nodes.size) { | ||
var index = this.structure.communities.indexOf(this); | ||
delete this.structure.communities[index]; | ||
} | ||
return result; | ||
}; | ||
module.exports = Community; | ||
/***/ }), | ||
/***/ "./node_modules/ngraph.modularity/CommunityStructure.js": | ||
/*!**************************************************************!*\ | ||
!*** ./node_modules/ngraph.modularity/CommunityStructure.js ***! | ||
\**************************************************************/ | ||
/***/ ((module, __unused_webpack_exports, __webpack_require__) => { | ||
"use strict"; | ||
var Community = __webpack_require__(/*! ./Community */ "./node_modules/ngraph.modularity/Community.js") | ||
, ModEdge = __webpack_require__(/*! ./ModEdge */ "./node_modules/ngraph.modularity/ModEdge.js") | ||
; | ||
/** | ||
* | ||
* @param {IGraph} graph | ||
* @param useWeight | ||
* @param {CommunityStructure} structure | ||
* @constructor | ||
*/ | ||
function CommunityStructure(graph, useWeight) { | ||
//this.graph = graph; | ||
this.N = graph.getNodesCount(); | ||
this.graphWeightSum = 0; | ||
this.structure = this; | ||
/** @type {Map.<Number, Community>} */ | ||
this.invMap = new Map(); | ||
/** @type {Array.< Map.<Community, Number> >} */ | ||
this.nodeConnectionsWeight = new Array(this.N); | ||
/** @type {Array.< Map.<Community, Number> >} */ | ||
this.nodeConnectionsCount = new Array(this.N); | ||
/** @type {Array.<Community>} */ | ||
this.nodeCommunities = new Array(this.N); | ||
/** @type {Map.<Node, Number>} */ | ||
this.map = new Map(); | ||
/** @type {Array.< Array.<ModEdge> >} */ | ||
this.topology = new Array(this.N); | ||
for (var i = 0; i < this.N; i++) this.topology[i] = []; | ||
/** @type {Array.<Community>} */ | ||
this.communities = []; | ||
/**@type {Array.<Number>} */ | ||
this.weights = new Array(this.N); | ||
var index = 0; | ||
graph.forEachNode(function (node) { | ||
this.map.set(node.id, index); | ||
this.nodeCommunities[index] = new Community(this); | ||
this.nodeConnectionsWeight[index] = new Map(); | ||
this.nodeConnectionsCount[index] = new Map(); | ||
this.weights[index] = 0; | ||
this.nodeCommunities[index].seed(index); | ||
var hidden = new Community(this); | ||
hidden.nodes.add(index); | ||
this.invMap.set(index, hidden); | ||
this.communities.push(this.nodeCommunities[index]); | ||
index++; | ||
}.bind(this)); | ||
graph.forEachLink(function (link) { | ||
var node_index = this.map.get(link.fromId) | ||
, neighbor_index = this.map.get(link.toId) | ||
, weight = 1 | ||
; | ||
if (node_index === neighbor_index) { | ||
return; | ||
} | ||
if (useWeight) { | ||
weight = link.data.weight; | ||
} | ||
this.setUpLink(node_index, neighbor_index, weight); | ||
this.setUpLink(neighbor_index, node_index, weight); | ||
}.bind(this)); | ||
this.graphWeightSum /= 2.0; | ||
} | ||
CommunityStructure.prototype.setUpLink = function (node_index, neighbor_index, weight) { | ||
this.weights[node_index] += weight; | ||
var /** @type {ModEdge} */ me = new ModEdge(node_index, neighbor_index, weight); | ||
this.topology[node_index].push(me); | ||
var /** @type {Community} **/ adjCom = this.nodeCommunities[neighbor_index]; | ||
this.nodeConnectionsWeight[node_index].set(adjCom, weight); | ||
this.nodeConnectionsCount[node_index].set(adjCom, 1); | ||
this.nodeCommunities[node_index].connectionsWeight.set(adjCom, weight); | ||
this.nodeCommunities[node_index].connectionsCount.set(adjCom, 1); | ||
this.nodeConnectionsWeight[neighbor_index].set(this.nodeCommunities[node_index], weight); | ||
this.nodeConnectionsCount[neighbor_index].set(this.nodeCommunities[node_index], 1); | ||
this.nodeCommunities[neighbor_index].connectionsWeight.set(this.nodeCommunities[node_index], weight); | ||
this.nodeCommunities[neighbor_index].connectionsCount.set(this.nodeCommunities[node_index], 1); | ||
this.graphWeightSum += weight; | ||
}; | ||
/** | ||
* @param {Number} node | ||
* @param {Community} to | ||
*/ | ||
CommunityStructure.prototype.addNodeTo = function (node, to) { | ||
to.add(node); | ||
this.nodeCommunities[node] = to; | ||
var nodeTopology = this.topology[node]; | ||
for (var topologyKey in nodeTopology) { | ||
//noinspection JSUnfilteredForInLoop | ||
var /** @type {ModEdge} */ e = nodeTopology[topologyKey]; | ||
var neighbor = e.target; | ||
//Remove Node Connection to this community | ||
var neighEdgesTo = this.nodeConnectionsWeight[neighbor].get(to); | ||
if (neighEdgesTo === undefined) { | ||
this.nodeConnectionsWeight[neighbor].set(to, e.weight); | ||
} else { | ||
var callbacks = registeredEvents[eventName]; | ||
for (var i = 0; i < callbacks.length; ++i) { | ||
if (callbacks[i].callback === callback) { | ||
callbacks.splice(i, 1); | ||
this.nodeConnectionsWeight[neighbor].set(to, neighEdgesTo + e.weight); | ||
} | ||
var neighCountEdgesTo = this.nodeConnectionsCount[neighbor].get(to); | ||
if (neighCountEdgesTo === undefined) { | ||
this.nodeConnectionsCount[neighbor].set(to, 1); | ||
} else { | ||
this.nodeConnectionsCount[neighbor].set(to, neighCountEdgesTo + 1); | ||
} | ||
var /** @type {Community} */ adjCom = this.nodeCommunities[neighbor]; | ||
var wEdgesto = adjCom.connectionsWeight.get(to); | ||
if (wEdgesto === undefined) { | ||
adjCom.connectionsWeight.set(to, e.weight); | ||
} else { | ||
adjCom.connectionsWeight.set(to, wEdgesto + e.weight); | ||
} | ||
var cEdgesto = adjCom.connectionsCount.get(to); | ||
if (cEdgesto === undefined) { | ||
adjCom.connectionsCount.set(to, 1); | ||
} else { | ||
adjCom.connectionsCount.set(to, cEdgesto + 1); | ||
} | ||
var nodeEdgesTo = this.nodeConnectionsWeight[node].get(adjCom); | ||
if (nodeEdgesTo === undefined) { | ||
this.nodeConnectionsWeight[node].set(adjCom, e.weight); | ||
} else { | ||
this.nodeConnectionsWeight[node].set(adjCom, nodeEdgesTo + e.weight); | ||
} | ||
var nodeCountEdgesTo = this.nodeConnectionsCount[node].get(adjCom); | ||
if (nodeCountEdgesTo === undefined) { | ||
this.nodeConnectionsCount[node].set(adjCom, 1); | ||
} else { | ||
this.nodeConnectionsCount[node].set(adjCom, nodeCountEdgesTo + 1); | ||
} | ||
if (to != adjCom) { | ||
var comEdgesto = to.connectionsWeight.get(adjCom); | ||
if (comEdgesto === undefined) { | ||
to.connectionsWeight.set(adjCom, e.weight); | ||
} else { | ||
to.connectionsWeight.set(adjCom, comEdgesto + e.weight); | ||
} | ||
} | ||
var comCountEdgesto = to.connectionsCount.get(adjCom); | ||
if (comCountEdgesto === undefined) { | ||
to.connectionsCount.set(adjCom, 1); | ||
} else { | ||
to.connectionsCount.set(adjCom, comCountEdgesto + 1); | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
return subject; | ||
}, | ||
/** | ||
* @param {Number} node | ||
* @param {Community} source | ||
*/ | ||
CommunityStructure.prototype.removeNodeFrom = function (node, source) { | ||
fire: function (eventName) { | ||
var callbacks = registeredEvents[eventName]; | ||
if (!callbacks) { | ||
return subject; | ||
} | ||
var community = this.nodeCommunities[node]; | ||
var fireArguments; | ||
if (arguments.length > 1) { | ||
fireArguments = Array.prototype.splice.call(arguments, 1); | ||
} | ||
for(var i = 0; i < callbacks.length; ++i) { | ||
var callbackInfo = callbacks[i]; | ||
callbackInfo.callback.apply(callbackInfo.ctx, fireArguments); | ||
} | ||
return subject; | ||
var nodeTopology = this.topology[node]; | ||
for (var topologyKey in nodeTopology) { | ||
//noinspection JSUnfilteredForInLoop | ||
var /** @type {ModEdge} */ e = nodeTopology[topologyKey]; | ||
var neighbor = e.target; | ||
//Remove Node Connection to this community | ||
var edgesTo = this.nodeConnectionsWeight[neighbor].get(community); | ||
var countEdgesTo = this.nodeConnectionsCount[neighbor].get(community); | ||
if ((countEdgesTo - 1) == 0) { | ||
this.nodeConnectionsWeight[neighbor].delete(community); | ||
this.nodeConnectionsCount[neighbor].delete(community); | ||
} else { | ||
this.nodeConnectionsWeight[neighbor].set(community, edgesTo - e.weight); | ||
this.nodeConnectionsCount[neighbor].set(community, countEdgesTo - 1); | ||
} | ||
//Remove Adjacency Community's connection to this community | ||
var adjCom = this.nodeCommunities[neighbor]; | ||
var oEdgesto = adjCom.connectionsWeight.get(community); | ||
var oCountEdgesto = adjCom.connectionsCount.get(community); | ||
if ((oCountEdgesto - 1) == 0) { | ||
adjCom.connectionsWeight.delete(community); | ||
adjCom.connectionsCount.delete(community); | ||
} else { | ||
adjCom.connectionsWeight.set(community, oEdgesto - e.weight); | ||
adjCom.connectionsCount.set(community, oCountEdgesto - 1); | ||
} | ||
if (node == neighbor) { | ||
continue; | ||
} | ||
if (adjCom != community) { | ||
var comEdgesto = community.connectionsWeight.get(adjCom); | ||
var comCountEdgesto = community.connectionsCount.get(adjCom); | ||
if (comCountEdgesto - 1 == 0) { | ||
community.connectionsWeight.delete(adjCom); | ||
community.connectionsCount.delete(adjCom); | ||
} else { | ||
community.connectionsWeight.set(adjCom, comEdgesto - e.weight); | ||
community.connectionsCount.set(adjCom, comCountEdgesto - 1); | ||
} | ||
} | ||
var nodeEdgesTo = this.nodeConnectionsWeight[node].get(adjCom); | ||
var nodeCountEdgesTo = this.nodeConnectionsCount[node].get(adjCom); | ||
if ((nodeCountEdgesTo - 1) == 0) { | ||
this.nodeConnectionsWeight[node].delete(adjCom); | ||
this.nodeConnectionsCount[node].delete(adjCom); | ||
} else { | ||
this.nodeConnectionsWeight[node].set(adjCom, nodeEdgesTo - e.weight); | ||
this.nodeConnectionsCount[node].set(adjCom, nodeCountEdgesTo - 1); | ||
} | ||
} | ||
}; | ||
source.remove(node); | ||
}; | ||
/** | ||
* @param {Number} node | ||
* @param {Community} to | ||
*/ | ||
CommunityStructure.prototype.moveNodeTo = function (node, to) { | ||
var source = this.nodeCommunities[node]; | ||
this.removeNodeFrom(node, source); | ||
this.addNodeTo(node, to); | ||
}; | ||
CommunityStructure.prototype.zoomOut = function () { | ||
var realCommunities = this.communities.reduce(function (arr, value) { | ||
arr.push(value); | ||
return arr; | ||
}, []); | ||
var M = realCommunities.length; // size | ||
var /** @type Array.< Array.<ModEdge> > */ newTopology = new Array(M); | ||
var index = 0; | ||
this.nodeCommunities = new Array(M); | ||
this.nodeConnectionsWeight = new Array(M); | ||
this.nodeConnectionsCount = new Array(M); | ||
var /** @type Map.<Number, Community>*/ newInvMap = new Map(); | ||
realCommunities.forEach(function (com) { | ||
var weightSum = 0; | ||
this.nodeConnectionsWeight[index] = new Map(); | ||
this.nodeConnectionsCount[index] = new Map(); | ||
newTopology[index] = []; | ||
this.nodeCommunities[index] = new Community(com); | ||
//var iter = com.connectionsWeight.keySet(); | ||
var hidden = new Community(this.structure); | ||
com.nodes.forEach(function (nodeInt) { | ||
var oldHidden = this.invMap.get(nodeInt); | ||
oldHidden.nodes.forEach(hidden.nodes.add.bind(hidden.nodes)); | ||
}, this); | ||
newInvMap.set(index, hidden); | ||
com.connectionsWeight.forEach(function (weight, adjCom) { | ||
var target = realCommunities.indexOf(adjCom); | ||
if (!~target) return; | ||
if (target == index) { | ||
weightSum += 2. * weight; | ||
} else { | ||
weightSum += weight; | ||
} | ||
var e = new ModEdge(index, target, weight); | ||
newTopology[index].push(e); | ||
}, this); | ||
this.weights[index] = weightSum; | ||
this.nodeCommunities[index].seed(index); | ||
index++; | ||
}.bind(this)); | ||
this.communities = []; | ||
for (var i = 0; i < M; i++) { | ||
var com = this.nodeCommunities[i]; | ||
this.communities.push(com); | ||
for (var ei in newTopology[i]) { | ||
//noinspection JSUnfilteredForInLoop | ||
var e = newTopology[i][ei]; | ||
this.nodeConnectionsWeight[i].set(this.nodeCommunities[e.target], e.weight); | ||
this.nodeConnectionsCount[i].set(this.nodeCommunities[e.target], 1); | ||
com.connectionsWeight.set(this.nodeCommunities[e.target], e.weight); | ||
com.connectionsCount.set(this.nodeCommunities[e.target], 1); | ||
} | ||
} | ||
this.N = M; | ||
this.topology = newTopology; | ||
this.invMap = newInvMap; | ||
}; | ||
module.exports = CommunityStructure; | ||
/***/ }), | ||
/***/ "./node_modules/ngraph.modularity/ModEdge.js": | ||
/*!***************************************************!*\ | ||
!*** ./node_modules/ngraph.modularity/ModEdge.js ***! | ||
\***************************************************/ | ||
/***/ ((module) => { | ||
/** | ||
* | ||
* @param s | ||
* @param t | ||
* @param w | ||
* @constructor | ||
*/ | ||
function ModEdge(s, t, w) { | ||
/** @type {Number} */ | ||
this.source = s; | ||
/** @type {Number} */ | ||
this.target = t; | ||
/** @type {Number} */ | ||
this.weight = w; | ||
} | ||
function validateSubject(subject) { | ||
if (!subject) { | ||
throw new Error('Eventify cannot use falsy object as events subject'); | ||
} | ||
var reservedWords = ['on', 'fire', 'off']; | ||
for (var i = 0; i < reservedWords.length; ++i) { | ||
if (subject.hasOwnProperty(reservedWords[i])) { | ||
throw new Error("Subject cannot be eventified, since it already has property '" + reservedWords[i] + "'"); | ||
module.exports = ModEdge; | ||
/***/ }), | ||
/***/ "./node_modules/ngraph.modularity/Modularity.js": | ||
/*!******************************************************!*\ | ||
!*** ./node_modules/ngraph.modularity/Modularity.js ***! | ||
\******************************************************/ | ||
/***/ ((module, __unused_webpack_exports, __webpack_require__) => { | ||
/* | ||
Copyright 2008-2011 Gephi | ||
Authors : Patick J. McSweeney <pjmcswee@syr.edu>, Sebastien Heymann <seb@gephi.org> | ||
Website : http://www.gephi.org | ||
This file is part of Gephi. | ||
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. | ||
Copyright 2011 Gephi Consortium. All rights reserved. | ||
The contents of this file are subject to the terms of either the GNU | ||
General Public License Version 3 only ("GPL") or the Common | ||
Development and Distribution License("CDDL") (collectively, the | ||
"License"). You may not use this file except in compliance with the | ||
License. You can obtain a copy of the License at | ||
http://gephi.org/about/legal/license-notice/ | ||
or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the | ||
specific language governing permissions and limitations under the | ||
License. When distributing the software, include this License Header | ||
Notice in each file and include the License files at | ||
/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the | ||
License Header, with the fields enclosed by brackets [] replaced by | ||
your own identifying information: | ||
"Portions Copyrighted [year] [name of copyright owner]" | ||
If you wish your version of this file to be governed by only the CDDL | ||
or only the GPL Version 3, indicate your decision by adding | ||
"[Contributor] elects to include this software in this distribution | ||
under the [CDDL or GPL Version 3] license." If you do not indicate a | ||
single choice of license, a recipient has the option to distribute | ||
your version of this file under either the CDDL, the GPL Version 3 or | ||
to extend the choice of license to its licensees as provided above. | ||
However, if you add GPL Version 3 code and therefore, elected the GPL | ||
Version 3 license, then the option applies only if the new code is | ||
made subject to such option by the copyright holder. | ||
Contributor(s): Thomas Aynaud <taynaud@gmail.com> | ||
Portions Copyrighted 2011 Gephi Consortium. | ||
*/ | ||
var CommunityStructure = __webpack_require__(/*! ./CommunityStructure */ "./node_modules/ngraph.modularity/CommunityStructure.js") | ||
, centrality = __webpack_require__(/*! ngraph.centrality */ "./node_modules/ngraph.centrality/index.js") | ||
; | ||
/** | ||
* @constructor | ||
*/ | ||
function Modularity (resolution, useWeight) { | ||
this.isRandomized = false; | ||
this.useWeight = useWeight; | ||
this.resolution = resolution || 1.; | ||
/** | ||
* @type {CommunityStructure} | ||
*/ | ||
this.structure = null; | ||
} | ||
/** | ||
* @param {IGraph} graph | ||
*/ | ||
Modularity.prototype.execute = function (graph/*, AttributeModel attributeModel*/) { | ||
this.structure = new CommunityStructure(graph, this.useWeight); | ||
var comStructure = new Array(graph.getNodesCount()); | ||
var computedModularityMetrics = this.computeModularity( | ||
graph | ||
, this.structure | ||
, comStructure | ||
, this.resolution | ||
, this.isRandomized | ||
, this.useWeight | ||
); | ||
var result = {}; | ||
this.structure.map.forEach(function (i, node) { | ||
result[node] = comStructure[i]; | ||
}); | ||
return result; | ||
}; | ||
/** | ||
* | ||
* @param {IGraph} graph | ||
* @param {CommunityStructure} theStructure | ||
* @param {Array.<Number>} comStructure | ||
* @param {Number} currentResolution | ||
* @param {Boolean} randomized | ||
* @param {Boolean} weighted | ||
* @returns {Object.<String, Number>} | ||
*/ | ||
Modularity.prototype.computeModularity = function(graph, theStructure, comStructure, currentResolution, randomized, weighted) { | ||
function getRandomInt(min, max) { | ||
return Math.floor(Math.random() * (max - min)) + min; | ||
} | ||
} | ||
var totalWeight = theStructure.graphWeightSum; | ||
var nodeDegrees = theStructure.weights.slice(); | ||
var /** @type {Object.<String, Number>} */ results = Object.create(null); | ||
var someChange = true; | ||
while (someChange) { | ||
someChange = false; | ||
var localChange = true; | ||
while (localChange) { | ||
localChange = false; | ||
var start = 0; | ||
if (randomized) { | ||
//start = Math.abs(rand.nextInt()) % theStructure.N; | ||
start = getRandomInt(0,theStructure.N); | ||
} | ||
var step = 0; | ||
for (var i = start; step < theStructure.N; i = (i + 1) % theStructure.N) { | ||
step++; | ||
var bestCommunity = this.updateBestCommunity(theStructure, i, currentResolution); | ||
if ((theStructure.nodeCommunities[i] != bestCommunity) && (bestCommunity != null)) { | ||
theStructure.moveNodeTo(i, bestCommunity); | ||
localChange = true; | ||
} | ||
} | ||
someChange = localChange || someChange; | ||
} | ||
if (someChange) { | ||
theStructure.zoomOut(); | ||
} | ||
} | ||
this.fillComStructure(graph, theStructure, comStructure); | ||
/* | ||
//TODO: uncomment when finalQ will be implemented | ||
var degreeCount = this.fillDegreeCount(graph, theStructure, comStructure, nodeDegrees, weighted); | ||
var computedModularity = this._finalQ(comStructure, degreeCount, graph, theStructure, totalWeight, 1., weighted); | ||
var computedModularityResolution = this._finalQ(comStructure, degreeCount, graph, theStructure, totalWeight, currentResolution, weighted); | ||
results["modularity"] = computedModularity; | ||
results["modularityResolution"] = computedModularityResolution; | ||
*/ | ||
return results; | ||
}; | ||
/** | ||
* @param {CommunityStructure} theStructure | ||
* @param {Number} i | ||
* @param {Number} currentResolution | ||
* @returns {Community} | ||
*/ | ||
Modularity.prototype.updateBestCommunity = function(theStructure, i, currentResolution) { | ||
var best = this.q(i, theStructure.nodeCommunities[i], theStructure, currentResolution); | ||
var bestCommunity = theStructure.nodeCommunities[i]; | ||
//var /*Set<Community>*/ iter = theStructure.nodeConnectionsWeight[i].keySet(); | ||
theStructure.nodeConnectionsWeight[i].forEach(function (_$$val, com) { | ||
var qValue = this.q(i, com, theStructure, currentResolution); | ||
if (qValue > best) { | ||
best = qValue; | ||
bestCommunity = com; | ||
} | ||
}, this); | ||
return bestCommunity; | ||
}; | ||
/** | ||
* | ||
* @param {IGraph} graph | ||
* @param {CommunityStructure} theStructure | ||
* @param {Array.<Number>} comStructure | ||
* @returns {Array.<Number>} | ||
*/ | ||
Modularity.prototype.fillComStructure = function(graph, theStructure, comStructure) { | ||
var count = 0; | ||
theStructure.communities.forEach(function (com) { | ||
com.nodes.forEach(function (node) { | ||
var hidden = theStructure.invMap.get(node); | ||
hidden.nodes.forEach( function (nodeInt){ | ||
comStructure[nodeInt] = count; | ||
}); | ||
}); | ||
count++; | ||
}); | ||
return comStructure; | ||
}; | ||
/** | ||
* @param {IGraph} graph | ||
* @param {CommunityStructure} theStructure | ||
* @param {Array.<Number>} comStructure | ||
* @param {Array.<Number>} nodeDegrees | ||
* @param {Boolean} weighted | ||
* @returns {Array.<Number>} | ||
*/ | ||
Modularity.prototype.fillDegreeCount = function(graph, theStructure, comStructure, nodeDegrees, weighted) { | ||
var degreeCount = new Array(theStructure.communities.length); | ||
var degreeCentrality = centrality.degree(graph); | ||
graph.forEachNode(function(node){ | ||
var index = theStructure.map.get(node); | ||
if (weighted) { | ||
degreeCount[comStructure[index]] += nodeDegrees[index]; | ||
} else { | ||
degreeCount[comStructure[index]] += degreeCentrality[node.id]; | ||
} | ||
}); | ||
return degreeCount; | ||
}; | ||
/** | ||
* | ||
* @param {Array.<Number>} struct | ||
* @param {Array.<Number>} degrees | ||
* @param {IGraph} graph | ||
* @param {CommunityStructure} theStructure | ||
* @param {Number} totalWeight | ||
* @param {Number} usedResolution | ||
* @param {Boolean} weighted | ||
* @returns {Number} | ||
*/ | ||
Modularity.prototype._finalQ = function(struct, degrees, graph, theStructure, totalWeight, usedResolution, weighted) { | ||
//TODO: rewrite for wighted version of algorithm | ||
throw new Error("not implemented properly"); | ||
var res = 0; | ||
var internal = new Array(degrees.length); | ||
graph.forEachNode(function(n){ | ||
var n_index = theStructure.map.get(n); | ||
graph.forEachLinkedNode(n.id, function(neighbor){ | ||
if (n == neighbor) { | ||
return; | ||
} | ||
var neigh_index = theStructure.map.get(neighbor); | ||
if (struct[neigh_index] == struct[n_index]) { | ||
if (weighted) { | ||
//throw new Error("weighted aren't implemented"); | ||
//internal[struct[neigh_index]] += graph.getEdge(n, neighbor).getWeight(); | ||
} else { | ||
internal[struct[neigh_index]]++; | ||
} | ||
} | ||
}.bind(this), false); | ||
}.bind(this)); | ||
for (var i = 0; i < degrees.length; i++) { | ||
internal[i] /= 2.0; | ||
res += usedResolution * (internal[i] / totalWeight) - Math.pow(degrees[i] / (2 * totalWeight), 2);//HERE | ||
} | ||
return res; | ||
}; | ||
/** | ||
* | ||
* @param {Number} nodeId | ||
* @param {Community} community | ||
* @param {CommunityStructure} theStructure | ||
* @param {Number} currentResolution | ||
* @returns {Number} | ||
*/ | ||
Modularity.prototype.q = function(nodeId, community, theStructure, currentResolution) { | ||
var edgesToFloat = theStructure.nodeConnectionsWeight[nodeId].get(community); | ||
var edgesTo = 0; | ||
if (edgesToFloat != null) { | ||
edgesTo = edgesToFloat; | ||
} | ||
var weightSum = community.weightSum; | ||
var nodeWeight = theStructure.weights[nodeId]; | ||
var qValue = currentResolution * edgesTo - (nodeWeight * weightSum) / (2.0 * theStructure.graphWeightSum); | ||
if ((theStructure.nodeCommunities[nodeId] == community) && (theStructure.nodeCommunities[nodeId].size() > 1)) { | ||
qValue = currentResolution * edgesTo - (nodeWeight * (weightSum - nodeWeight)) / (2.0 * theStructure.graphWeightSum); | ||
} | ||
if ((theStructure.nodeCommunities[nodeId] == community) && (theStructure.nodeCommunities[nodeId].size() == 1)) { | ||
qValue = 0.; | ||
} | ||
return qValue; | ||
}; | ||
module.exports = Modularity; | ||
/***/ }), | ||
/***/ "./src/main.js": | ||
/*!*********************!*\ | ||
!*** ./src/main.js ***! | ||
\*********************/ | ||
/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { | ||
var Modularity = __webpack_require__(/*! ngraph.modularity/Modularity */ "./node_modules/ngraph.modularity/Modularity.js"); | ||
var echarts = __webpack_require__(/*! echarts/lib/echarts */ "echarts/lib/echarts"); | ||
var createNGraph = __webpack_require__(/*! ngraph.graph */ "./node_modules/ngraph.graph/index.js"); | ||
function createModularityVisual(chartType) { | ||
return function (ecModel, api) { | ||
var paletteScope = {}; | ||
ecModel.eachSeriesByType(chartType, function (seriesModel) { | ||
var modularityOpt = seriesModel.get('modularity'); | ||
if (modularityOpt) { | ||
var graph = seriesModel.getGraph(); | ||
var idIndexMap = {}; | ||
var ng = createNGraph(); | ||
graph.data.each(function (idx) { | ||
var node = graph.getNodeByIndex(idx); | ||
idIndexMap[node.id] = idx; | ||
ng.addNode(node.id); | ||
return node.id; | ||
}); | ||
graph.edgeData.each('value', function (val, idx) { | ||
var edge = graph.getEdgeByIndex(idx); | ||
ng.addLink(edge.node1.id, edge.node2.id); | ||
return { | ||
source: edge.node1.id, | ||
target: edge.node2.id, | ||
value: val | ||
}; | ||
}); | ||
var modularity = new Modularity(seriesModel.get('modularity.resolution') || 1); | ||
var result = modularity.execute(ng); | ||
var communities = {}; | ||
for (var id in result) { | ||
var comm = result[id]; | ||
communities[comm] = communities[comm] || 0; | ||
communities[comm]++; | ||
} | ||
var communitiesList = Object.keys(communities); | ||
if (seriesModel.get('modularity.sort')) { | ||
communitiesList.sort(function (a, b) { | ||
return b - a; | ||
}); | ||
} | ||
var colors = {}; | ||
communitiesList.forEach(function (comm) { | ||
colors[comm] = seriesModel.getColorFromPalette(comm, paletteScope); | ||
}); | ||
for (var id in result) { | ||
var comm = result[id]; | ||
var style = graph.data.ensureUniqueItemVisual(idIndexMap[id], 'style'); | ||
style.fill = colors[comm]; | ||
} | ||
graph.edgeData.each(function (idx) { | ||
var itemModel = graph.edgeData.getItemModel(idx); | ||
var edge = graph.getEdgeByIndex(idx); | ||
var color = itemModel.get('lineStyle.normal.color'); | ||
switch (color) { | ||
case 'source': | ||
color = edge.node1.getVisual('style').fill; | ||
break; | ||
case 'target': | ||
color = edge.node2.getVisual('style').fill; | ||
break; | ||
} | ||
if (color != null) { | ||
edge.data.ensureUniqueItemVisual(idx, 'style').stroke = color; | ||
} | ||
}); | ||
} | ||
}); | ||
}; | ||
} | ||
echarts.registerVisual(echarts.PRIORITY.VISUAL.CHART + 1, createModularityVisual('graph')); | ||
echarts.registerVisual(echarts.PRIORITY.VISUAL.CHART + 1, createModularityVisual('graphGL')); | ||
/***/ }), | ||
/***/ "echarts/lib/echarts": | ||
/*!**************************!*\ | ||
!*** external "echarts" ***! | ||
\**************************/ | ||
/***/ ((module) => { | ||
"use strict"; | ||
module.exports = __WEBPACK_EXTERNAL_MODULE_echarts_lib_echarts__; | ||
/***/ }) | ||
/******/ ]); | ||
}); | ||
/******/ }); | ||
/************************************************************************/ | ||
/******/ // The module cache | ||
/******/ var __webpack_module_cache__ = {}; | ||
/******/ | ||
/******/ // The require function | ||
/******/ function __webpack_require__(moduleId) { | ||
/******/ // Check if module is in cache | ||
/******/ if(__webpack_module_cache__[moduleId]) { | ||
/******/ return __webpack_module_cache__[moduleId].exports; | ||
/******/ } | ||
/******/ // Create a new module (and put it into the cache) | ||
/******/ var module = __webpack_module_cache__[moduleId] = { | ||
/******/ // no module.id needed | ||
/******/ // no module.loaded needed | ||
/******/ exports: {} | ||
/******/ }; | ||
/******/ | ||
/******/ // Execute the module function | ||
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); | ||
/******/ | ||
/******/ // Return the exports of the module | ||
/******/ return module.exports; | ||
/******/ } | ||
/******/ | ||
/************************************************************************/ | ||
/******/ // module exports must be returned from runtime so entry inlining is disabled | ||
/******/ // startup | ||
/******/ // Load entry module and return exports | ||
/******/ return __webpack_require__("./index.js"); | ||
/******/ })() | ||
; | ||
}); | ||
//# sourceMappingURL=echarts-graph-modularity.js.map |
@@ -1,1 +0,2 @@ | ||
!function(t,n){"object"==typeof exports&&"object"==typeof module?module.exports=n(require("echarts")):"function"==typeof define&&define.amd?define(["echarts"],n):"object"==typeof exports?exports["echarts-graph-modularity"]=n(require("echarts")):t["echarts-graph-modularity"]=n(t.echarts)}(this,function(t){return function(t){function n(o){if(e[o])return e[o].exports;var i=e[o]={i:o,l:!1,exports:{}};return t[o].call(i.exports,i,i.exports,n),i.l=!0,i.exports}var e={};return n.m=t,n.c=e,n.d=function(t,e,o){n.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:o})},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,n){return Object.prototype.hasOwnProperty.call(t,n)},n.p="",n(n.s=0)}([function(t,n,e){t.exports=e(1)},function(t,n,e){function o(t){return function(n,e){var o={};n.eachSeriesByType(t,function(t){if(t.get("modularity")){var n=t.getGraph(),e={},r=s();n.data.each(function(t){var o=n.getNodeByIndex(t);return e[o.id]=t,r.addNode(o.id),o.id}),n.edgeData.each("value",function(t,e){var o=n.getEdgeByIndex(e);return r.addLink(o.node1.id,o.node2.id),{source:o.node1.id,target:o.node2.id,value:t}});var u=new i(t.get("modularity.resolution")||1),c=u.execute(r);console.log(c);for(var h in c){var a=c[h];n.data.setItemVisual(e[h],"color",t.getColorFromPalette(a,o))}n.edgeData.each(function(t){var e=n.edgeData.getItemModel(t),o=n.getEdgeByIndex(t),i=e.get("lineStyle.normal.color");switch(i){case"source":i=o.node1.getVisual("color");break;case"target":i=o.node2.getVisual("color")}null!=i&&o.setVisual("color",i)})}})}}var i=e(2),r=e(9),s=e(10);r.registerVisual(r.PRIORITY.VISUAL.CHART+1,o("graph")),r.registerVisual(r.PRIORITY.VISUAL.CHART+1,o("graphGL"))},function(t,n,e){function o(t,n){this.isRandomized=!1,this.useWeight=n,this.resolution=t||1,this.structure=null}var i=e(3),r=e(6);o.prototype.execute=function(t){this.structure=new i(t,this.useWeight);var n=new Array(t.getNodesCount()),e=(this.computeModularity(t,this.structure,n,this.resolution,this.isRandomized,this.useWeight),{});return this.structure.map.forEach(function(t,o){e[o]=n[t]}),e},o.prototype.computeModularity=function(t,n,e,o,i,r){for(var s=(n.graphWeightSum,n.weights.slice(),Object.create(null)),u=!0;u;){u=!1;for(var c=!0;c;){c=!1;var h=0;i&&(h=function(t,n){return Math.floor(Math.random()*(n-t))+t}(0,n.N));for(var a=0,d=h;a<n.N;d=(d+1)%n.N){a++;var f=this.updateBestCommunity(n,d,o);n.nodeCommunities[d]!=f&&null!=f&&(n.moveNodeTo(d,f),c=!0)}u=c||u}u&&n.zoomOut()}return this.fillComStructure(t,n,e),s},o.prototype.updateBestCommunity=function(t,n,e){var o=this.q(n,t.nodeCommunities[n],t,e),i=t.nodeCommunities[n];return t.nodeConnectionsWeight[n].forEach(function(r,s){var u=this.q(n,s,t,e);u>o&&(o=u,i=s)},this),i},o.prototype.fillComStructure=function(t,n,e){var o=0;return n.communities.forEach(function(t){t.nodes.forEach(function(t){n.invMap.get(t).nodes.forEach(function(t){e[t]=o})}),o++}),e},o.prototype.fillDegreeCount=function(t,n,e,o,i){var s=new Array(n.communities.length),u=r.degree(t);return t.forEachNode(function(t){var r=n.map.get(t);s[e[r]]+=i?o[r]:u[t.id]}),s},o.prototype._finalQ=function(t,n,e,o,i,r,s){throw new Error("not implemented properly")},o.prototype.q=function(t,n,e,o){var i=e.nodeConnectionsWeight[t].get(n),r=0;null!=i&&(r=i);var s=n.weightSum,u=e.weights[t],c=o*r-u*s/(2*e.graphWeightSum);return e.nodeCommunities[t]==n&&e.nodeCommunities[t].size()>1&&(c=o*r-u*(s-u)/(2*e.graphWeightSum)),e.nodeCommunities[t]==n&&1==e.nodeCommunities[t].size()&&(c=0),c},t.exports=o},function(t,n,e){"use strict";function o(t,n){this.N=t.getNodesCount(),this.graphWeightSum=0,this.structure=this,this.invMap=new Map,this.nodeConnectionsWeight=new Array(this.N),this.nodeConnectionsCount=new Array(this.N),this.nodeCommunities=new Array(this.N),this.map=new Map,this.topology=new Array(this.N);for(var e=0;e<this.N;e++)this.topology[e]=[];this.communities=[],this.weights=new Array(this.N);var o=0;t.forEachNode(function(t){this.map.set(t.id,o),this.nodeCommunities[o]=new i(this),this.nodeConnectionsWeight[o]=new Map,this.nodeConnectionsCount[o]=new Map,this.weights[o]=0,this.nodeCommunities[o].seed(o);var n=new i(this);n.nodes.add(o),this.invMap.set(o,n),this.communities.push(this.nodeCommunities[o]),o++}.bind(this)),t.forEachLink(function(t){var e=this.map.get(t.fromId),o=this.map.get(t.toId),i=1;e!==o&&(n&&(i=t.data.weight),this.setUpLink(e,o,i),this.setUpLink(o,e,i))}.bind(this)),this.graphWeightSum/=2}var i=e(4),r=e(5);o.prototype.setUpLink=function(t,n,e){this.weights[t]+=e;var o=new r(t,n,e);this.topology[t].push(o);var i=this.nodeCommunities[n];this.nodeConnectionsWeight[t].set(i,e),this.nodeConnectionsCount[t].set(i,1),this.nodeCommunities[t].connectionsWeight.set(i,e),this.nodeCommunities[t].connectionsCount.set(i,1),this.nodeConnectionsWeight[n].set(this.nodeCommunities[t],e),this.nodeConnectionsCount[n].set(this.nodeCommunities[t],1),this.nodeCommunities[n].connectionsWeight.set(this.nodeCommunities[t],e),this.nodeCommunities[n].connectionsCount.set(this.nodeCommunities[t],1),this.graphWeightSum+=e},o.prototype.addNodeTo=function(t,n){n.add(t),this.nodeCommunities[t]=n;var e=this.topology[t];for(var o in e){var i=e[o],r=i.target,s=this.nodeConnectionsWeight[r].get(n);void 0===s?this.nodeConnectionsWeight[r].set(n,i.weight):this.nodeConnectionsWeight[r].set(n,s+i.weight);var u=this.nodeConnectionsCount[r].get(n);void 0===u?this.nodeConnectionsCount[r].set(n,1):this.nodeConnectionsCount[r].set(n,u+1);var c=this.nodeCommunities[r],h=c.connectionsWeight.get(n);void 0===h?c.connectionsWeight.set(n,i.weight):c.connectionsWeight.set(n,h+i.weight);var a=c.connectionsCount.get(n);void 0===a?c.connectionsCount.set(n,1):c.connectionsCount.set(n,a+1);var d=this.nodeConnectionsWeight[t].get(c);void 0===d?this.nodeConnectionsWeight[t].set(c,i.weight):this.nodeConnectionsWeight[t].set(c,d+i.weight);var f=this.nodeConnectionsCount[t].get(c);if(void 0===f?this.nodeConnectionsCount[t].set(c,1):this.nodeConnectionsCount[t].set(c,f+1),n!=c){var g=n.connectionsWeight.get(c);void 0===g?n.connectionsWeight.set(c,i.weight):n.connectionsWeight.set(c,g+i.weight);var l=n.connectionsCount.get(c);void 0===l?n.connectionsCount.set(c,1):n.connectionsCount.set(c,l+1)}}},o.prototype.removeNodeFrom=function(t,n){var e=this.nodeCommunities[t],o=this.topology[t];for(var i in o){var r=o[i],s=r.target,u=this.nodeConnectionsWeight[s].get(e),c=this.nodeConnectionsCount[s].get(e);c-1==0?(this.nodeConnectionsWeight[s].delete(e),this.nodeConnectionsCount[s].delete(e)):(this.nodeConnectionsWeight[s].set(e,u-r.weight),this.nodeConnectionsCount[s].set(e,c-1));var h=this.nodeCommunities[s],a=h.connectionsWeight.get(e),d=h.connectionsCount.get(e);if(d-1==0?(h.connectionsWeight.delete(e),h.connectionsCount.delete(e)):(h.connectionsWeight.set(e,a-r.weight),h.connectionsCount.set(e,d-1)),t!=s){if(h!=e){var f=e.connectionsWeight.get(h),g=e.connectionsCount.get(h);g-1==0?(e.connectionsWeight.delete(h),e.connectionsCount.delete(h)):(e.connectionsWeight.set(h,f-r.weight),e.connectionsCount.set(h,g-1))}var l=this.nodeConnectionsWeight[t].get(h),p=this.nodeConnectionsCount[t].get(h);p-1==0?(this.nodeConnectionsWeight[t].delete(h),this.nodeConnectionsCount[t].delete(h)):(this.nodeConnectionsWeight[t].set(h,l-r.weight),this.nodeConnectionsCount[t].set(h,p-1))}}n.remove(t)},o.prototype.moveNodeTo=function(t,n){var e=this.nodeCommunities[t];this.removeNodeFrom(t,e),this.addNodeTo(t,n)},o.prototype.zoomOut=function(){var t=this.communities.reduce(function(t,n){return t.push(n),t},[]),n=t.length,e=new Array(n),o=0;this.nodeCommunities=new Array(n),this.nodeConnectionsWeight=new Array(n),this.nodeConnectionsCount=new Array(n);var s=new Map;t.forEach(function(n){var u=0;this.nodeConnectionsWeight[o]=new Map,this.nodeConnectionsCount[o]=new Map,e[o]=[],this.nodeCommunities[o]=new i(n);var c=new i(this.structure);n.nodes.forEach(function(t){this.invMap.get(t).nodes.forEach(c.nodes.add.bind(c.nodes))},this),s.set(o,c),n.connectionsWeight.forEach(function(n,i){var s=t.indexOf(i);if(~s){u+=s==o?2*n:n;var c=new r(o,s,n);e[o].push(c)}},this),this.weights[o]=u,this.nodeCommunities[o].seed(o),o++}.bind(this)),this.communities=[];for(var u=0;u<n;u++){var c=this.nodeCommunities[u];this.communities.push(c);for(var h in e[u]){var a=e[u][h];this.nodeConnectionsWeight[u].set(this.nodeCommunities[a.target],a.weight),this.nodeConnectionsCount[u].set(this.nodeCommunities[a.target],1),c.connectionsWeight.set(this.nodeCommunities[a.target],a.weight),c.connectionsCount.set(this.nodeCommunities[a.target],1)}}this.N=n,this.topology=e,this.invMap=s},t.exports=o},function(t,n){function e(t){this.structure=t.structure?t.structure:t,this.connectionsWeight=new Map,this.connectionsCount=new Map,this.nodes=new Set,this.weightSum=0}e.prototype.size=function(){return this.nodes.size},e.prototype.seed=function(t){this.nodes.add(t),this.weightSum+=this.structure.weights[t]},e.prototype.add=function(t){return this.nodes.add(t),this.weightSum+=this.structure.weights[t],!0},e.prototype.remove=function(t){var n=this.nodes.delete(t);if(this.weightSum-=this.structure.weights[t],!this.nodes.size){var e=this.structure.communities.indexOf(this);delete this.structure.communities[e]}return n},t.exports=e},function(t,n){function e(t,n,e){this.source=t,this.target=n,this.weight=e}t.exports=e},function(t,n,e){t.exports.degree=e(7),t.exports.betweenness=e(8)},function(t,n){function e(t,n){function e(n){var e=t.getLinks(n.id);u[n.id]=s(e,n.id)}var s,u=Object.create(null);if("both"===(n=(n||"both").toLowerCase())||"inout"===n)s=r;else if("in"===n)s=o;else{if("out"!==n)throw new Error("Expected centrality degree kind is: in, out or both");s=i}return t.forEachNode(e),u}function o(t,n){var e=0;if(!t)return e;for(var o=0;o<t.length;o+=1)e+=t[o].toId===n?1:0;return e}function i(t,n){var e=0;if(!t)return e;for(var o=0;o<t.length;o+=1)e+=t[o].fromId===n?1:0;return e}function r(t){return t?t.length:0}t.exports=e},function(t,n){function e(t,n){function e(t){p[t]/=2}function o(t){p[t.id]=0}function i(t){c=t.id,u(c),r()}function r(){for(t.forEachNode(s);a.length;){for(var n=a.pop(),e=(1+l[n])/g[n],o=d[n],i=0;i<o.length;++i){var r=o[i];l[r]+=g[r]*e}n!==c&&(p[n]+=l[n])}}function s(t){l[t.id]=0}function u(e){function o(t){r(t.id)}function i(t){var n=t.id;d[n]=[],f[n]=-1,g[n]=0}function r(t){-1===f[t]&&(f[t]=f[s]+1,h.push(t)),f[t]===f[s]+1&&(g[t]+=g[s],d[t].push(s))}for(t.forEachNode(i),f[e]=0,g[e]=1,h.push(e);h.length;){var s=h.shift();Object.create(null);a.push(s),t.forEachLinkedNode(s,o,n)}}var c,h=[],a=[],d=Object.create(null),f=Object.create(null),g=Object.create(null),l=Object.create(null),p=Object.create(null);return t.forEachNode(o),t.forEachNode(i),n||Object.keys(p).forEach(e),p}t.exports=e},function(n,e){n.exports=t},function(t,n,e){function o(t){function n(t,n){z.push({link:t,changeType:n})}function e(t,n){z.push({node:t,changeType:n})}function o(t,n){if(void 0===t)throw new Error("Invalid node identifier");R();var e=c(t);return e?q(e,"update"):(e=new r(t),S++,q(e,"add")),e.data=n,O[t]=e,V(),e}function c(t){return O[t]}function d(t){var n=c(t);if(!n)return!1;if(R(),n.links)for(;n.links.length;){var e=n.links[0];m(e)}return delete O[t],S--,q(n,"remove"),V(),!0}function f(t,n,e){R();var i=c(t)||o(t),r=c(n)||o(n),u=T(t,n,e);return j.push(u),s(i,u),t!==n&&s(r,u),U(u,"add"),V(),u}function g(t,n,e){return new u(t,n,e,h(t,n))}function l(t,n,e){var o=h(t,n),i=L.hasOwnProperty(o);if(i||v(t,n)){i||(L[o]=0);var r="@"+ ++L[o];o=h(t+r,n+r)}return new u(t,n,e,o)}function p(t){var n=c(t);return n?n.links:null}function m(t){if(!t)return!1;var n=i(t,j);if(n<0)return!1;R(),j.splice(n,1);var e=c(t.fromId),o=c(t.toId);return e&&(n=i(t,e.links))>=0&&e.links.splice(n,1),o&&(n=i(t,o.links))>=0&&o.links.splice(n,1),U(t,"remove"),V(),!0}function v(t,n){var e,o=c(t);if(!o||!o.links)return null;for(e=0;e<o.links.length;++e){var i=o.links[e];if(i.fromId===t&&i.toId===n)return i}return null}function C(){R(),A(function(t){d(t.id)}),V()}function w(t){var n,e;if("function"==typeof t)for(n=0,e=j.length;n<e;++n)t(j[n])}function y(t,n,e){var o=c(t);if(o&&o.links&&"function"==typeof n)return e?k(o.links,t,n):W(o.links,t,n)}function W(t,n,e){for(var o=0;o<t.length;++o){var i=t[o],r=i.fromId===n?i.toId:i.fromId;if(e(O[r],i))return!0}}function k(t,n,e){for(var o=0;o<t.length;++o){var i=t[o];if(i.fromId===n&&e(O[i.toId],i))return!0}}function b(){}function N(){M+=1}function x(){0===(M-=1)&&z.length>0&&(P.fire("changed",z),z.length=0)}function E(t){if("function"==typeof t)for(var n=Object.keys(O),e=0;e<n.length;++e)if(t(O[n[e]]))return!0}function I(t){if("function"==typeof t){var n;for(n in O)if(t(O[n]))return!0}}t=t||{},void 0===t.uniqueLinkId&&(t.uniqueLinkId=!0);var O="function"==typeof Object.create?Object.create(null):{},j=[],L={},S=0,M=0,A=function(){return Object.keys?E:I}(),T=t.uniqueLinkId?l:g,z=[],U=b,q=b,R=b,V=b,P={addNode:o,addLink:f,removeLink:m,removeNode:d,getNode:c,getNodesCount:function(){return S},getLinksCount:function(){return j.length},getLinks:p,forEachNode:A,forEachLinkedNode:y,forEachLink:w,beginUpdate:R,endUpdate:V,clear:C,hasLink:v,getLink:v};return a(P),function(){function t(){return P.beginUpdate=R=N,P.endUpdate=V=x,U=n,q=e,P.on=o,o.apply(P,arguments)}var o=P.on;P.on=t}(),P}function i(t,n){if(!n)return-1;if(n.indexOf)return n.indexOf(t);var e,o=n.length;for(e=0;e<o;e+=1)if(n[e]===t)return e;return-1}function r(t){this.id=t,this.links=null,this.data=null}function s(t,n){t.links?t.links.push(n):t.links=[n]}function u(t,n,e,o){this.fromId=t,this.toId=n,this.data=e,this.id=o}function c(t){var n,e,o,i=0;if(0==t.length)return i;for(n=0,o=t.length;n<o;n++)e=t.charCodeAt(n),i=(i<<5)-i+e,i|=0;return i}function h(t,n){return c(t.toString()+"👉 "+n.toString())}t.exports=o;var a=e(11)},function(t,n){function e(t){var n=Object.create(null);return{on:function(e,o,i){if("function"!=typeof o)throw new Error("callback is expected to be a function");var r=n[e];return r||(r=n[e]=[]),r.push({callback:o,ctx:i}),t},off:function(e,o){if(void 0===e)return n=Object.create(null),t;if(n[e]){if("function"!=typeof o)delete n[e];else for(var i=n[e],r=0;r<i.length;++r)i[r].callback===o&&i.splice(r,1)}return t},fire:function(e){var o=n[e];if(!o)return t;var i;arguments.length>1&&(i=Array.prototype.splice.call(arguments,1));for(var r=0;r<o.length;++r){var s=o[r];s.callback.apply(s.ctx,i)}return t}}}function o(t){if(!t)throw new Error("Eventify cannot use falsy object as events subject");for(var n=["on","fire","off"],e=0;e<n.length;++e)if(t.hasOwnProperty(n[e]))throw new Error("Subject cannot be eventified, since it already has property '"+n[e]+"'")}t.exports=function(t){o(t);var n=e(t);return t.on=n.on,t.off=n.off,t.fire=n.fire,t}}])}); | ||
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("echarts")):"function"==typeof define&&define.amd?define(["echarts"],e):"object"==typeof exports?exports["echarts-graph-modularity"]=e(require("echarts")):t["echarts-graph-modularity"]=e(t.echarts)}(self,(function(t){return e={10:(t,e,n)=>{t.exports=n(225)},216:(t,e,n)=>{t.exports.degree=n(176),t.exports.betweenness=n(476)},476:t=>{t.exports=function(t,e){var n,o=[],i=[],r=Object.create(null),s=Object.create(null),u=Object.create(null),c=Object.create(null),h=Object.create(null);return t.forEachNode((function(t){h[t.id]=0})),t.forEachNode((function(d){(function(n){for(t.forEachNode((function(t){var e=t.id;r[e]=[],s[e]=-1,u[e]=0})),s[n]=0,u[n]=1,o.push(n);o.length;){var c=o.shift();i.push(c),t.forEachLinkedNode(c,h,e)}function h(t){var e;e=t.id,-1===s[e]&&(s[e]=s[c]+1,o.push(e)),s[e]===s[c]+1&&(u[e]+=u[c],r[e].push(c))}})(n=d.id),function(){for(t.forEachNode(a);i.length;){for(var e=i.pop(),o=(1+c[e])/u[e],s=r[e],d=0;d<s.length;++d){var f=s[d];c[f]+=u[f]*o}e!==n&&(h[e]+=c[e])}}()})),e||Object.keys(h).forEach((function(t){h[t]/=2})),h;function a(t){c[t.id]=0}}},176:t=>{function e(t,e){var n=0;if(!t)return n;for(var o=0;o<t.length;o+=1)n+=t[o].toId===e?1:0;return n}function n(t,e){var n=0;if(!t)return n;for(var o=0;o<t.length;o+=1)n+=t[o].fromId===e?1:0;return n}function o(t){return t?t.length:0}t.exports=function(t,i){var r,s=Object.create(null);if("both"===(i=(i||"both").toLowerCase())||"inout"===i)r=o;else if("in"===i)r=e;else{if("out"!==i)throw new Error("Expected centrality degree kind is: in, out or both");r=n}return t.forEachNode((function(e){var n=t.getLinks(e.id);s[e.id]=r(n,e.id)})),s}},245:t=>{t.exports=function(t){!function(t){if(!t)throw new Error("Eventify cannot use falsy object as events subject");for(var e=["on","fire","off"],n=0;n<e.length;++n)if(t.hasOwnProperty(e[n]))throw new Error("Subject cannot be eventified, since it already has property '"+e[n]+"'")}(t);var e=function(t){var e=Object.create(null);return{on:function(n,o,i){if("function"!=typeof o)throw new Error("callback is expected to be a function");var r=e[n];return r||(r=e[n]=[]),r.push({callback:o,ctx:i}),t},off:function(n,o){if(void 0===n)return e=Object.create(null),t;if(e[n])if("function"!=typeof o)delete e[n];else for(var i=e[n],r=0;r<i.length;++r)i[r].callback===o&&i.splice(r,1);return t},fire:function(n){var o,i=e[n];if(!i)return t;arguments.length>1&&(o=Array.prototype.splice.call(arguments,1));for(var r=0;r<i.length;++r){var s=i[r];s.callback.apply(s.ctx,o)}return t}}}(t);return t.on=e.on,t.off=e.off,t.fire=e.fire,t}},736:(t,e,n)=>{t.exports=function(t){void 0===(t=t||{}).uniqueLinkId&&(t.uniqueLinkId=!0);var e,n="function"==typeof Object.create?Object.create(null):{},h=[],a={},d=0,f=0,g=Object.keys?function(t){if("function"==typeof t)for(var e=Object.keys(n),o=0;o<e.length;++o)if(t(n[e[o]]))return!0}:function(t){var e;if("function"==typeof t)for(e in n)if(t(n[e]))return!0},l=t.uniqueLinkId?function(t,e,n){var o=c(t,e),i=a.hasOwnProperty(o);if(i||I(t,e)){i||(a[o]=0);var r="@"+ ++a[o];o=c(t+r,e+r)}return new u(t,e,n,o)}:function(t,e,n){return new u(t,e,n,c(t,e))},p=[],m=O,v=O,C=O,y=O,w={addNode:b,addLink:function(t,e,n){C();var o=N(t)||b(t),i=N(e)||b(e),r=l(t,e,n);return h.push(r),s(o,r),t!==e&&s(i,r),m(r,"add"),y(),r},removeLink:E,removeNode:x,getNode:N,getNodesCount:function(){return d},getLinksCount:function(){return h.length},getLinks:function(t){var e=N(t);return e?e.links:null},forEachNode:g,forEachLinkedNode:function(t,e,o){var i=N(t);if(i&&i.links&&"function"==typeof e)return o?function(t,e,o){for(var i=0;i<t.length;++i){var r=t[i];if(r.fromId===e&&o(n[r.toId],r))return!0}}(i.links,t,e):function(t,e,o){for(var i=0;i<t.length;++i){var r=t[i],s=r.fromId===e?r.toId:r.fromId;if(o(n[s],r))return!0}}(i.links,t,e)},forEachLink:function(t){var e,n;if("function"==typeof t)for(e=0,n=h.length;e<n;++e)t(h[e])},beginUpdate:C,endUpdate:y,clear:function(){C(),g((function(t){x(t.id)})),y()},hasLink:I,getLink:I};return o(w),e=w.on,w.on=function(){return w.beginUpdate=C=L,w.endUpdate=y=j,m=k,v=W,w.on=e,e.apply(w,arguments)},w;function k(t,e){p.push({link:t,changeType:e})}function W(t,e){p.push({node:t,changeType:e})}function b(t,e){if(void 0===t)throw new Error("Invalid node identifier");C();var o=N(t);return o?v(o,"update"):(o=new r(t),d++,v(o,"add")),o.data=e,n[t]=o,y(),o}function N(t){return n[t]}function x(t){var e=N(t);if(!e)return!1;if(C(),e.links)for(;e.links.length;)E(e.links[0]);return delete n[t],d--,v(e,"remove"),y(),!0}function E(t){if(!t)return!1;var e=i(t,h);if(e<0)return!1;C(),h.splice(e,1);var n=N(t.fromId),o=N(t.toId);return n&&(e=i(t,n.links))>=0&&n.links.splice(e,1),o&&(e=i(t,o.links))>=0&&o.links.splice(e,1),m(t,"remove"),y(),!0}function I(t,e){var n,o=N(t);if(!o||!o.links)return null;for(n=0;n<o.links.length;++n){var i=o.links[n];if(i.fromId===t&&i.toId===e)return i}return null}function O(){}function L(){f+=1}function j(){0==(f-=1)&&p.length>0&&(w.fire("changed",p),p.length=0)}};var o=n(245);function i(t,e){if(!e)return-1;if(e.indexOf)return e.indexOf(t);var n,o=e.length;for(n=0;n<o;n+=1)if(e[n]===t)return n;return-1}function r(t){this.id=t,this.links=null,this.data=null}function s(t,e){t.links?t.links.push(e):t.links=[e]}function u(t,e,n,o){this.fromId=t,this.toId=e,this.data=n,this.id=o}function c(t,e){return function(t){var e,n,o=0;if(0==t.length)return o;for(e=0,n=t.length;e<n;e++)o=(o<<5)-o+t.charCodeAt(e),o|=0;return o}(t.toString()+"👉 "+e.toString())}},410:t=>{function e(t){this.structure=t.structure?t.structure:t,this.connectionsWeight=new Map,this.connectionsCount=new Map,this.nodes=new Set,this.weightSum=0}e.prototype.size=function(){return this.nodes.size},e.prototype.seed=function(t){this.nodes.add(t),this.weightSum+=this.structure.weights[t]},e.prototype.add=function(t){return this.nodes.add(t),this.weightSum+=this.structure.weights[t],!0},e.prototype.remove=function(t){var e=this.nodes.delete(t);if(this.weightSum-=this.structure.weights[t],!this.nodes.size){var n=this.structure.communities.indexOf(this);delete this.structure.communities[n]}return e},t.exports=e},997:(t,e,n)=>{"use strict";var o=n(410),i=n(623);function r(t,e){this.N=t.getNodesCount(),this.graphWeightSum=0,this.structure=this,this.invMap=new Map,this.nodeConnectionsWeight=new Array(this.N),this.nodeConnectionsCount=new Array(this.N),this.nodeCommunities=new Array(this.N),this.map=new Map,this.topology=new Array(this.N);for(var n=0;n<this.N;n++)this.topology[n]=[];this.communities=[],this.weights=new Array(this.N);var i=0;t.forEachNode(function(t){this.map.set(t.id,i),this.nodeCommunities[i]=new o(this),this.nodeConnectionsWeight[i]=new Map,this.nodeConnectionsCount[i]=new Map,this.weights[i]=0,this.nodeCommunities[i].seed(i);var e=new o(this);e.nodes.add(i),this.invMap.set(i,e),this.communities.push(this.nodeCommunities[i]),i++}.bind(this)),t.forEachLink(function(t){var n=this.map.get(t.fromId),o=this.map.get(t.toId),i=1;n!==o&&(e&&(i=t.data.weight),this.setUpLink(n,o,i),this.setUpLink(o,n,i))}.bind(this)),this.graphWeightSum/=2}r.prototype.setUpLink=function(t,e,n){this.weights[t]+=n;var o=new i(t,e,n);this.topology[t].push(o);var r=this.nodeCommunities[e];this.nodeConnectionsWeight[t].set(r,n),this.nodeConnectionsCount[t].set(r,1),this.nodeCommunities[t].connectionsWeight.set(r,n),this.nodeCommunities[t].connectionsCount.set(r,1),this.nodeConnectionsWeight[e].set(this.nodeCommunities[t],n),this.nodeConnectionsCount[e].set(this.nodeCommunities[t],1),this.nodeCommunities[e].connectionsWeight.set(this.nodeCommunities[t],n),this.nodeCommunities[e].connectionsCount.set(this.nodeCommunities[t],1),this.graphWeightSum+=n},r.prototype.addNodeTo=function(t,e){e.add(t),this.nodeCommunities[t]=e;var n=this.topology[t];for(var o in n){var i=n[o],r=i.target,s=this.nodeConnectionsWeight[r].get(e);void 0===s?this.nodeConnectionsWeight[r].set(e,i.weight):this.nodeConnectionsWeight[r].set(e,s+i.weight);var u=this.nodeConnectionsCount[r].get(e);void 0===u?this.nodeConnectionsCount[r].set(e,1):this.nodeConnectionsCount[r].set(e,u+1);var c=this.nodeCommunities[r],h=c.connectionsWeight.get(e);void 0===h?c.connectionsWeight.set(e,i.weight):c.connectionsWeight.set(e,h+i.weight);var a=c.connectionsCount.get(e);void 0===a?c.connectionsCount.set(e,1):c.connectionsCount.set(e,a+1);var d=this.nodeConnectionsWeight[t].get(c);void 0===d?this.nodeConnectionsWeight[t].set(c,i.weight):this.nodeConnectionsWeight[t].set(c,d+i.weight);var f=this.nodeConnectionsCount[t].get(c);if(void 0===f?this.nodeConnectionsCount[t].set(c,1):this.nodeConnectionsCount[t].set(c,f+1),e!=c){var g=e.connectionsWeight.get(c);void 0===g?e.connectionsWeight.set(c,i.weight):e.connectionsWeight.set(c,g+i.weight);var l=e.connectionsCount.get(c);void 0===l?e.connectionsCount.set(c,1):e.connectionsCount.set(c,l+1)}}},r.prototype.removeNodeFrom=function(t,e){var n=this.nodeCommunities[t],o=this.topology[t];for(var i in o){var r=o[i],s=r.target,u=this.nodeConnectionsWeight[s].get(n),c=this.nodeConnectionsCount[s].get(n);c-1==0?(this.nodeConnectionsWeight[s].delete(n),this.nodeConnectionsCount[s].delete(n)):(this.nodeConnectionsWeight[s].set(n,u-r.weight),this.nodeConnectionsCount[s].set(n,c-1));var h=this.nodeCommunities[s],a=h.connectionsWeight.get(n),d=h.connectionsCount.get(n);if(d-1==0?(h.connectionsWeight.delete(n),h.connectionsCount.delete(n)):(h.connectionsWeight.set(n,a-r.weight),h.connectionsCount.set(n,d-1)),t!=s){if(h!=n){var f=n.connectionsWeight.get(h),g=n.connectionsCount.get(h);g-1==0?(n.connectionsWeight.delete(h),n.connectionsCount.delete(h)):(n.connectionsWeight.set(h,f-r.weight),n.connectionsCount.set(h,g-1))}var l=this.nodeConnectionsWeight[t].get(h),p=this.nodeConnectionsCount[t].get(h);p-1==0?(this.nodeConnectionsWeight[t].delete(h),this.nodeConnectionsCount[t].delete(h)):(this.nodeConnectionsWeight[t].set(h,l-r.weight),this.nodeConnectionsCount[t].set(h,p-1))}}e.remove(t)},r.prototype.moveNodeTo=function(t,e){var n=this.nodeCommunities[t];this.removeNodeFrom(t,n),this.addNodeTo(t,e)},r.prototype.zoomOut=function(){var t=this.communities.reduce((function(t,e){return t.push(e),t}),[]),e=t.length,n=new Array(e),r=0;this.nodeCommunities=new Array(e),this.nodeConnectionsWeight=new Array(e),this.nodeConnectionsCount=new Array(e);var s=new Map;t.forEach(function(e){var u=0;this.nodeConnectionsWeight[r]=new Map,this.nodeConnectionsCount[r]=new Map,n[r]=[],this.nodeCommunities[r]=new o(e);var c=new o(this.structure);e.nodes.forEach((function(t){this.invMap.get(t).nodes.forEach(c.nodes.add.bind(c.nodes))}),this),s.set(r,c),e.connectionsWeight.forEach((function(e,o){var s=t.indexOf(o);if(~s){u+=s==r?2*e:e;var c=new i(r,s,e);n[r].push(c)}}),this),this.weights[r]=u,this.nodeCommunities[r].seed(r),r++}.bind(this)),this.communities=[];for(var u=0;u<e;u++){var c=this.nodeCommunities[u];for(var h in this.communities.push(c),n[u]){var a=n[u][h];this.nodeConnectionsWeight[u].set(this.nodeCommunities[a.target],a.weight),this.nodeConnectionsCount[u].set(this.nodeCommunities[a.target],1),c.connectionsWeight.set(this.nodeCommunities[a.target],a.weight),c.connectionsCount.set(this.nodeCommunities[a.target],1)}}this.N=e,this.topology=n,this.invMap=s},t.exports=r},623:t=>{t.exports=function(t,e,n){this.source=t,this.target=e,this.weight=n}},109:(t,e,n)=>{var o=n(997),i=n(216);function r(t,e){this.isRandomized=!1,this.useWeight=e,this.resolution=t||1,this.structure=null}r.prototype.execute=function(t){this.structure=new o(t,this.useWeight);var e=new Array(t.getNodesCount()),n=(this.computeModularity(t,this.structure,e,this.resolution,this.isRandomized,this.useWeight),{});return this.structure.map.forEach((function(t,o){n[o]=e[t]})),n},r.prototype.computeModularity=function(t,e,n,o,i,r){e.graphWeightSum,e.weights.slice();for(var s,u=Object.create(null),c=!0;c;){c=!1;for(var h=!0;h;){h=!1;var a=0;i&&(0,s=e.N,a=Math.floor(Math.random()*(s-0))+0);for(var d=0,f=a;d<e.N;f=(f+1)%e.N){d++;var g=this.updateBestCommunity(e,f,o);e.nodeCommunities[f]!=g&&null!=g&&(e.moveNodeTo(f,g),h=!0)}c=h||c}c&&e.zoomOut()}return this.fillComStructure(t,e,n),u},r.prototype.updateBestCommunity=function(t,e,n){var o=this.q(e,t.nodeCommunities[e],t,n),i=t.nodeCommunities[e];return t.nodeConnectionsWeight[e].forEach((function(r,s){var u=this.q(e,s,t,n);u>o&&(o=u,i=s)}),this),i},r.prototype.fillComStructure=function(t,e,n){var o=0;return e.communities.forEach((function(t){t.nodes.forEach((function(t){e.invMap.get(t).nodes.forEach((function(t){n[t]=o}))})),o++})),n},r.prototype.fillDegreeCount=function(t,e,n,o,r){var s=new Array(e.communities.length),u=i.degree(t);return t.forEachNode((function(t){var i=e.map.get(t);s[n[i]]+=r?o[i]:u[t.id]})),s},r.prototype._finalQ=function(t,e,n,o,i,r,s){throw new Error("not implemented properly")},r.prototype.q=function(t,e,n,o){var i=n.nodeConnectionsWeight[t].get(e),r=0;null!=i&&(r=i);var s=e.weightSum,u=n.weights[t],c=o*r-u*s/(2*n.graphWeightSum);return n.nodeCommunities[t]==e&&n.nodeCommunities[t].size()>1&&(c=o*r-u*(s-u)/(2*n.graphWeightSum)),n.nodeCommunities[t]==e&&1==n.nodeCommunities[t].size()&&(c=0),c},t.exports=r},225:(t,e,n)=>{var o=n(109),i=n(83),r=n(736);function s(t){return function(e,n){var i={};e.eachSeriesByType(t,(function(t){if(t.get("modularity")){var e=t.getGraph(),n={},s=r();e.data.each((function(t){var o=e.getNodeByIndex(t);return n[o.id]=t,s.addNode(o.id),o.id})),e.edgeData.each("value",(function(t,n){var o=e.getEdgeByIndex(n);return s.addLink(o.node1.id,o.node2.id),{source:o.node1.id,target:o.node2.id,value:t}}));var u=new o(t.get("modularity.resolution")||1).execute(s),c={};for(var h in u)c[f=u[h]]=c[f]||0,c[f]++;var a=Object.keys(c);t.get("modularity.sort")&&a.sort((function(t,e){return e-t}));var d={};for(var h in a.forEach((function(e){d[e]=t.getColorFromPalette(e,i)})),u){var f=u[h];e.data.ensureUniqueItemVisual(n[h],"style").fill=d[f]}e.edgeData.each((function(t){var n=e.edgeData.getItemModel(t),o=e.getEdgeByIndex(t),i=n.get("lineStyle.normal.color");switch(i){case"source":i=o.node1.getVisual("style").fill;break;case"target":i=o.node2.getVisual("style").fill}null!=i&&(o.data.ensureUniqueItemVisual(t,"style").stroke=i)}))}}))}}i.registerVisual(i.PRIORITY.VISUAL.CHART+1,s("graph")),i.registerVisual(i.PRIORITY.VISUAL.CHART+1,s("graphGL"))},83:e=>{"use strict";e.exports=t}},n={},function t(o){if(n[o])return n[o].exports;var i=n[o]={exports:{}};return e[o](i,i.exports,t),i.exports}(10);var e,n})); | ||
//# sourceMappingURL=echarts-graph-modularity.min.js.map |
{ | ||
"name": "echarts-graph-modularity", | ||
"version": "1.1.0", | ||
"version": "2.0.0", | ||
"description": "ECharts graph modularity extension for community detection", | ||
@@ -9,6 +9,18 @@ "main": "index.js", | ||
"dependencies": { | ||
"echarts": "^3.6.2", | ||
"ngraph.graph": "0.0.12", | ||
"ngraph.modularity": "1.0.5" | ||
}, | ||
"scripts": { | ||
"dev": "npx webpack --mode development --watch", | ||
"build": "npx webpack --mode development", | ||
"release": "npx webpack --mode production && npx webpack --mode development" | ||
}, | ||
"peerDependencies": { | ||
"echarts": "^5.0.0" | ||
}, | ||
"devDependencies": { | ||
"echarts": "^5.0.0", | ||
"webpack": "^5.11.1", | ||
"webpack-cli": "^4.3.1" | ||
}, | ||
"repository": { | ||
@@ -15,0 +27,0 @@ "type": "git", |
@@ -1,9 +0,5 @@ | ||
# [ECharts](https://github.com/ecomfe/echarts) graph modularity extension | ||
# graph modularity extension for [Apache ECharts (incubating)](https://github.com/apache/incubator-echarts) | ||
<a href="http://echarts.baidu.com"> | ||
<img style="vertical-align: top;" src="https://github.com/ecomfe/echarts/raw/master/asset/logo.png?raw=true" alt="logo" height="50px"> | ||
</a> | ||
Graph modularity extension will do community detection and partition a graph's vertices in several subsets. Each subset will be assigned a different color. | ||
Graph modularity extension will do community detection and partian a graph's vertices in several subsets. Each subset will be assigned a different color. | ||
 | ||
@@ -25,6 +21,10 @@ | ||
```js | ||
var echarts = require('echarts'); | ||
require('echarts-graph-modularity'); | ||
import * as echarts from 'echarts'; | ||
import 'echarts-graph-modularity'; | ||
``` | ||
NOTE: | ||
V2.x is for ECharts 5.x | ||
## Usage | ||
@@ -46,3 +46,5 @@ | ||
modularity: { | ||
resolution: 5 | ||
resolution: 5, | ||
// If sort the communities | ||
sort: false | ||
} | ||
@@ -49,0 +51,0 @@ |
@@ -32,9 +32,26 @@ var Modularity = require('ngraph.modularity/Modularity'); | ||
var result = modularity.execute(ng); | ||
console.log(result); | ||
var communities = {}; | ||
for (var id in result) { | ||
var comm = result[id]; | ||
graph.data.setItemVisual(idIndexMap[id], 'color', seriesModel.getColorFromPalette(comm, paletteScope)); | ||
communities[comm] = communities[comm] || 0; | ||
communities[comm]++; | ||
} | ||
var communitiesList = Object.keys(communities); | ||
if (seriesModel.get('modularity.sort')) { | ||
communitiesList.sort(function (a, b) { | ||
return b - a; | ||
}); | ||
} | ||
var colors = {}; | ||
communitiesList.forEach(function (comm) { | ||
colors[comm] = seriesModel.getColorFromPalette(comm, paletteScope); | ||
}); | ||
for (var id in result) { | ||
var comm = result[id]; | ||
var style = graph.data.ensureUniqueItemVisual(idIndexMap[id], 'style'); | ||
style.fill = colors[comm]; | ||
} | ||
graph.edgeData.each(function (idx) { | ||
@@ -47,6 +64,6 @@ var itemModel = graph.edgeData.getItemModel(idx); | ||
case 'source': | ||
color = edge.node1.getVisual('color'); | ||
color = edge.node1.getVisual('style').fill; | ||
break; | ||
case 'target': | ||
color = edge.node2.getVisual('color'); | ||
color = edge.node2.getVisual('style').fill; | ||
break; | ||
@@ -56,3 +73,3 @@ } | ||
if (color != null) { | ||
edge.setVisual('color', color); | ||
edge.data.ensureUniqueItemVisual(idx, 'style').stroke = color; | ||
} | ||
@@ -59,0 +76,0 @@ }); |
@@ -1,16 +0,20 @@ | ||
var PROD = process.argv.indexOf('-p') >= 0; | ||
module.exports = { | ||
entry: { | ||
'echarts-graph-modularity': __dirname + '/index.js' | ||
}, | ||
output: { | ||
libraryTarget: 'umd', | ||
library: ['echarts-graph-modularity'], | ||
path: __dirname + '/dist', | ||
filename: PROD ? '[name].min.js' : '[name].js' | ||
}, | ||
externals: { | ||
'echarts/lib/echarts': 'echarts' | ||
} | ||
module.exports = (env, options) => { | ||
return { | ||
entry: { | ||
'echarts-graph-modularity': __dirname + '/index.js' | ||
}, | ||
output: { | ||
libraryTarget: 'umd', | ||
library: ['echarts-graph-modularity'], | ||
path: __dirname + '/dist', | ||
filename: options.mode === 'production' ? '[name].min.js' : '[name].js' | ||
}, | ||
optimization: { | ||
concatenateModules: true | ||
}, | ||
devtool: 'source-map', | ||
externals: { | ||
'echarts/lib/echarts': 'echarts' | ||
} | ||
}; | ||
}; |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
326248
11
1630
54
3
2
+ Addedecharts@5.6.0(transitive)
+ Addedtslib@2.3.0(transitive)
+ Addedzrender@5.6.1(transitive)
- Removedecharts@^3.6.2
- Removedecharts@3.8.5(transitive)
- Removedzrender@3.7.4(transitive)