;(function() { 'use strict'; sigma.utils.pkg('sigma.webgl.edges'); /** * This edge renderer will display edges as arrows going from the source node * to the target node. To deal with edge thicknesses, the lines are made of * three triangles: two forming rectangles, with the gl.TRIANGLES drawing * mode. * * It is expensive, since drawing a single edge requires 9 points, each * having a lot of attributes. */ sigma.webgl.edges.arrow = { POINTS: 9, ATTRIBUTES: 11, addEdge: function(edge, source, target, data, i, prefix, settings) { var w = (edge[prefix + 'size'] || 1) / 2, x1 = source[prefix + 'x'], y1 = source[prefix + 'y'], x2 = target[prefix + 'x'], y2 = target[prefix + 'y'], targetSize = target[prefix + 'size'], color = edge.color; if (!color) switch (settings('edgeColor')) { case 'source': color = source.color || settings('defaultNodeColor'); break; case 'target': color = target.color || settings('defaultNodeColor'); break; default: color = settings('defaultEdgeColor'); break; } // Normalize color: color = sigma.utils.floatColor(color); data[i++] = x1; data[i++] = y1; data[i++] = x2; data[i++] = y2; data[i++] = w; data[i++] = targetSize; data[i++] = 0.0; data[i++] = 0.0; data[i++] = 0.0; data[i++] = 0.0; data[i++] = color; data[i++] = x2; data[i++] = y2; data[i++] = x1; data[i++] = y1; data[i++] = w; data[i++] = targetSize; data[i++] = 1.0; data[i++] = 1.0; data[i++] = 0.0; data[i++] = 0.0; data[i++] = color; data[i++] = x2; data[i++] = y2; data[i++] = x1; data[i++] = y1; data[i++] = w; data[i++] = targetSize; data[i++] = 1.0; data[i++] = 0.0; data[i++] = 0.0; data[i++] = 0.0; data[i++] = color; data[i++] = x2; data[i++] = y2; data[i++] = x1; data[i++] = y1; data[i++] = w; data[i++] = targetSize; data[i++] = 1.0; data[i++] = 0.0; data[i++] = 0.0; data[i++] = 0.0; data[i++] = color; data[i++] = x1; data[i++] = y1; data[i++] = x2; data[i++] = y2; data[i++] = w; data[i++] = targetSize; data[i++] = 0.0; data[i++] = 1.0; data[i++] = 0.0; data[i++] = 0.0; data[i++] = color; data[i++] = x1; data[i++] = y1; data[i++] = x2; data[i++] = y2; data[i++] = w; data[i++] = targetSize; data[i++] = 0.0; data[i++] = 0.0; data[i++] = 0.0; data[i++] = 0.0; data[i++] = color; // Arrow head: data[i++] = x2; data[i++] = y2; data[i++] = x1; data[i++] = y1; data[i++] = w; data[i++] = targetSize; data[i++] = 1.0; data[i++] = 0.0; data[i++] = 1.0; data[i++] = -1.0; data[i++] = color; data[i++] = x2; data[i++] = y2; data[i++] = x1; data[i++] = y1; data[i++] = w; data[i++] = targetSize; data[i++] = 1.0; data[i++] = 0.0; data[i++] = 1.0; data[i++] = 0.0; data[i++] = color; data[i++] = x2; data[i++] = y2; data[i++] = x1; data[i++] = y1; data[i++] = w; data[i++] = targetSize; data[i++] = 1.0; data[i++] = 0.0; data[i++] = 1.0; data[i++] = 1.0; data[i++] = color; }, render: function(gl, program, data, params) { var buffer; // Define attributes: var positionLocation1 = gl.getAttribLocation(program, 'a_pos1'), positionLocation2 = gl.getAttribLocation(program, 'a_pos2'), thicknessLocation = gl.getAttribLocation(program, 'a_thickness'), targetSizeLocation = gl.getAttribLocation(program, 'a_tSize'), delayLocation = gl.getAttribLocation(program, 'a_delay'), minusLocation = gl.getAttribLocation(program, 'a_minus'), headLocation = gl.getAttribLocation(program, 'a_head'), headPositionLocation = gl.getAttribLocation(program, 'a_headPosition'), colorLocation = gl.getAttribLocation(program, 'a_color'), resolutionLocation = gl.getUniformLocation(program, 'u_resolution'), matrixLocation = gl.getUniformLocation(program, 'u_matrix'), matrixHalfPiLocation = gl.getUniformLocation(program, 'u_matrixHalfPi'), matrixHalfPiMinusLocation = gl.getUniformLocation(program, 'u_matrixHalfPiMinus'), ratioLocation = gl.getUniformLocation(program, 'u_ratio'), nodeRatioLocation = gl.getUniformLocation(program, 'u_nodeRatio'), arrowHeadLocation = gl.getUniformLocation(program, 'u_arrowHead'), scaleLocation = gl.getUniformLocation(program, 'u_scale'); buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); gl.uniform2f(resolutionLocation, params.width, params.height); gl.uniform1f( ratioLocation, params.ratio / Math.pow(params.ratio, params.settings('edgesPowRatio')) ); gl.uniform1f( nodeRatioLocation, Math.pow(params.ratio, params.settings('nodesPowRatio')) / params.ratio ); gl.uniform1f(arrowHeadLocation, 5.0); gl.uniform1f(scaleLocation, params.scalingRatio); gl.uniformMatrix3fv(matrixLocation, false, params.matrix); gl.uniformMatrix2fv( matrixHalfPiLocation, false, sigma.utils.matrices.rotation(Math.PI / 2, true) ); gl.uniformMatrix2fv( matrixHalfPiMinusLocation, false, sigma.utils.matrices.rotation(-Math.PI / 2, true) ); gl.enableVertexAttribArray(positionLocation1); gl.enableVertexAttribArray(positionLocation2); gl.enableVertexAttribArray(thicknessLocation); gl.enableVertexAttribArray(targetSizeLocation); gl.enableVertexAttribArray(delayLocation); gl.enableVertexAttribArray(minusLocation); gl.enableVertexAttribArray(headLocation); gl.enableVertexAttribArray(headPositionLocation); gl.enableVertexAttribArray(colorLocation); gl.vertexAttribPointer(positionLocation1, 2, gl.FLOAT, false, this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 0 ); gl.vertexAttribPointer(positionLocation2, 2, gl.FLOAT, false, this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 8 ); gl.vertexAttribPointer(thicknessLocation, 1, gl.FLOAT, false, this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 16 ); gl.vertexAttribPointer(targetSizeLocation, 1, gl.FLOAT, false, this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 20 ); gl.vertexAttribPointer(delayLocation, 1, gl.FLOAT, false, this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 24 ); gl.vertexAttribPointer(minusLocation, 1, gl.FLOAT, false, this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 28 ); gl.vertexAttribPointer(headLocation, 1, gl.FLOAT, false, this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 32 ); gl.vertexAttribPointer(headPositionLocation, 1, gl.FLOAT, false, this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 36 ); gl.vertexAttribPointer(colorLocation, 1, gl.FLOAT, false, this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 40 ); gl.drawArrays( gl.TRIANGLES, params.start || 0, params.count || (data.length / this.ATTRIBUTES) ); }, initProgram: function(gl) { var vertexShader, fragmentShader, program; vertexShader = sigma.utils.loadShader( gl, [ 'attribute vec2 a_pos1;', 'attribute vec2 a_pos2;', 'attribute float a_thickness;', 'attribute float a_tSize;', 'attribute float a_delay;', 'attribute float a_minus;', 'attribute float a_head;', 'attribute float a_headPosition;', 'attribute float a_color;', 'uniform vec2 u_resolution;', 'uniform float u_ratio;', 'uniform float u_nodeRatio;', 'uniform float u_arrowHead;', 'uniform float u_scale;', 'uniform mat3 u_matrix;', 'uniform mat2 u_matrixHalfPi;', 'uniform mat2 u_matrixHalfPiMinus;', 'varying vec4 color;', 'void main() {', // Find the good point: 'vec2 pos = normalize(a_pos2 - a_pos1);', 'mat2 matrix = (1.0 - a_head) *', '(', 'a_minus * u_matrixHalfPiMinus +', '(1.0 - a_minus) * u_matrixHalfPi', ') + a_head * (', 'a_headPosition * u_matrixHalfPiMinus * 0.6 +', '(a_headPosition * a_headPosition - 1.0) * mat2(1.0)', ');', 'pos = a_pos1 + (', // Deal with body: '(1.0 - a_head) * a_thickness * u_ratio * matrix * pos +', // Deal with head: 'a_head * u_arrowHead * a_thickness * u_ratio * matrix * pos +', // Deal with delay: 'a_delay * pos * (', 'a_tSize / u_nodeRatio +', 'u_arrowHead * a_thickness * u_ratio', ')', ');', // Scale from [[-1 1] [-1 1]] to the container: 'gl_Position = vec4(', '((u_matrix * vec3(pos, 1)).xy /', 'u_resolution * 2.0 - 1.0) * vec2(1, -1),', '0,', '1', ');', // Extract the color: 'float c = a_color;', 'color.b = mod(c, 256.0); c = floor(c / 256.0);', 'color.g = mod(c, 256.0); c = floor(c / 256.0);', 'color.r = mod(c, 256.0); c = floor(c / 256.0); color /= 255.0;', 'color.a = 1.0;', '}' ].join('\n'), gl.VERTEX_SHADER ); fragmentShader = sigma.utils.loadShader( gl, [ 'precision mediump float;', 'varying vec4 color;', 'void main(void) {', 'gl_FragColor = color;', '}' ].join('\n'), gl.FRAGMENT_SHADER ); program = sigma.utils.loadProgram(gl, [vertexShader, fragmentShader]); return program; } }; })();