Skip to content

Commit

Permalink
Add FCOSE gnee-gene demo to replace Cola gene-gene demo
Browse files Browse the repository at this point in the history
- While Cola is a good layout, FCOSE is faster and provides better compound support.  It's our default recommendation in the blog (https://blog.js.cytoscape.org/2020/05/11/layouts/).
- The Cola demo used to be the first one at the top of the demos.  It gives the impression that it's our top/default pick.
- People end up complaining, "Why can't I do X with Cola?"  If they were to use FCOSE first, they'd get a better result most of the time.
  • Loading branch information
maxkfranz committed Dec 9, 2024
1 parent 0ec5568 commit ee4c829
Show file tree
Hide file tree
Showing 7 changed files with 13,433 additions and 1 deletion.
239 changes: 239 additions & 0 deletions documentation/demos/fcose-gene/code.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
/* global Promise, fetch, window, cytoscape, document, tippy, _ */

Promise.all([
fetch('cy-style.json')
.then(function(res) {
return res.json();
}),
fetch('data.json')
.then(function(res) {
return res.json();
})
])
.then(function(dataArray) {
var h = function(tag, attrs, children){
var el = document.createElement(tag);

Object.keys(attrs).forEach(function(key){
var val = attrs[key];

el.setAttribute(key, val);
});

children.forEach(function(child){
el.appendChild(child);
});

return el;
};

var t = function(text){
var el = document.createTextNode(text);

return el;
};

var $ = document.querySelector.bind(document);

var cy = window.cy = cytoscape({
container: document.getElementById('cy'),
style: dataArray[0],
elements: dataArray[1],
layout: { name: 'grid' } // fixed, non-equal positions ensure deterministic fcose results
});

// values modified by sliders
var nodeRepulsionVal = 4500;
var idealEdgeLengthVal = 1;

var params = {
name: 'fcose',
nodeRepulsion: n => nodeRepulsionVal,
idealEdgeLength: e => idealEdgeLengthVal / e.data('weight'),
animate: true,
randomize: false
};
var layout = makeLayout({ animate: false });

layout.run();

var $btnParam = h('div', {
'class': 'param'
}, []);

var $config = $('#config');

$config.appendChild( $btnParam );

var sliders = [
{
label: 'Edge length',
update: sliderVal => idealEdgeLengthVal = sliderVal,
initVal: idealEdgeLengthVal,
min: 1,
max: 5,
step: 0.1
},

{
label: 'Node repulsion',
update: sliderVal => nodeRepulsionVal = sliderVal,
initVal: nodeRepulsionVal,
min: 4500,
max: 1000000,
step: 1
}
];

var buttons = [
{
label: h('span', { 'class': 'fa fa-random' }, []),
layoutOpts: {
randomize: true,
animate: true
}
},

{
label: h('span', { 'class': 'fa fa-play' }, []),
layoutOpts: {
randomize: false,
animate: true
}
}
];

sliders.forEach( makeSlider );

buttons.forEach( makeButton );

function makeLayout( opts ){
params.randomize = (opts || {}).randomize || false;

for( var i in opts ){
params[i] = opts[i];
}

return cy.layout( Object.assign({}, params) );
}

function makeSlider( opts ){
var $input = h('input', {
id: 'slider-'+opts.param,
type: 'range',
min: opts.min,
max: opts.max,
step: opts.step,
value: opts.initVal,
'class': 'slider'
}, []);

var $param = h('div', { 'class': 'param' }, []);

var $label = h('label', { 'class': 'label label-default', for: 'slider-'+opts.param }, [ t(opts.label) ]);

$param.appendChild( $label );
$param.appendChild( $input );

$config.appendChild( $param );

var update = _.throttle(function(){
opts.update(parseFloat($input.value));

layout.stop();
layout = makeLayout({ animate: true });
layout.run();
}, 1000/4, { trailing: true });

$input.addEventListener('input', update);
$input.addEventListener('change', update);
}

function makeButton( opts ){
var $button = h('button', { 'class': 'btn btn-default' }, [ opts.label ]);

$btnParam.appendChild( $button );

$button.addEventListener('click', function(){
layout.stop();

layout = makeLayout( opts.layoutOpts );
layout.run();
});
}

var makeTippy = function(node, html){
return tippy( node.popperRef(), {
html: html,
trigger: 'manual',
arrow: true,
placement: 'bottom',
hideOnClick: false,
interactive: true
} ).tooltips[0];
};

var hideTippy = function(node){
var tippy = node.data('tippy');

if(tippy != null){
tippy.hide();
}
};

var hideAllTippies = function(){
cy.nodes().forEach(hideTippy);
};

cy.on('tap', function(e){
if(e.target === cy){
hideAllTippies();
}
});

cy.on('tap', 'edge', function(e){
hideAllTippies();
});

cy.on('zoom pan', function(e){
hideAllTippies();
});

cy.nodes().forEach(function(n){
var g = n.data('name');

var $links = [
{
name: 'GeneCard',
url: 'http://www.genecards.org/cgi-bin/carddisp.pl?gene=' + g
},
{
name: 'UniProt search',
url: 'http://www.uniprot.org/uniprot/?query='+ g +'&fil=organism%3A%22Homo+sapiens+%28Human%29+%5B9606%5D%22&sort=score'
},
{
name: 'GeneMANIA',
url: 'http://genemania.org/search/human/' + g
}
].map(function( link ){
return h('a', { target: '_blank', href: link.url, 'class': 'tip-link' }, [ t(link.name) ]);
});

var tippy = makeTippy(n, h('div', {}, $links));

n.data('tippy', tippy);

n.on('click', function(e){
tippy.show();

cy.nodes().not(n).forEach(hideTippy);
});
});

$('#config-toggle').addEventListener('click', function(){
$('body').classList.toggle('config-closed');

cy.resize();
});

});
144 changes: 144 additions & 0 deletions documentation/demos/fcose-gene/cy-style.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
[{
"selector": "core",
"style": {
"selection-box-color": "#AAD8FF",
"selection-box-border-color": "#8BB0D0",
"selection-box-opacity": "0.5"
}
}, {
"selector": "node",
"style": {
"width": "mapData(score, 0, 0.006769776522008331, 20, 60)",
"height": "mapData(score, 0, 0.006769776522008331, 20, 60)",
"content": "data(name)",
"font-size": "12px",
"text-valign": "center",
"text-halign": "center",
"background-color": "#555",
"text-outline-color": "#555",
"text-outline-width": "2px",
"color": "#fff",
"overlay-padding": "6px",
"z-index": "10"
}
}, {
"selector": "node[?attr]",
"style": {
"shape": "rectangle",
"background-color": "#aaa",
"text-outline-color": "#aaa",
"width": "16px",
"height": "16px",
"font-size": "6px",
"z-index": "1"
}
}, {
"selector": "node[?query]",
"style": {
"background-clip": "none",
"background-fit": "contain"
}
}, {
"selector": "node:selected",
"style": {
"border-width": "6px",
"border-color": "#AAD8FF",
"border-opacity": "0.5",
"background-color": "#77828C",
"text-outline-color": "#77828C"
}
}, {
"selector": "edge",
"style": {
"curve-style": "haystack",
"haystack-radius": "0.5",
"opacity": "0.4",
"line-color": "#bbb",
"width": "mapData(weight, 0, 1, 1, 8)",
"overlay-padding": "3px"
}
}, {
"selector": "node.unhighlighted",
"style": {
"opacity": "0.2"
}
}, {
"selector": "edge.unhighlighted",
"style": {
"opacity": "0.05"
}
}, {
"selector": ".highlighted",
"style": {
"z-index": "999999"
}
}, {
"selector": "node.highlighted",
"style": {
"border-width": "6px",
"border-color": "#AAD8FF",
"border-opacity": "0.5",
"background-color": "#394855",
"text-outline-color": "#394855"
}
}, {
"selector": "edge.filtered",
"style": {
"opacity": "0"
}
}, {
"selector": "edge[group=\"coexp\"]",
"style": {
"line-color": "#d0b7d5"
}
}, {
"selector": "edge[group=\"coloc\"]",
"style": {
"line-color": "#a0b3dc"
}
}, {
"selector": "edge[group=\"gi\"]",
"style": {
"line-color": "#90e190"
}
}, {
"selector": "edge[group=\"path\"]",
"style": {
"line-color": "#9bd8de"
}
}, {
"selector": "edge[group=\"pi\"]",
"style": {
"line-color": "#eaa2a2"
}
}, {
"selector": "edge[group=\"predict\"]",
"style": {
"line-color": "#f6c384"
}
}, {
"selector": "edge[group=\"spd\"]",
"style": {
"line-color": "#dad4a2"
}
}, {
"selector": "edge[group=\"spd_attr\"]",
"style": {
"line-color": "#D0D0D0"
}
}, {
"selector": "edge[group=\"reg\"]",
"style": {
"line-color": "#D0D0D0"
}
}, {
"selector": "edge[group=\"reg_attr\"]",
"style": {
"line-color": "#D0D0D0"
}
}, {
"selector": "edge[group=\"user\"]",
"style": {
"line-color": "#f0ec86"
}
}]
Loading

0 comments on commit ee4c829

Please sign in to comment.