VivaGraphJS icon indicating copy to clipboard operation
VivaGraphJS copied to clipboard

Bezier curve link webGL

Open Matthi0uw opened this issue 10 years ago • 9 comments

I would like to have a visualization with bezier curves instead of line between the links. In the example "07 - Dual Show Links", there is an implementation of a curve between two nodes with svg render.

My graph can be up to 15k nodes, so I have to use webGL part of your code.

Have you an idea to have curve on webGL render ?

Matthi0uw avatar Mar 17 '14 14:03 Matthi0uw

That would be really a nice add... vivagraph has very rudimentary webgl support, adding bezier curve is feasible but is not straightforward.

anvaka avatar Mar 18 '14 04:03 anvaka

Thanks for the quick answer, I can give it a try. But can you tell me where I can access the vertices positions (x,y) coordinates ?

Matthi0uw avatar Mar 18 '14 15:03 Matthi0uw

Sure, webgl graphics receives a link position when link is added, this happens in webglGraphics.js.

By default links in webgl are rendered by a program: a combination of vertex and fragment shaders. In vivagraph program implements position() method, which accepts position for a link.

Example of how to create custom node program is available in webglCustomNode.html. Link program could be implemented in a similar way

anvaka avatar Mar 19 '14 03:03 anvaka

Okay, so the method position() returns a table with the coordinates of each node ? 1st element is x of 1st node 2nd element is y of 1st node 3rd element is z of 1st node 4th element is x of 2nd node 5th element is y of 2nd node 6nd element is z of 2nd node 7nd element is x of 3rd node ...

I found an example of webGL with circles : on this site and try to use their function on your code. There is a function arc() to calculate the path of the curve approximation between 2 nodes:

function arc(cx, cy, r, a, b, n) {
    var angle = a * Math.PI / 180;
    var dA = (b * Math.PI / 180) / n;
    var points = [];
    for (var i = 0; i <= n; i++) {
        var x = cx + r * Math.cos(angle + i * dA);
        var y = cy + r * Math.sin(angle + i * dA);
        points.push(x, y, 0);
    }   
    return points;
}

I've made a fiddlejs to try a example with 1 node on the curve. I display in console the coordinates of nodes : there is a bug in my function because coordinates of start are not good. It should represent my link (even if position is false), but I do not see it. Maybe I do not use the render () function in the right way

Matthi0uw avatar Mar 19 '14 14:03 Matthi0uw

The reason you're not seeing the lines is due to your points.push- the third argument is color, and you've specified 0. Swap that with positions[2] and I think you should get the color of the from position.

Having done that, though, all I'm seeing is a straight line, positioned incorrectly. Since I'm a complete amateur with glsl / webgl in general, I'm guessing it has to do with some sort of mismatch in array buffer size, perhaps?

dzdrazil avatar Apr 16 '14 17:04 dzdrazil

Found the other problem, yet another silly mistake- instead of

gl.drawArrays(gl.LINES, 0, ap.length/3)

you should be doing

gl.drawArrays(gl.LINE_STRIP, 0, ap.length/3)

. That'll draw a line between each vertex, rather than drawing one line per vertex pairs. From there, it's just a matter of getting the maths right so that the curve starts and stops at the two points.

dzdrazil avatar Apr 17 '14 16:04 dzdrazil

Thank you for your anwser @dzdrazil ! I have update my fiddle with your advice : this is an early response to my initial problem. I can draw a circle arc for the 1st link, but not for all... Have you any idea to draw all links ?

Matthi0uw avatar Apr 28 '14 15:04 Matthi0uw

Here is my new fiddle update with Bezier quadratic curves and cubic curves with 2 control points (see on wikipedia). I wrote the function arc() called in render of Viva.Graph.View.webglLinkProgram :

