What's the real meaning of randomize=false using cose-bilkent layout - layout

What the real meaning of randomize=false using cose-bilkent layout ?
I can only read such a simple comment to describe this feature in this extension wiki page.
// Whether to enable incremental mode
randomize: true,
I also find another description for randomize in cytoscape.js-expand-collapse wiki page.
// recommended usage: use cose-bilkent layout with randomize: false to preserve mental map upon expand/collapse
So I have learned that randomize: false is to preserve mental map. But what is the real behavior ?
Suppose that I have run a topology graph using cose-bilkent layout, then call the following snippet to append new nodes and edges to the graph with randomize: false. What will be the expected result on incremental mode ? Should the old nodes and edges keep the relative position as so called mental map , while new nodes and edges should be positioned to a resonable coordinate?
var demoId = 10000;
document.getElementById("append").addEventListener("click", function() {
var tid = cy.nodes()[Math.floor(Math.random()*cy.nodes().length)].data().id;
var sid = ++demoId;
var data = {
nodes : [
{data:{"id":"id"+source, "name":"name"+source}}
],
edges :[
{data:{"id":"id"+source+"-"+target, "source":"id"+source, "target":target}}
]
};
cy.add(data);
cy.layout({
name: 'cose-bilkent',
animate: false,
randomize : false,
fit : false
}).run();
});
See my demo. My test result is that all nodes and edges will be relayout. You could watch some nodes and their neighborhood. The neighborhood will be changed after each layout. I can't get a clear mental map. So could the experts explain how incremental layout task effect here.
document.addEventListener("DOMContentLoaded", function() {
var cy = (window.cy = cytoscape({
container: document.getElementById("cy"),
ready: function() {
this.layout({
name: "cose-bilkent",
animationDuration: 1000
}).run();
},
style: [{
selector: "node",
style: {
'content': 'data(id)',
'text-valign': 'center',
'text-halign': 'center',
'background-color': 'blue',
'color':'red',
'width':'10px',
'height':'10px'
}
},
{
selector: ":parent",
style: {
"background-opacity": 0.333
}
},
{
selector: "edge",
style: {
width: 3,
"line-color": "#ad1a66"
}
}
],
elements: [{"group":"nodes","data":{"id":"p_1","parent":"n_72"}},{"group":"nodes","data":{"id":"p_298","parent":"n_72"}},{"group":"nodes","data":{"id":"p_4","parent":"n_72"}},{"group":"nodes","data":{"id":"p_5","parent":"n_72"}},{"group":"nodes","data":{"id":"p_9","parent":"n_72"}},{"group":"nodes","data":{"id":"p_32","parent":"n_72"}},{"group":"nodes","data":{"id":"p_607","parent":"n_72"}},{"group":"nodes","data":{"id":"p_57","parent":"n_72"}},{"group":"nodes","data":{"id":"p_242","parent":"n_72"}},{"group":"nodes","data":{"id":"p_64","parent":"n_72"}},{"group":"nodes","data":{"id":"p_77","parent":"n_72"}},{"group":"nodes","data":{"id":"p_81","parent":"n_72"}},{"group":"nodes","data":{"id":"p_82","parent":"n_72"}},{"group":"nodes","data":{"id":"p_289","parent":"n_72"}},{"group":"nodes","data":{"id":"p_803","parent":"n_72"}},{"group":"nodes","data":{"id":"n_0"}},{"group":"nodes","data":{"id":"n_72"}},{"group":"edges","data":{"source":"n_0","target":"n_0","id":"n_0-n_0"}},{"group":"edges","data":{"source":"n_0","target":"p_81","id":"n_0-p_81"}},{"group":"edges","data":{"source":"n_0","target":"p_4","id":"n_0-p_4"}},{"group":"edges","data":{"source":"n_0","target":"p_298","id":"n_0-p_298"}},{"group":"edges","data":{"source":"n_0","target":"p_803","id":"n_0-p_803"}},{"group":"edges","data":{"source":"n_0","target":"p_607","id":"n_0-p_607"}},{"group":"edges","data":{"source":"n_0","target":"p_1","id":"n_0-p_1"}},{"group":"edges","data":{"source":"n_0","target":"p_289","id":"n_0-p_289"}},{"group":"edges","data":{"source":"n_0","target":"p_57","id":"n_0-p_57"}},{"group":"edges","data":{"source":"n_0","target":"p_82","id":"n_0-p_82"}},{"group":"edges","data":{"source":"n_0","target":"p_32","id":"n_0-p_32"}},{"group":"edges","data":{"source":"p_77","target":"n_0","id":"p_77-n_0"}},{"group":"edges","data":{"source":"p_64","target":"n_0","id":"p_64-n_0"}},{"group":"edges","data":{"source":"p_607","target":"n_0","id":"p_607-n_0"}},{"group":"edges","data":{"source":"p_5","target":"n_0","id":"p_5-n_0"}},{"group":"edges","data":{"source":"p_9","target":"n_0","id":"p_9-n_0"}},{"group":"edges","data":{"source":"p_9","target":"p_803","id":"p_9-p_803"}},{"group":"edges","data":{"source":"p_242","target":"n_0","id":"p_242-n_0"}}],
}));
var demoId = 10000;
document.getElementById("append").addEventListener("click", function() {
var tid = cy.nodes()[Math.floor(Math.random()*cy.nodes().length)].data().id;
var sid = ++demoId;
var data = {
nodes : [
{data:{"id":sid}}
],
edges :[
{data:{"id":sid+"-"+tid, "source":sid, "target":tid}}
]
};
cy.add(data);
cy.layout({
name: 'cose-bilkent',
animate: false,
randomize : false,
fit : true
}).run();
});
});
body {
font-family: helvetica;
font-size: 14px;
}
#cy {
height: 100%;
width: 90%;
position: absolute;
}
h1 {
opacity: 0.5;
font-size: 1em;
}
button {
margin-right: 10px;
}
<script src="https://unpkg.com/cytoscape/dist/cytoscape.min.js"></script>
<!--polyfills are needed for this extension for old browsers like IE -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/core-js/2.5.7/shim.min.js"></script>
<script src="https://unpkg.com/layout-base/layout-base.js"></script>
<script src="https://unpkg.com/cose-base/cose-base.js"></script>
<script src="https://cdn.jsdelivr.net/npm/cytoscape-cose-bilkent#4.1.0/cytoscape-cose-bilkent.min.js"></script>
<body>
<button id="append" class="button">Append</button>
<div id="cy"></div>
</body>

