cytoscape-cose-bilkent
Advanced tools
Comparing version 4.0.0 to 4.1.0
@@ -6,3 +6,4 @@ { | ||
"dependencies": { | ||
"cytoscape": "^3.2.0" | ||
"cytoscape": "^3.2.0", | ||
"cose-base": "^1.0.0" | ||
}, | ||
@@ -9,0 +10,0 @@ "repository": { |
{ | ||
"name": "cytoscape-cose-bilkent", | ||
"version": "4.0.0", | ||
"version": "4.1.0", | ||
"description": "The CoSE layout for Cytoscape.js by Bilkent with enhanced compound node placement", | ||
"main": "cytoscape-cose-bilkent.js", | ||
"scripts": { | ||
"postpublish": "run-s gh-pages:demo gh-pages:deploy gh-pages:clean", | ||
"gh-pages:demo": "cpy demo.html . --rename=index.html", | ||
"gh-pages:deploy": "gh-pages -d .", | ||
"gh-pages:clean": "rimraf index.html", | ||
"postpublish": "run-s gh-pages", | ||
"gh-pages": "gh-pages -d pages", | ||
"copyright": "update license", | ||
"lint": "eslint src", | ||
"build": "cross-env NODE_ENV=production webpack", | ||
"build:min": "cross-env NODE_ENV=production MIN=true webpack", | ||
"build:release": "run-s build copyright", | ||
"watch": "webpack --progress --watch", | ||
@@ -37,11 +37,13 @@ "dev": "webpack-dev-server --open", | ||
"chai": "4.0.2", | ||
"cpy-cli": "^1.0.1", | ||
"cross-env": "^5.0.0", | ||
"eslint": "^3.9.1", | ||
"gh-pages": "^1.0.0", | ||
"mocha": "3.4.2", | ||
"npm-run-all": "^4.1.2", | ||
"rimraf": "^2.6.2", | ||
"update": "^0.7.4", | ||
"updater-license": "^1.0.0", | ||
"webpack": "^2.6.1", | ||
"webpack-dev-server": "^2.4.5", | ||
"cpy-cli": "^1.0.1", | ||
"npm-run-all": "^4.1.2", | ||
"gh-pages": "^1.0.0", | ||
"rimraf": "^2.6.2" | ||
"webpack-dev-server": "^2.4.5" | ||
}, | ||
@@ -51,3 +53,5 @@ "peerDependencies": { | ||
}, | ||
"dependencies": {} | ||
"dependencies": { | ||
"cose-base": "^1.0.0" | ||
} | ||
} |
@@ -8,3 +8,4 @@ cytoscape-cose-bilkent | ||
The CoSE layout for Cytoscape.js by the [i-Vis Lab](http://cs.bilkent.edu.tr/~ivis/) in Bilkent University ([demo](https://cytoscape.github.io/cytoscape.js-cose-bilkent/demo.html), [compound demo](https://cytoscape.github.io/cytoscape.js-cose-bilkent/demo-compound.html)) | ||
The CoSE (Compound Spring Embedder) layout for Cytoscape.js developed by [i-Vis Lab](http://cs.bilkent.edu.tr/~ivis/) in Bilkent University is a spring embedder layout with support for compound graphs (nested structures) and varying (non-uniform) node dimensions. | ||
([demo](https://raw.githack.com/cytoscape/cytoscape.js-cose-bilkent/master/demo.html), [compound demo](https://raw.githack.com/cytoscape/cytoscape.js-cose-bilkent/master/demo-compound.html)) | ||
@@ -18,2 +19,3 @@ Please cite the following when using this layout: | ||
* Cytoscape.js ^3.2.0 | ||
* cose-base ^1.0.0 | ||
@@ -71,2 +73,7 @@ | ||
}, | ||
// 'draft', 'default' or 'proof" | ||
// - 'draft' fast cooling rate | ||
// - 'default' moderate cooling rate | ||
// - "proof" slow cooling rate | ||
quality: 'default', | ||
// Whether to include labels in node dimensions. Useful for avoiding label overlap | ||
@@ -98,2 +105,4 @@ nodeDimensionsIncludeLabels: false, | ||
animate: 'end', | ||
// Duration for animate:end | ||
animationDuration: 500, | ||
// Amount of vertical space to put between degree zero nodes during tiling (can also be a function) | ||
@@ -114,3 +123,7 @@ tilingPaddingVertical: 10, | ||
*Note that this extension supports only relatively modern browsers. Browsers like IE require significant shimming, for example with [core-js](https://www.npmjs.com/package/core-js).* | ||
*Note that while running Cytoscape.js in headless mode, stylingEnabled option of Cytoscape.js should be set as true because this extension considers node dimensions and some other styling properties.* | ||
## Build targets | ||
@@ -131,3 +144,3 @@ | ||
1. Build the extension : `npm run build` | ||
1. Build the extension : `npm run build:release` | ||
1. Commit the build : `git commit -am "Build for release"` | ||
@@ -134,0 +147,0 @@ 1. Bump the version number and tag: `npm version major|minor|patch` |
381
src/index.js
'use strict'; | ||
// registers the extension on a cytoscape lib ref | ||
var getLayout = require('./Layout'); | ||
var LayoutConstants = require('cose-base').layoutBase.LayoutConstants; | ||
var FDLayoutConstants = require('cose-base').layoutBase.FDLayoutConstants; | ||
var CoSEConstants = require('cose-base').CoSEConstants; | ||
var CoSELayout = require('cose-base').CoSELayout; | ||
var CoSENode = require('cose-base').CoSENode; | ||
var PointD = require('cose-base').layoutBase.PointD; | ||
var DimensionD = require('cose-base').layoutBase.DimensionD; | ||
var defaults = { | ||
// Called on `layoutready` | ||
ready: function () { | ||
}, | ||
// Called on `layoutstop` | ||
stop: function () { | ||
}, | ||
// 'draft', 'default' or 'proof" | ||
// - 'draft' fast cooling rate | ||
// - 'default' moderate cooling rate | ||
// - "proof" slow cooling rate | ||
quality: 'default', | ||
// include labels in node dimensions | ||
nodeDimensionsIncludeLabels: false, | ||
// number of ticks per frame; higher is faster but more jerky | ||
refresh: 30, | ||
// Whether to fit the network view after when done | ||
fit: true, | ||
// Padding on fit | ||
padding: 10, | ||
// Whether to enable incremental mode | ||
randomize: true, | ||
// Node repulsion (non overlapping) multiplier | ||
nodeRepulsion: 4500, | ||
// Ideal edge (non nested) length | ||
idealEdgeLength: 50, | ||
// Divisor to compute edge forces | ||
edgeElasticity: 0.45, | ||
// Nesting factor (multiplier) to compute ideal edge length for nested edges | ||
nestingFactor: 0.1, | ||
// Gravity force (constant) | ||
gravity: 0.25, | ||
// Maximum number of iterations to perform | ||
numIter: 2500, | ||
// For enabling tiling | ||
tile: true, | ||
// Type of layout animation. The option set is {'during', 'end', false} | ||
animate: 'end', | ||
// Duration for animate:end | ||
animationDuration: 500, | ||
// Represents the amount of the vertical space to put between the zero degree members during the tiling operation(can also be a function) | ||
tilingPaddingVertical: 10, | ||
// Represents the amount of the horizontal space to put between the zero degree members during the tiling operation(can also be a function) | ||
tilingPaddingHorizontal: 10, | ||
// Gravity range (constant) for compounds | ||
gravityRangeCompound: 1.5, | ||
// Gravity force (constant) for compounds | ||
gravityCompound: 1.0, | ||
// Gravity range (constant) | ||
gravityRange: 3.8, | ||
// Initial cooling factor for incremental layout | ||
initialEnergyOnIncremental: 0.5 | ||
}; | ||
function extend(defaults, options) { | ||
var obj = {}; | ||
for (var i in defaults) { | ||
obj[i] = defaults[i]; | ||
} | ||
for (var i in options) { | ||
obj[i] = options[i]; | ||
} | ||
return obj; | ||
}; | ||
function _CoSELayout(_options) { | ||
this.options = extend(defaults, _options); | ||
getUserOptions(this.options); | ||
} | ||
var getUserOptions = function (options) { | ||
if (options.nodeRepulsion != null) | ||
CoSEConstants.DEFAULT_REPULSION_STRENGTH = FDLayoutConstants.DEFAULT_REPULSION_STRENGTH = options.nodeRepulsion; | ||
if (options.idealEdgeLength != null) | ||
CoSEConstants.DEFAULT_EDGE_LENGTH = FDLayoutConstants.DEFAULT_EDGE_LENGTH = options.idealEdgeLength; | ||
if (options.edgeElasticity != null) | ||
CoSEConstants.DEFAULT_SPRING_STRENGTH = FDLayoutConstants.DEFAULT_SPRING_STRENGTH = options.edgeElasticity; | ||
if (options.nestingFactor != null) | ||
CoSEConstants.PER_LEVEL_IDEAL_EDGE_LENGTH_FACTOR = FDLayoutConstants.PER_LEVEL_IDEAL_EDGE_LENGTH_FACTOR = options.nestingFactor; | ||
if (options.gravity != null) | ||
CoSEConstants.DEFAULT_GRAVITY_STRENGTH = FDLayoutConstants.DEFAULT_GRAVITY_STRENGTH = options.gravity; | ||
if (options.numIter != null) | ||
CoSEConstants.MAX_ITERATIONS = FDLayoutConstants.MAX_ITERATIONS = options.numIter; | ||
if (options.gravityRange != null) | ||
CoSEConstants.DEFAULT_GRAVITY_RANGE_FACTOR = FDLayoutConstants.DEFAULT_GRAVITY_RANGE_FACTOR = options.gravityRange; | ||
if(options.gravityCompound != null) | ||
CoSEConstants.DEFAULT_COMPOUND_GRAVITY_STRENGTH = FDLayoutConstants.DEFAULT_COMPOUND_GRAVITY_STRENGTH = options.gravityCompound; | ||
if(options.gravityRangeCompound != null) | ||
CoSEConstants.DEFAULT_COMPOUND_GRAVITY_RANGE_FACTOR = FDLayoutConstants.DEFAULT_COMPOUND_GRAVITY_RANGE_FACTOR = options.gravityRangeCompound; | ||
if (options.initialEnergyOnIncremental != null) | ||
CoSEConstants.DEFAULT_COOLING_FACTOR_INCREMENTAL = FDLayoutConstants.DEFAULT_COOLING_FACTOR_INCREMENTAL = options.initialEnergyOnIncremental; | ||
if (options.quality == 'draft') | ||
LayoutConstants.QUALITY = 0; | ||
else if(options.quality == 'proof') | ||
LayoutConstants.QUALITY = 2; | ||
else | ||
LayoutConstants.QUALITY = 1; | ||
CoSEConstants.NODE_DIMENSIONS_INCLUDE_LABELS = FDLayoutConstants.NODE_DIMENSIONS_INCLUDE_LABELS = LayoutConstants.NODE_DIMENSIONS_INCLUDE_LABELS = options.nodeDimensionsIncludeLabels; | ||
CoSEConstants.DEFAULT_INCREMENTAL = FDLayoutConstants.DEFAULT_INCREMENTAL = LayoutConstants.DEFAULT_INCREMENTAL = | ||
!(options.randomize); | ||
CoSEConstants.ANIMATE = FDLayoutConstants.ANIMATE = LayoutConstants.ANIMATE = options.animate; | ||
CoSEConstants.TILE = options.tile; | ||
CoSEConstants.TILING_PADDING_VERTICAL = | ||
typeof options.tilingPaddingVertical === 'function' ? options.tilingPaddingVertical.call() : options.tilingPaddingVertical; | ||
CoSEConstants.TILING_PADDING_HORIZONTAL = | ||
typeof options.tilingPaddingHorizontal === 'function' ? options.tilingPaddingHorizontal.call() : options.tilingPaddingHorizontal; | ||
}; | ||
_CoSELayout.prototype.run = function () { | ||
var ready; | ||
var frameId; | ||
var options = this.options; | ||
var idToLNode = this.idToLNode = {}; | ||
var layout = this.layout = new CoSELayout(); | ||
var self = this; | ||
self.stopped = false; | ||
this.cy = this.options.cy; | ||
this.cy.trigger({ type: 'layoutstart', layout: this }); | ||
var gm = layout.newGraphManager(); | ||
this.gm = gm; | ||
var nodes = this.options.eles.nodes(); | ||
var edges = this.options.eles.edges(); | ||
this.root = gm.addRoot(); | ||
this.processChildrenList(this.root, this.getTopMostNodes(nodes), layout); | ||
for (var i = 0; i < edges.length; i++) { | ||
var edge = edges[i]; | ||
var sourceNode = this.idToLNode[edge.data("source")]; | ||
var targetNode = this.idToLNode[edge.data("target")]; | ||
if(sourceNode !== targetNode && sourceNode.getEdgesBetween(targetNode).length == 0){ | ||
var e1 = gm.add(layout.newEdge(), sourceNode, targetNode); | ||
e1.id = edge.id(); | ||
} | ||
} | ||
var getPositions = function(ele, i){ | ||
if(typeof ele === "number") { | ||
ele = i; | ||
} | ||
var theId = ele.data('id'); | ||
var lNode = self.idToLNode[theId]; | ||
return { | ||
x: lNode.getRect().getCenterX(), | ||
y: lNode.getRect().getCenterY() | ||
}; | ||
}; | ||
/* | ||
* Reposition nodes in iterations animatedly | ||
*/ | ||
var iterateAnimated = function () { | ||
// Thigs to perform after nodes are repositioned on screen | ||
var afterReposition = function() { | ||
if (options.fit) { | ||
options.cy.fit(options.eles, options.padding); | ||
} | ||
if (!ready) { | ||
ready = true; | ||
self.cy.one('layoutready', options.ready); | ||
self.cy.trigger({type: 'layoutready', layout: self}); | ||
} | ||
}; | ||
var ticksPerFrame = self.options.refresh; | ||
var isDone; | ||
for( var i = 0; i < ticksPerFrame && !isDone; i++ ){ | ||
isDone = self.stopped || self.layout.tick(); | ||
} | ||
// If layout is done | ||
if (isDone) { | ||
// If the layout is not a sublayout and it is successful perform post layout. | ||
if (layout.checkLayoutSuccess() && !layout.isSubLayout) { | ||
layout.doPostLayout(); | ||
} | ||
// If layout has a tilingPostLayout function property call it. | ||
if (layout.tilingPostLayout) { | ||
layout.tilingPostLayout(); | ||
} | ||
layout.isLayoutFinished = true; | ||
self.options.eles.nodes().positions(getPositions); | ||
afterReposition(); | ||
// trigger layoutstop when the layout stops (e.g. finishes) | ||
self.cy.one('layoutstop', self.options.stop); | ||
self.cy.trigger({ type: 'layoutstop', layout: self }); | ||
if (frameId) { | ||
cancelAnimationFrame(frameId); | ||
} | ||
ready = false; | ||
return; | ||
} | ||
var animationData = self.layout.getPositionsData(); // Get positions of layout nodes note that all nodes may not be layout nodes because of tiling | ||
// Position nodes, for the nodes whose id does not included in data (because they are removed from their parents and included in dummy compounds) | ||
// use position of their ancestors or dummy ancestors | ||
options.eles.nodes().positions(function (ele, i) { | ||
if (typeof ele === "number") { | ||
ele = i; | ||
} | ||
// If ele is a compound node, then its position will be defined by its children | ||
if(!ele.isParent()){ | ||
var theId = ele.id(); | ||
var pNode = animationData[theId]; | ||
var temp = ele; | ||
// If pNode is undefined search until finding position data of its first ancestor (It may be dummy as well) | ||
while (pNode == null) { | ||
pNode = animationData[temp.data('parent')] || animationData['DummyCompound_' + temp.data('parent')]; | ||
animationData[theId] = pNode; | ||
temp = temp.parent()[0]; | ||
if(temp == undefined){ | ||
break; | ||
} | ||
} | ||
if(pNode != null){ | ||
return { | ||
x: pNode.x, | ||
y: pNode.y | ||
}; | ||
} else{ | ||
return { | ||
x: ele.position('x'), | ||
y: ele.position('y') | ||
}; | ||
} | ||
} | ||
}); | ||
afterReposition(); | ||
frameId = requestAnimationFrame(iterateAnimated); | ||
}; | ||
/* | ||
* Listen 'layoutstarted' event and start animated iteration if animate option is 'during' | ||
*/ | ||
layout.addListener('layoutstarted', function () { | ||
if (self.options.animate === 'during') { | ||
frameId = requestAnimationFrame(iterateAnimated); | ||
} | ||
}); | ||
layout.runLayout(); // Run cose layout | ||
/* | ||
* If animate option is not 'during' ('end' or false) perform these here (If it is 'during' similar things are already performed) | ||
*/ | ||
if(this.options.animate !== "during"){ | ||
self.options.eles.nodes().not(":parent").layoutPositions(self, self.options, getPositions); // Use layout positions to reposition the nodes it considers the options parameter | ||
ready = false; | ||
} | ||
return this; // chaining | ||
}; | ||
//Get the top most ones of a list of nodes | ||
_CoSELayout.prototype.getTopMostNodes = function(nodes) { | ||
var nodesMap = {}; | ||
for (var i = 0; i < nodes.length; i++) { | ||
nodesMap[nodes[i].id()] = true; | ||
} | ||
var roots = nodes.filter(function (ele, i) { | ||
if(typeof ele === "number") { | ||
ele = i; | ||
} | ||
var parent = ele.parent()[0]; | ||
while(parent != null){ | ||
if(nodesMap[parent.id()]){ | ||
return false; | ||
} | ||
parent = parent.parent()[0]; | ||
} | ||
return true; | ||
}); | ||
return roots; | ||
}; | ||
_CoSELayout.prototype.processChildrenList = function (parent, children, layout) { | ||
var size = children.length; | ||
for (var i = 0; i < size; i++) { | ||
var theChild = children[i]; | ||
var children_of_children = theChild.children(); | ||
var theNode; | ||
var dimensions = theChild.layoutDimensions({ | ||
nodeDimensionsIncludeLabels: this.options.nodeDimensionsIncludeLabels | ||
}); | ||
if (theChild.outerWidth() != null | ||
&& theChild.outerHeight() != null) { | ||
theNode = parent.add(new CoSENode(layout.graphManager, | ||
new PointD(theChild.position('x') - dimensions.w / 2, theChild.position('y') - dimensions.h / 2), | ||
new DimensionD(parseFloat(dimensions.w), parseFloat(dimensions.h)))); | ||
} | ||
else { | ||
theNode = parent.add(new CoSENode(this.graphManager)); | ||
} | ||
// Attach id to the layout node | ||
theNode.id = theChild.data("id"); | ||
// Attach the paddings of cy node to layout node | ||
theNode.paddingLeft = parseInt( theChild.css('padding') ); | ||
theNode.paddingTop = parseInt( theChild.css('padding') ); | ||
theNode.paddingRight = parseInt( theChild.css('padding') ); | ||
theNode.paddingBottom = parseInt( theChild.css('padding') ); | ||
//Attach the label properties to compound if labels will be included in node dimensions | ||
if(this.options.nodeDimensionsIncludeLabels){ | ||
if(theChild.isParent()){ | ||
var labelWidth = theChild.boundingBox({ includeLabels: true, includeNodes: false }).w; | ||
var labelHeight = theChild.boundingBox({ includeLabels: true, includeNodes: false }).h; | ||
var labelPos = theChild.css("text-halign"); | ||
theNode.labelWidth = labelWidth; | ||
theNode.labelHeight = labelHeight; | ||
theNode.labelPos = labelPos; | ||
} | ||
} | ||
// Map the layout node | ||
this.idToLNode[theChild.data("id")] = theNode; | ||
if (isNaN(theNode.rect.x)) { | ||
theNode.rect.x = 0; | ||
} | ||
if (isNaN(theNode.rect.y)) { | ||
theNode.rect.y = 0; | ||
} | ||
if (children_of_children != null && children_of_children.length > 0) { | ||
var theNewGraph; | ||
theNewGraph = layout.getGraphManager().add(layout.newGraph(), theNode); | ||
this.processChildrenList(theNewGraph, children_of_children, layout); | ||
} | ||
} | ||
}; | ||
/** | ||
* @brief : called on continuous layouts to stop them before they finish | ||
*/ | ||
_CoSELayout.prototype.stop = function () { | ||
this.stopped = true; | ||
return this; // chaining | ||
}; | ||
var register = function( cytoscape ){ | ||
var Layout = getLayout( cytoscape ); | ||
// var Layout = getLayout( cytoscape ); | ||
cytoscape('layout', 'cose-bilkent', Layout); | ||
cytoscape('layout', 'cose-bilkent', _CoSELayout); | ||
}; | ||
@@ -11,0 +384,0 @@ |
@@ -25,3 +25,10 @@ const path = require('path'); | ||
}, | ||
externals: PROD ? Object.keys( pkg.dependencies || {} ) : [], | ||
externals: PROD ? { | ||
'cose-base': { | ||
commonjs2: 'cose-base', | ||
commonjs: 'cose-base', | ||
amd: 'cose-base', | ||
root: 'coseBase' | ||
} | ||
} : {}, | ||
plugins: MIN ? [ | ||
@@ -28,0 +35,0 @@ new webpack.optimize.UglifyJsPlugin({ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
146
82395
2
16
16
801
1
+ Addedcose-base@^1.0.0
+ Addedcose-base@1.0.3(transitive)
+ Addedlayout-base@1.0.2(transitive)