Cytoscape.js zigzag like edges - javascript

I am trying to create a zigzag like edges in cytoscape.js. I have found zigzag edges on github but I guess it's not yet implemented or it's abandoned, so I am trying to calculate the points but I am having some trouble.
My graph looks like this:
I assume that there are 3 cases here. One when the source is on the left of the target, other when the source and target have same x value and the third case when the source is on the right of the target.
The second case is basic and I have calculated it (just a straight line from source to the target) but I am having trouble calculating the other two cases.
I am trying to make the edges look like this:
I am using the following function to try to calculate them:
cy.elements('edge').css({'segment-distances':
function(ele){
var target = ele.target();
var source = ele.source();
var source_id = source.data().id;
var target_id = target.data().id;
var sou_pos_x = (cy.$('#' + source_id).position('x'));
var tar_pos_x = (cy.$('#' + target_id).position('x'));
var sou_pos_y = (cy.$('#' + source_id).position('y'));
var tar_pos_y = (cy.$('#' + target_id).position('y'));
var sou_tar_distance_x = (sou_pos_x - tar_pos_x);
var sou_tar_distance_y = (sou_pos_y - tar_pos_y);
// source on the left of the target
if (sou_pos_x < tar_pos_x){
return 0;
}
// source on same position as target
else if (sou_pos_x == tar_pos_x){
return 0;
}
// source on the right of the target
else{
return 0;
}
}
});
From what I have read from the documentation on segments-edges, I should have 2 points.
The calculation would have been a lot easier if you could specify coordinates for each segment point. With the way that is right now it's hard to calculate the values perpendicular to the line formed from the source to the target because those values might vary depending how far apart is the source from the target node
Can anyone help me with this issue or at least give me a guidance on how to achieve what I am looking for? I have been stuck with this problem for quite some time.
EDIT
I have added:
'source-endpoint': '180deg',
'target-endpoint': '0deg'
so now the all the edges start and end from same position, which would simplify the calculation.
EDIT 2:
This has been implemented by taxi edges like this

Related

Create reversed mxHierarchical layout in mxgraph

Greetings to the community! I am currently creating a custom mxgraph editor and I am converting the graphs using different layouts. I would like to convert my graph layout into an mxHierarchicalLayout but reversed (instead of top-to-down I would like it to be down-to-top). Example of what I do and what I would like to do.
My graph
My graph converted with mxHierarchicalLayout
Code snippet:
let layout = new mxHierarchicalLayout(graph);
layout.execute(graph.getDefaultParent());
How I want to convert the graph
I found in the mxHierarchicalLayout that there is an orientation member variable which is by default 'NORTH'. However when I tried to do the following
let layout = new mxHierarchicalLayout(graph);
layout.orientation=mxConstants.DIRECTION_SOUTH;
layout.execute(graph.getDefaultParent());
The graph went out of my "canvas" and cannot be seen to check if this is the responsible parameter to "reverse" the tree. Could anyone help ? Thanks in advance.
PS
When I use layout.orientation = mxConstants.DIRECTION_SOUTH;
it seems that the layout is converted as I want to but cannot be seen because the coordinates of the svg elements are of type x=-10, y=-5 (negatives), any workaround for this?
SOLVED
I do not know why mxgraph has that buggy behaviour with orientation = south in mxHierarchicalLayout but I managed to create a workaround solution for my problem since I realized that the newly generated layout put my mxCell children objects of the graph to the north ( negative y coordinate). So what I did was after layout.execute(graph.getDefaultParent()); I get each and every child of the graph and retrieve the most negative y coordinate and then move all the cells of the graph from their new coordinates to a new incremented by the absolute value of the most negative y-coordinated element.
Code
function convertGraphToInverseHorizontalTree(){
let layout = new mxHierarchicalLayout(graph);
layout.orientation = mxConstants.DIRECTION_SOUTH;
layout.execute(graph.getDefaultParent());
graph.model.beginUpdate();
//get the most negative y
let mostNegativeY = getMostNegativeCoordinateY(graph);
let children = graph.getChildCells();
graph.moveCells(children, undefined , Math.abs(mostNegativeY));
graph.model.endUpdate();
}
function getMostNegativeCoordinateY(graph){
let children = graph.getChildCells();
let mostNegative = 10000;
for(let i = 0; i < children.length; i++){
if(children[i].geometry != undefined){
if(children[i].geometry.y < mostNegative){
mostNegative = children[i].geometry.y;
}
}
}
return mostNegative;
}
Now the graph is like this

Using Javascript getBoundingClientRect to Snap Items to Grid