Related

Empty Div Preventing Interaction with amCharts5 MapChart on Vue3

I decided to dip my toes in Vue and have had an idea for a website for a while which I'd like to use amCharts5 for.
I had some issues initially as all the info I could find was related to Vue2, but I think I've somewhat wrapped my head around Vue3 and its composition API.
The MapChart is created, however there is always a div slapped on top of it which prevent any interaction. If I delete this element via DevTools, the MapChart becomes interactive.
I've tried debugging this and commenting sections of the code out, regardless this div is always created. And I simply can't figure out if it's injected by Vue or if amCharts 5 is the culprit.
The highlighted element is the one I must delete for it to become interactive.
Here's how the component is setup;
<template>
<div class="testClass" ref="chartdiv">
</div>
</template>
<script setup lang="ts">
import * as am5 from "#amcharts/amcharts5";
import * as am5map from "#amcharts/amcharts5/map";
import am5geodata_worldLow from "#amcharts/amcharts5-geodata/worldLow";
import am5themes_Animated from '#amcharts/amcharts5/themes/Animated';
import { ref, onMounted, onUnmounted } from "vue";
const chartdiv = ref<HTMLElement | null>()
var root!: am5.Root;
onMounted(() => {
if (chartdiv.value) {
// Create the Root
var root = am5.Root.new(chartdiv.value);
// Setup the MapChart
var chart = root.container.children.push(
am5map.MapChart.new(root, {
panX: "rotateX",
panY: "rotateY",
projection: am5map.geoOrthographic(),
centerMapOnZoomOut: false
})
);
// Setup Animations
root.setThemes([
am5themes_Animated.new(root)
]);
// Create MapPolygons
var polygonSeries = chart.series.push(
am5map.MapPolygonSeries.new(root, {
geoJSON: am5geodata_worldLow
})
);
// Setup MapPolygon Styling
polygonSeries.mapPolygons.template.setAll({
tooltipText: "{name}",
fill: am5.color("#909090")
});
// Setup MapPolygon Hover Styling
polygonSeries.mapPolygons.template.states.create("hover", {
fill: am5.color("#FF0000"),
stroke: am5.color("#00FF00"),
strokeWidth: 2
});
polygonSeries.mapPolygons.template.events.on("click", function(event) {
//console.log("Clicked: {0}", event.target);
});
// Setup Background
var backgroundSeries = chart.series.unshift(
am5map.MapPolygonSeries.new(root, {})
);
backgroundSeries.mapPolygons.template.setAll({
fill: am5.color(0x2c84d0),
stroke: am5.color(0x2c84d0)
});
backgroundSeries.data.push({
geometry: am5map.getGeoRectangle(90, 180, -90, -180)
});
}
});
onUnmounted(() => {
if (root) {
root.dispose();
}
});
</script>
<style scoped>
.testClass {
width: 50vw;
height: 50vh;
}
</style>
When you create a Vite-powered Vue project, it automatically creates a bunch of CSS files for you. One of those is base.css.
Inside this file, you'll find these lines which causes all the headache;
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
position: relative;
font-weight: normal;
}
Removing those lines will fix the issue.

