flowsLayer = ({
id: 'flows',
type: 'custom',
onAdd: function (map, gl) {
this.program = gl.createProgram();
gl.attachShader(this.program, compileShader(gl, gl.VERTEX_SHADER, `
precision highp float;
uniform mat4 u_matrix;
attribute vec3 a_origin;
attribute vec3 a_dest;
attribute vec4 a_color;
attribute vec3 a_square_vert;
varying vec2 v_square_pos;
varying vec4 v_color;
void main() {
v_square_pos = a_square_vert.xy; // position within [-1, 1] square
v_color = a_color;
gl_Position = u_matrix * vec4(mix(a_origin.xy, a_dest.xy, v_square_pos.x), 0.0, 1.0) +
vec4(a_square_vert.x, 10.0*a_square_vert.y, 0.0, 0.0);
}
`));
gl.attachShader(this.program, compileShader(gl, gl.FRAGMENT_SHADER, `
precision highp float;
varying vec2 v_square_pos;
varying vec4 v_color;
void main() {
gl_FragColor = v_color;
}
`));
gl.linkProgram(this.program);
this.aSquareVert = gl.getAttribLocation(this.program, "a_square_vert");
this.squareVerts = new Float32Array([
0, -1, 0,
0, 1, 0,
1, -1, 0,
1, 1, 0,
]);
this.squareVertsBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.squareVertsBuffer);
gl.bufferData(gl.ARRAY_BUFFER, this.squareVerts, gl.STATIC_DRAW);
// Flows
this.aOrigin = gl.getAttribLocation(this.program, "a_origin");
this.aDest = gl.getAttribLocation(this.program, "a_dest");
this.aColor = gl.getAttribLocation(this.program, "a_color");
const originCoords = new Float32Array(flows.length * 3);
const destCoords = new Float32Array(flows.length * 3);
const colors = new Float32Array(flows.length * 4);
for (let i = 0; i < flows.length; i++) {
const { origin, dest, count } = flows[i];
originCoords[i * 3] = origin.x;
originCoords[i * 3 + 1] = origin.y;
originCoords[i * 3 + 2] = i;
destCoords[i * 3] = dest.x;
destCoords[i * 3 + 1] = dest.y;
destCoords[i * 3 + 2] = i;
const {r,g,b} = d3.rgb(flowColorScale(count));
colors[i * 4] = r/255;
colors[i * 4 + 1] = g/255;
colors[i * 4 + 2] = b/255;
colors[i * 4 + 3] = flowOpacityScale(count);
}
this.originCoordsBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.originCoordsBuffer);
gl.bufferData(gl.ARRAY_BUFFER, originCoords, gl.STATIC_DRAW);
this.destCoordsBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.destCoordsBuffer);
gl.bufferData(gl.ARRAY_BUFFER, destCoords, gl.STATIC_DRAW);
this.colorsBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.colorsBuffer);
gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
},
render: function (gl, matrix) {
gl.useProgram(this.program);
gl.uniformMatrix4fv(gl.getUniformLocation(this.program, "u_matrix"), false, matrix);
// Square vertices
gl.bindBuffer(gl.ARRAY_BUFFER, this.squareVertsBuffer);
gl.enableVertexAttribArray(this.squareVertsBuffer);
gl.vertexAttribPointer(this.aSquareVert, 3, gl.FLOAT, false, 0, 0);
gl.vertexAttribDivisor(this.aSquareVert, 0); // non-instanced
// Flows
gl.bindBuffer(gl.ARRAY_BUFFER, this.originCoordsBuffer);
gl.enableVertexAttribArray(this.aOrigin);
gl.vertexAttribPointer(this.aOrigin, 3, gl.FLOAT, false, 0, 0);
gl.vertexAttribDivisor(this.aOrigin, 1); // instanced
gl.bindBuffer(gl.ARRAY_BUFFER, this.destCoordsBuffer);
gl.enableVertexAttribArray(this.aDest);
gl.vertexAttribPointer(this.aDest, 3, gl.FLOAT, false, 0, 0);
gl.vertexAttribDivisor(this.aDest, 1); // instanced
gl.bindBuffer(gl.ARRAY_BUFFER, this.colorsBuffer);
gl.enableVertexAttribArray(this.aColor);
gl.vertexAttribPointer(this.aColor, 4, gl.FLOAT, false, 0, 0);
gl.vertexAttribDivisor(this.aColor, 1); // instanced
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.drawArraysInstanced(gl.TRIANGLE_STRIP, 0, this.squareVerts.length / 3, flows.length);
}
})