/* function to draw links:
    positions : position of all nodes in the graph
    n : 1 = line / 2 = quadratic curve / 3 = cubic curve
    m : number of point on the curve
*/
function arc(positions, n, m) {
    for (var j = 0; j < positions.length; j++){
        if (positions[j] != positions[j+3] || positions[j+1] != positions[j+4]){
            points = [];
            pointsMid = [];
            var dx = positions[j] - positions[j+3],
                dy = positions[j+1] - positions[j+4],
                cx = (positions[j]+positions[j+3])/2,
                cy = (positions[j+1]+positions[j+4])/2,
                a = Math.atan2(dy, dx) * 180 / Math.PI,
                r = Math.sqrt(dx * dx + dy * dy)/2,
                angle = a * Math.PI / 180,
                dA = Math.PI / n;

            for (var i = 0; i <= n; i++) {
                var x = cx + r * Math.cos(angle + i * dA);
                var y = cy + r * Math.sin(angle + i * dA);
                pointsMid.push(x, y,0.2);
            }
            if (n == 1){
                points = pointsMid;
            }else if (n == 2){
                for (var k = 0; k <= m; k++){
                    t = k / m;
                    Ax = ( (1 - t) * pointsMid[0] ) + (t * pointsMid[3]);
                    Ay = ( (1 - t) * pointsMid[1] ) + (t * pointsMid[4]);
                    Bx = ( (1 - t) * pointsMid[3] ) + (t * pointsMid[6]);
                    By = ( (1 - t) * pointsMid[4] ) + (t * pointsMid[7]);
                    x = ( (1 - t) * Ax ) + (t * Bx);
                    y = ( (1 - t) * Ay ) + (t * By);
                    points.push(x, y,0.2);
                }
            } else if (n == 3){
                for (var k = 0; k <= m; k++){
                    t = k / m;
                    Px0 = pointsMid[0];
                    Px1 = pointsMid[3];
                    Px2 = pointsMid[6];
                    Px3 = pointsMid[9];
                    Py0 = pointsMid[1];
                    Py1 = pointsMid[4];
                    Py2 = pointsMid[7];
                    Py3 = pointsMid[10];
                    x = Px0*(1-t)*(1-t)*(1-t) + 3*Px1*t*(1-t)*(1-t) + 3*Px2*t*t*(1-t) + Px3*t*t*t;
                    y = Py0*(1-t)*(1-t)*(1-t) + 3*Py1*t*(1-t)*(1-t) + 3*Py2*t*t*(1-t) + Py3*t*t*t;
                    points.push(x, y,0.2);
                }
            }
            gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(points), gl.DYNAMIC_DRAW);

            if (sizeDirty) {
                sizeDirty = false;
                gl.uniformMatrix4fv(locations.transform, false, transform);
                gl.uniform2f(locations.screenSize, width, height);
            }

            gl.vertexAttribPointer(locations.vertexPos, 2, gl.FLOAT, false, 3 * Float32Array.BYTES_PER_ELEMENT, 0);
            gl.vertexAttribPointer(locations.color, 4, gl.UNSIGNED_BYTE, true, 3 * Float32Array.BYTES_PER_ELEMENT, 2 * 4);
            // param : mode , 0, numItems
            gl.drawArrays(gl.LINE_STRIP, 0, points.length/3)
        }
        j += 5;
    }
}

In pointsMid array, I put 3 nodes :

  • the starting node A
  • the end node C
  • the intermediate node B. B is a point lying on the intersection of a circle (having as center the middle of the segment AC and with radius AC/2) and a straight line (perpendicular to the segment AC through B).

In points array, I put m nodes (m defined in parameters) that are in the path of the curve.

Then I simply display these nodes.

Currently I have 3 problems:

  • How do you manage the link color? When I push points, I use this points.push(x, y,0.15); . The third value is used to add colors, but I don’t really understand how it works, for instance 0.1 = cyan / blue = 0.2 / 0 and 0.5 hide the links, is there a way to have some RGB colors in there?
  • As curves do not exist webGL, I create m links (m corresponding to the parameter value of the arc function. Regarding performance, the rendering becomes very slow with m times fewer nodes on the screen... Do you have any idea on how to improve it?
  • Also, how can I add this feature to vivagraphJS properly and to have access to n parameter? (at the moment, I've put it in "Viva.Graph.View.webglLinkProgram" but I am not sure it is the correct location for this part of the code)

Matthi0uw avatar Apr 30 '14 09:04 Matthi0uw

Can you do straight dynamic ribbons, like Almende does?

valtih1978 avatar Feb 12 '15 16:02 valtih1978