Phaser 3: Spritesheet doesn't load correctly

I tried to add a spritesheet to my Phaser game the same way I've done it a few times before, however this time it seems to not load the images correctly, causing it to display a black & green square instead and causing it to throw an error when trying to play an animation.
Can anyone tell what is causing this issue?
(Warning: Playing the Code here seems to freeze up your browser for a few seconds, alternatively view on JSFiddle: https://jsfiddle.net/cak3goru/4/ )
// Configs and Constants
const gameState = {
gameWidth: 800,
gameHeight: 800,
textStyle: {
fontFamily: "'Comic Sans MS'",
fill: "#fff",
align: "center",
boundsAlignH: "left",
boundsAlignV: "top"
},
mouseDown: false,
menu: {
options: [
"Feed", "Clean", "Play"
],
barColor: "0x123456",
items: [],
itemText: [],
itemColor: "0x654321",
}
};
function preload() {
// Clean Tools
this.load.atlas('clean', 'https://gaylie.neocities.org/game/assets/clean.png', 'https://gaylie.neocities.org/game/assets/clean.json');
}
function create () {
this.anims.create({
key: "shower_cursor",
framerate: 12,
frames: this.anims.generateFrameNumbers("clean", {
prefix: "showerhead_000",
start: 0,
end: 7}),
repeat: -1
});
gameState.shower_cursor = this.add.sprite(400,400,'shower_cursor');
console.log(gameState.shower_cursor.frame);
//gameState.shower_cursor.playAfterDelay('shower_cursor', 100);
//gameState.shower_cursor.alpha = 0;
}
var config = {
backgroundColor: "0xf0f0f0",
scale: {
width: gameState.gameWidth,
height: gameState.gameHeight,
autoCenter: Phaser.Scale.CENTER_BOTH
},
scene: {
preload, create
}
};
var game = new Phaser.Game(config);
<head>
<title>Pet Simulator</title>
<script src="//cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.min.js"></script>
</head>
<body style="height: 100%; width: 100%; margin: 0;">
</body>
The problem is, that you are using the wrong function, you should use this.anims.generateFrameNames, and not this.anims.generateFrameNumbers.
And set the correct key clean for the sprite.
the line should be:
gameState.shower_cursor = this.add.sprite(200, 100, 'clean');
because shower_cursor, is only the key of the animation, not the key of the sprite.
P.s.: the posted code doesn't run on jsfiddler or Stackoverflow due to CORS-Error, but if all assets are on the same server, it should not be a problem.

Cytoscape Cola Layout: How to restart the layout without any change of positions?

I'm trying to use the Cytoscape cola layout to render a graph that should apply a force directed layout while using it (so when dragging nodes around, they should act as if there is some gravity involved).
Relevant libraries:
https://github.com/cytoscape/cytoscape.js
https://github.com/tgdwyer/WebCola
https://github.com/cytoscape/cytoscape.js-cola
My first problem is that adding nodes to the graph via add(node) doesn't include them in the cola layout algorithm. The only way I found around that is to destroy the layout, re-initialize it and start it again. But this causes the nodes to jump in some cases.
I assumed that this was due to the fact that I completely destroyed the old layout but when setting up a minimal example, I realized that even just calling layout.stop() and layout.run() leads to nodes being repositioned.
In the following example, there is only one node. Moving the node via drag and drop, then pressing the "stop" button and then the "start" button causes the node to jump back to its initial position:
document.addEventListener('DOMContentLoaded', function(){
// Register cola layout
cytoscapeCola(cytoscape);
var nodes = [{ data: { id: 1, name: 1 } }]
var edges = [];
var cy = window.cy = cytoscape({
container: document.getElementById('cy'),
style: [
{
selector: 'node[name]',
style: {
'content': 'data(name)'
}
},
{
selector: 'edge',
style: {
'curve-style': 'bezier',
'target-arrow-shape': 'triangle'
}
},
],
elements: {
nodes: nodes,
edges: edges
}
});
var layout = cy.layout({
name: 'cola',
infinite: true,
fit: false,
});
layout.run();
document.querySelector('#start').addEventListener('click', function() {
layout.run();
});
document.querySelector('#stop').addEventListener('click', function() {
layout.stop();
});
document.querySelector('#add-node').addEventListener('click', function() {
var id = Math.random();
cy.add({ group: 'nodes', data: { id: id, name: id } });
cy.add({ group: 'edges', data: { source: id, target: _.head(nodes).data.id } });
layout.stop();
layout.destroy();
layout = cy.layout({
name: 'cola',
infinite: true,
fit: false,
});
layout.run();
});
});
body {
font-family: helvetica neue, helvetica, liberation sans, arial, sans-serif;
font-size: 14px;
}
#cy {
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
z-index: 999;
}
h1 {
opacity: 0.5;
font-size: 1em;
font-weight: bold;
}
#buttons {
position: absolute;
right: 0;
bottom: 0;
z-index: 99999;
}
<!DOCTYPE>
<html>
<head>
<title>cytoscape-edgehandles.js demo for infinite layout</title>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<script src="https://unpkg.com/cytoscape/dist/cytoscape.min.js"></script>
<script src="https://unpkg.com/webcola/WebCola/cola.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/cytoscape-cola#2.4.0/cytoscape-cola.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.js"></script>
<script src="cytoscape-edgehandles.js"></script>
</head>
<body>
<h1>cytoscape-edgehandles demo with an infinite layout</h1>
<div id="cy"></div>
<div id="buttons">
<button id="start">Start</button>
<button id="stop">Stop</button>
<button id="add-node">Add Node</button>
</div>
</body>
</html>
Is this a bug or am I doing something wrong? Does anyone know how to stop and restart the layout without the nodes changing their position?
Thanks a lot,
Jesse
Okay actually you were very close #Stephan.
The problem was that WebCola centers the nodes when calling start by default:
https://github.com/tgdwyer/WebCola/blob/78a24fc0dbf0b4eb4a12386db9c09b087633267d/src/layout.ts#L504
The cytoscape wrapper for WebCola does not currently support this option, so I forked it and added the option myself:
https://github.com/deje1011/cytoscape.js-cola/commit/f357b97aba900327e12f97b1530c4df624ff9d61
I'll open a pull request at some point.
Now you can smoothly restart the layout like this:
layout.stop();
layout.destroy(); // cleanup event listeners
layout = graph.layout({ name: 'cola', infinite: true, fit: false, centerGraph: false });
layout.run()
This way, the nodes keep their position 🎉

