From 37e3a1b061303aaf6e16da34ab0b31c7c7c9b6ca Mon Sep 17 00:00:00 2001 From: Przemek Kaminski <pk@intrepidus.pl> Date: Thu, 13 Oct 2022 14:24:38 +0200 Subject: [PATCH] [sigmajs] screenshot WebGL implementation --- package.json | 3 +- src/Gargantext/Hooks/Sigmax/Sigma.js | 13 +- src/external-deps/sigmajs-screenshot.js | 150 ++++++++++++++++++++++++ yarn.lock | 5 + 4 files changed, 159 insertions(+), 12 deletions(-) create mode 100644 src/external-deps/sigmajs-screenshot.js diff --git a/package.json b/package.json index 639e828bf..72278a103 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,8 @@ "react-dom": "^17.0.2", "react-tooltip": "^4.2.8", "secp256k1": "^4.0.2", - "sigma": "^2.3.1" + "sigma": "^2.3.1", + "twgl.js": "^5.0.4" }, "devDependencies": { "@babel/core": "^7.15.0", diff --git a/src/Gargantext/Hooks/Sigmax/Sigma.js b/src/Gargantext/Hooks/Sigmax/Sigma.js index 9e7aabaa6..bd55643c4 100644 --- a/src/Gargantext/Hooks/Sigmax/Sigma.js +++ b/src/Gargantext/Hooks/Sigmax/Sigma.js @@ -2,6 +2,7 @@ import Graph from 'graphology'; import Sigma from 'sigma'; +import { takeScreenshot } from '../../src/external-deps/sigmajs-screenshot.js'; let sigma = Sigma.Sigma; console.log('imported sigma', Sigma); @@ -192,17 +193,7 @@ function _bindMouseSelectorPlugin(left, right, sig) { function _on(sigma, event, handler) { sigma.on(event, handler); } function _takeScreenshot(sigma) { - let c = sigma.renderers[0].container; - let edges = c.getElementsByClassName('sigma-edges')[0]; - let scene = c.getElementsByClassName('sigma-scene')[0]; - // let sceneCtx = scene.getContext('2d'); - // sceneCtx.globalAlpha = 1; - // sceneCtx.drawImage(edges, 0, 0); - // return scene.toDataURL('image/png'); - let edgesCtx = edges.getContext('2d'); - edgesCtx.globalAlpha = 1; - edgesCtx.drawImage(scene, 0, 0); - return edges.toDataURL('image/png'); + return takeScreenshot(sigma); } function _proxySetSettings(window, sigma, settings) { diff --git a/src/external-deps/sigmajs-screenshot.js b/src/external-deps/sigmajs-screenshot.js new file mode 100644 index 000000000..56fc6b86e --- /dev/null +++ b/src/external-deps/sigmajs-screenshot.js @@ -0,0 +1,150 @@ +import * as twgl from 'twgl.js'; + + +const vertexShader = ` +attribute vec2 a_position; +attribute vec2 a_texCoord; + +uniform vec2 u_resolution; + +varying vec2 v_texCoord; + +void main() { + // convert the rectangle from pixels to 0.0 to 1.0 + vec2 zeroToOne = a_position / u_resolution; + + // convert from 0->1 to 0->2 + vec2 zeroToTwo = zeroToOne * 2.0; + + // convert from 0->2 to -1->+1 (clipspace) + vec2 clipSpace = zeroToTwo - 1.0; + + gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1); + + // pass the texCoord to the fragment shader + // The GPU will interpolate this value between points. + v_texCoord = a_texCoord; +} +`; + +const fragmentShader = ` +precision mediump float; + + // our 2 canvases + uniform sampler2D u_canvas1; + uniform sampler2D u_canvas2; + + // the texCoords passed in from the vertex shader. + // note: we're only using 1 set of texCoords which means + // we're assuming the canvases are the same size. + varying vec2 v_texCoord; + + void main() { + // Look up a pixel from first canvas + vec4 color1 = texture2D(u_canvas1, v_texCoord); + + // Look up a pixel from second canvas + vec4 color2 = texture2D(u_canvas2, v_texCoord); + + // return the 2 colors multiplied + // TODO Modify this + gl_FragColor = color1 * color2; + } +`; + + + +function setupTexture(gl, canvas, textureUnit, program, uniformName) { + let tex = gl.createTexture(); + + updateTextureFromCanvas(gl, tex, canvas, textureUnit); + + // Set the parameters so we can render any size image. + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + + let location = gl.getUniformLocation(program, uniformName); + gl.uniform1i(location, textureUnit); +} + +function updateTextureFromCanvas(gl, tex, canvas, textureUnit) { + gl.activeTexture(gl.TEXTURE0 + textureUnit); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas); +} + + +export function takeScreenshot(sigma) { + // https://stackoverflow.com/questions/12590685/blend-two-canvases-onto-one-with-webgl + let c = sigma.container; + let edges = c.getElementsByClassName('sigma-edges')[0]; + let nodes = c.getElementsByClassName('sigma-nodes')[0]; + // let sceneCtx = scene.getContext('2d'); + // sceneCtx.globalAlpha = 1; + // sceneCtx.drawImage(edges, 0, 0); + // return scene.toDataURL('image/png'); + let edgesCtx = twgl.getContext(edges); + //edgesCtx.globalAlpha = 1; + //edgesCtx.drawImage(nodes, 0, 0); + + let gl = edgesCtx; // TODO Create separate canvas for this + + const program = twgl.createProgramFromSources(gl, [vertexShader, fragmentShader]); + + gl.useProgram(program); + + const positionLocation = gl.getAttribLocation(program, "a_position"); + const texCoordLocation = gl.getAttribLocation(program, "a_texCoord"); + + const texCoordBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + 0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + 0.0, 1.0, + 1.0, 0.0, + 1.0, 1.0]), gl.STATIC_DRAW); + gl.enableVertexAttribArray(texCoordLocation); + gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0); + + // lookup uniforms + let resolutionLocation = gl.getUniformLocation(program, "u_resolution"); + + // set the resolution + gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height); + + // Create a buffer for the position of the rectangle corners. + let buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + gl.enableVertexAttribArray(positionLocation); + gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0); + + // Set a rectangle the same size as the image. + setRectangle(gl, 0, 0, gl.canvas.width, gl.canvas.height); + + let tex1 = setupTexture(gl, nodes, 0, program, "u_canvas1"); + let tex2 = setupTexture(gl, edges, 1, program, "u_canvas2"); + + // Draw the rectangle. + gl.drawArrays(gl.TRIANGLES, 0, 6); + + return gl.canvas.toDataURL('image/png'); +} + + +function setRectangle(gl, x, y, width, height) { + const x1 = x; + const x2 = x + width; + const y1 = y; + const y2 = y + height; + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + x1, y1, + x2, y1, + x1, y2, + x1, y2, + x2, y1, + x2, y2]), gl.STATIC_DRAW); +} diff --git a/yarn.lock b/yarn.lock index 23038674e..77975bcd9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10218,6 +10218,11 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= +twgl.js@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/twgl.js/-/twgl.js-5.0.4.tgz#4e3d54c4e8ff01af418bfbf7ce38df26595a6108" + integrity sha512-8U0ehLWvqOMTqMQd3xGjeQJSDwCNtoFvLg4V3Ne4QTCaPoJmOB4CSpy7MMHkMkc1qO2DMRhnV0uAgZmhO3Q/2w== + type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" -- 2.21.0