EDIT: I've simplified the code (below and in fiddle) down to the major problem needed to be solved in hope of creating more readability.
I've implemented Bader's solution for correctly using getBoundingClientRect value and using document.querySelector for getting both the class name and the html tag needed for the function. I'd now like to move on to the last five lines of the code beginning with var = style.
I've now corrected the math for the final two variables.
→ I'm trying to achieve creating a snapping function for use alongside Plumber, a baseline-grid Sass plugin.
Basically, I have a vertically centered flex item that needs to -- instead of being perfectly centered -- snap in an upward direction to the closest grid line. This will allow me to have a consistent vertical rhythm between slides in a custom mobile-based experience.
I'm using getBoundingClientRect to calculate the distance between the bottom of an object, and the top of the window.
Then I use Math.floor to round down to the nearest multiple of my rem value.
Then I use this new value to create a CSS bottom margin on the flex-centered container for the alignment fix.
(Then to finish, I'd like to have this function load on $(document).ready and on window resize.)
function() {
var box = document.querySelector('.box-1');
var rect = box.getBoundingClientRect();
var bottomOrig = rect.bottom;
var htmlRoot = document.querySelector('html');
var style = getComputedStyle(htmlRoot);
var remValue = style.getPropertyValue('font-size');
var bottomNew = Math.floor(bottomOrig / remValue) * remValue;
var fix = bottomOrig - bottomNew;
$('.container-2').css("margin-bottom", "fix + 'px'");
}
Here's the fiddle.
I most likely have a syntax problem here, and would greatly appreciate help.
Thanks!
Here are some errors / corrections.
GetBoundingClientRect() is a JS function, not jQuery, so it must be used on a javascript element, not a jquery selector. Using the [0] accessor on the jquery selector (if that's how you want to get it) will give you the JS element.
Also noticed that you were trying to select the "html" tag by id, but it doesn't have any Id. Changed it to getElementsByTagName.
var offsetYOrig = $('.box-1')[0].getBoundingClientRect().bottom;
// or, without jQuery:
// var offsetYOrig = document.getElementsByClassName('box-1')[0].getBoundingClientRect().bottom;
var html = document.getElementsByTagName("html")[0];
var style = window.getComputedStyle(html);
var remValue = style.getPropertyValue('font-size');
Edit: Regarding your edit, if you need to call the javascript to recompute on window resize, you may want to try something like this. I'm not sure if it achieves what you want fully (I don't completely understand your 'snapping' requirements, but this will at least call the code again. You may still have to edit the code in the snapFunction if it doesn't suit your needs.
I added some console logs that might help you check your math as it seemed a bit problematic to me, though I was unsure how to fix it because I don't understand your goal.
function snapFunction ()
{
var box = document.querySelector('.box-1');
var rect = box.getBoundingClientRect();
var bottomOrig = rect.bottom;
var htmlRoot = document.querySelector('html');
var style = getComputedStyle(htmlRoot);
var remValue = style.getPropertyValue('font-size');
var bottomNew = Math.floor(bottomOrig / remValue) * remValue;
var fix = bottomOrig - bottomNew;
// open your browser console and check the value of these to check your math and what values you're getting
console.log("bottomOrig: " + bottomOrig )
console.log("remValue: " + remValue)
console.log("bottomNew: " + bottomNew )
// note: no quotes around your variable name fix here
$('.container-2').css("margin-bottom", fix + "px");
};
// call on load
(function() {
snapFunction();
})();
// call on resize
$( window ).resize(function() {
snapFunction();
});
I did notice that the value of your bottomNew variable was logging as "NaN" (Not a Number) so I think something is going wrong there.
I think you're getting a font-size like "36px" instead of just "36". Maybe you could try
var remValue = parseInt(style.getPropertyValue('font-size'), 10);
The 10 in that parseInt function is just specifying we want to use base 10 numbers.
I hope this will help you
Here's the edited fiddle
jsfiddle.net/ztf64mwg/82/
I just edited some variables and fixed some of the errors
I ended up jumping on HackHands and, with help, came up with a great working solution.
This will snap any vertically flex-centered object to a grid with its size set as 1rem.
All you need to do is give the object that is being measured for distance the id attribute "measure", making sure that this object is aligned correctly with a 1rem grid from the top of its own container.
Then give the parent container (or any container higher in the DOM tree) that you'd like to snap to the grid the class of "snap".
If anyone ever finds a use for this and needs further explanation, just let me know.
function snap(object){
var rect = object.getBoundingClientRect();
var bottomOrig = rect.bottom;
var htmlRoot = document.querySelector('html');
var style = getComputedStyle(htmlRoot);
var remValue = parseInt(style.getPropertyValue('font-size'));
var bottomNew = Math.floor(bottomOrig / remValue) * remValue;
var topFixPositive = bottomNew - bottomOrig;
var topFixNegative = -Math.abs(topFixPositive);
$(object).closest('.snap').css("margin-top", topFixNegative);
}
function snapClear(object){
$(object).closest('.snap').css("margin-top", "0");
}
var measureHome = document.querySelector('#measure');
snap(measureHome);
$(window).on('resize', function() {
snapClear(measureHome);
snap(measureHome);
});

How to dynamically reposition binary tree nodes visually

I have been working with the following code: http://bl.ocks.org/NPashaP/7683252. This is a graphical representation of a tree. I have stripped away much of the code (the graceful labels), only allowing two nodes per parent, and changed the data structure to an array.
The only problem left now is the repositioning. The original code does that perfectly. But since I want a binary tree, I have let the user choose to insert a left or right child. The original reposition code animates the first child straight down from the parent, but that would be wrong in a binary tree. I want it to either go to the left or right.
reposition = function (v) {
function repos(v) {
var lC = getLeafCount(v.v),
left = v.p.x; //parent's x-position
v.c.forEach(function (d) {
var vc = d; //saving reference of the child in parent object
d = tree.getVerticeById(d.v); //actually fetching the child object
var w = 0;
if(d.d == 'right') { w += 15 * lC }
if(d.d == 'left') { w -= 15 * lC }
d.p = {x: left + w, y: v.p.y + tree.h}; //setting the position
vc.p = d.p; //setting the child's pos in parent obj
repos(d);
});
}
repos(v[0]);
};
Some of the parts of my code are different from the original code, because I have changed the data structure as stated before. I have tried to comment the parts that may be confusing, but what is important is the math of the repositioning.
At first, this code seemed to work well (https://i.stack.imgur.com/gjzOq.png). But after some testing I discovered a huge problem with the repositioning: the nodes crash with each other (https://i.stack.imgur.com/pdQfy.png)!
Conclusion: I have tried to modify the original function to take the in mind the left and right positioning of nodes, but could not do it. I wrote this variant of the method which does, but it still has some problems as illustrated in the pictures. I would be thankful for some input in this matter.
I eventually did a quick fix for this problem.
After running the reposition function as posted in the question, i ran another function which fixed the problem. The new function goes through all levels of the tree and checks if the nodes are too close together. If they are, the algorithm finds closest common ancestor, and increases the distance between its left and right children. After this, there is no more collisions between nodes.

Draw a line from .Point depending on attribute & without re-sizing with zoom

I am adding points to the map and then giving them a speed attribute
I want to be able to have a line that represents the speed from position 0 of the point e.g. 200 knots = 200 pixels (then some math to keep it at a reasonable length).
I also need it not to be longer/shorter depending on the zoom
What i have so far:
var vlPoint = OpenLayers.Point;
var vlPixel = OpenLayers.Pixel;
var vlSpeed = itemData.mIntendedMovement.mSpeedOverGround;
vlPixel.add(point, vlSpeed);
vlPoint.move(0, vlPixel);
I am very new to OpenLayers and cannot find anything on the OpenLayers dev pages or on SO
Turns out, all it needed was:
var vlPoint = OpenLayers.Point;
var length = (itemData.mIntendedMovement.mSpeedOverGround * this.map.resolution)/10;
vlPoint.move(0, length);
Sometimes its the simple things

Why does my my gravity work in this?

http://davzy.com/gameA/
I can't figure out a smart way to get gravity. Now with this it detects which block the character is over but it does't drop to that block!
Is there a better way to do gravity? I'd like to do this without a game library.
I don't know what you mean by "get gravity"; your question is unclear. I assume that if you can detect when the block is over, you can use the following formula:
s(t) = ut + 1/2at2
Where s is the distance at time t, u is the initial velocity (which in your case would be zero), and a is the acceleration (on Earth this is 9.8m/s2). Essentially you would be adjusting the top position of your object based on the value you get at time t (so original top position of object + s(t)). I would imagine you would use some sort of animation loop. Perhaps a setInterval. Maybe others with more experience in Javascript animation can chime in about the best way to implement this. However, this would be the formula that you would be using to figure out where the object is at time t, if it falls.
Basically gravity in a platformer goes like this:
var currentGrav = 0.0;
var gravAdd = 0.5; // add this every iteration of the game loop to currentGrav
var maxGrav = 4.0; // this caps currentGrav
var charPosY = getCharPosY(); // vertical position of the character, in this case the lower end
var colPosY = getColDow(); // some way to get the vertical position of the next "collision"
for(var i = 0; i < Math.abs(Math.ceil(currentGrav)); i++) { // make sure we have "full pixel" values
if (charPosY == colPosY) {
onGround = true;
break; // we hit the ground
}
onGround = false;
charPosY++;
}
Now to jump one could simply do this:
if (jumpKeyPressed && onGround) {
currentGrav = -5.0; //
}
You can, if you want(and understand C), check out my game for a basic platformer(with moving platforms) here:
http://github.com/BonsaiDen/Norum/blob/master/sources/character.c

Categories

Resources