How can I set an Overlay on top when I click on it

I have an Openlayers map with a lot of overlays (Point-coordinates).
These overlays are often very close to each other or overlapping.
When I click on an existing Overlay I want the Overlay to be set on top, so that it is fully seen, not behind any other Overlay.
So far I have only seen that the Layers can be set with an z-index. Is it possible to do that with overlays, too?
I would like to do something like that:
map.setLayerIndex(markers, 99);
but with an overlay
Overlays are controls, which are positioned on an coordinate instead of being in a fixed place. They are basically nothing more but regular html div elements and change position with the map.
This also means, you can apply normal CSS styling and use z-index on them.
var layer = new ol.layer.Tile({
source: new ol.source.OSM()
});
var map = new ol.Map({
layers: [layer],
target: 'map',
view: new ol.View({
center: [0, 0],
zoom: 2
})
});
// Vienna marker
var marker1 = new ol.Overlay({
position: ol.proj.fromLonLat([16.3725, 48.208889]),
positioning: 'center-center',
element: document.getElementById('marker1'),
stopEvent: false,
className: 'm1 ol ol-overlay-container ol-selectable'
});
map.addOverlay(marker1);
marker2 = new ol.Overlay({
position: ol.proj.fromLonLat([23.3725, 48.208889]),
positioning: 'center-center',
element: document.getElementById('marker2'),
stopEvent: false,
className: 'm2 ol ol-overlay-container ol-selectable'
});
map.addOverlay(marker2);
function clicked(selector) {
console.log('clicked overlay', selector);
document.querySelectorAll(".ol").forEach(function(el){
el.classList.remove('active');
});
document.querySelector(selector).classList.add('active');
}
html, body, .map {
min-height: 50px;
min-width: 50px;
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
.marker {
width: 30px;
height: 30px;
border: 1px solid #088;
border-radius: 15px;
background-color: #0FF;
}
.m1 .marker {
background-color: #FF0;
}
.active {
z-index: 1234782904789;
}
.active .marker {
background-color: red;
}
<link href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io#main/dist/en/v7.0.0/legacy/ol.css" rel="stylesheet"/>
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io#main/dist/en/v7.0.0/legacy/ol.js"></script>
<div id="map" class="map"></div>
<div id="marker1" title="Marker" class="marker" onclick="clicked('.m1')"></div>
<div id="marker2" title="Marker" class="marker" onclick="clicked('.m2')"></div>
The existing answer works, but it doesn't preserve the z-order of the overlays, it only guarantees that the clicked one will be on top. Since it is the only element with a z-index in this stacking context, the z-order of the other elements will be random.
Here is a solution that brings the clicked overlay to the front, while preserving the current z-order of all the other ones:
export function bringToFront (map: PluggableMap, clickedOverlayElement: HTMLElement) {
const overlays = map.getOverlays().sort(zIndexComparator);
overlays.forEach((overlay, i) => {
const element = overlay.get('element');
const container = pointInfo.closest('.ol-overlay-container') as HTMLElement;
container.style.zIndex = element === clickedOverlayElement ? overlays.length.toFixed() : i.toFixed();
});
}
function getOverlayContainer (overlay: Overlay) {
return overlay.get('element').closest('.ol-overlay-container') as HTMLElement;
}
function zIndexComparator (a: Overlay, b: Overlay) {
return (getOverlayContainer(a).style.zIndex > getOverlayContainer(b).style.zIndex)
? 1
: -1;
}
Just call the bringToFront() function when your overlay element is clicked.

Border issue with ExtJS hbox Layout

I am trying to develop a simple layout using Hbox and Vbox in ExtJS. But in two boxes (see Relationship and Abbrivation boxes in snap-shot)there are not displaying borders randomly. I can't get any reason. Please help me, if possible. I am giving my screen shot and code snap here.
Screen-Shot
Code
Ext.define('EzRE.view.Viewport', {
extend : 'Ext.container.Viewport',
requires : ['EzRE.view.DashBoard'],
getOneSectionLayout:function(title,isFirst){
var margin = null;
if(isFirst)
{
margin = "10px 10px 10px 10px";
}
else
{
margin = "10px 10px 10px 0px";
}
return{
xtype:'panel',
height:'100%',
flex:1,
style:{
"margin":margin
},
title:title
};
},
getMainLayout:function(title,items){
return{
xtype:'panel',
flex:1,
width:'100%',
title:title,
border:false,
layout:{
type:'hbox',
align:'middle',
pack:'center'
},
items:items
};
},
initComponent:function(){
this.layout={
type:'vbox'
};
var NLP_SEC_ITEMS = [this.getOneSectionLayout('Section Header',true),
this.getOneSectionLayout('Abbrivation',false),
this.getOneSectionLayout('Nagation',false),
this.getOneSectionLayout('Head Rule',false),
this.getOneSectionLayout('Relationship',false),
this.getOneSectionLayout('Overlap',false)];
var KNOWLEDGE_SEC_ITEMS = [this.getOneSectionLayout('Precision Analysis',true),
this.getOneSectionLayout('Recall Analysis W/O Annotation',false),
this.getOneSectionLayout('Recall Analysis With Annotation',false)];
var CODE_SEC_ITEMS = [this.getOneSectionLayout('Code Rule Engine',true),
this.getOneSectionLayout('Code DRG Analysis',false),
this.getOneSectionLayout('Code Associan Analysis',false)];
this.items = [this.getMainLayout('NLP', NLP_SEC_ITEMS),
this.getMainLayout('Knowledge', KNOWLEDGE_SEC_ITEMS),
this.getMainLayout('Code', CODE_SEC_ITEMS)];
this.callParent(arguments);
}
});

Resources