diff --git a/script.js b/script.js index 080c699985b075dbf915a92e932a33db93298fdc..c4e71a357df251b0d34e35d3f065704d91159f1e 100644 --- a/script.js +++ b/script.js @@ -1,56 +1,5 @@ -/* -MIT License - -Copyright (c) 2021 Hyung-Taik Choi - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - 'use strict'; -// Mobile promo section - -// const promoPopup = document.getElementsByClassName('promo')[0]; -// const promoPopupClose = document.getElementsByClassName('promo-close')[0]; - -// if (isMobile()) { -// setTimeout(() => { -// promoPopup.style.display = 'table'; -// }, 20000); -// } - -// promoPopupClose.addEventListener('click', e => { -// promoPopup.style.display = 'none'; -// }); - -// const appleLink = document.getElementById('apple_link'); -// appleLink.addEventListener('click', e => { -// ga('send', 'event', 'link promo', 'app'); -// window.open('https://apps.apple.com/us/app/fluid-simulation/id1443124993'); -// }); - -// const googleLink = document.getElementById('google_link'); -// googleLink.addEventListener('click', e => { -// ga('send', 'event', 'link promo', 'app'); -// window.open('https://play.google.com/store/apps/details?id=games.paveldogreat.fluidsimfree'); -// }); - // Simulation section const canvas = document.getElementsByTagName('canvas')[0]; @@ -84,7 +33,7 @@ let config = { SUNRAYS_WEIGHT: 1.0, } -function pointerPrototype () { +function pointerPrototype() { this.id = -1; this.texcoordX = 0; this.texcoordY = 0; @@ -113,9 +62,7 @@ if (!ext.supportLinearFiltering) { config.SUNRAYS = false; } -// startGUI(); - -function getWebGLContext (canvas) { +function getWebGLContext(canvas) { const params = { alpha: true, depth: false, stencil: false, antialias: false, preserveDrawingBuffer: false }; let gl = canvas.getContext('webgl2', params); @@ -140,21 +87,17 @@ function getWebGLContext (canvas) { let formatRG; let formatR; - if (isWebGL2) - { + if (isWebGL2) { formatRGBA = getSupportedFormat(gl, gl.RGBA16F, gl.RGBA, halfFloatTexType); formatRG = getSupportedFormat(gl, gl.RG16F, gl.RG, halfFloatTexType); formatR = getSupportedFormat(gl, gl.R16F, gl.RED, halfFloatTexType); } - else - { + else { formatRGBA = getSupportedFormat(gl, gl.RGBA, gl.RGBA, halfFloatTexType); formatRG = getSupportedFormat(gl, gl.RGBA, gl.RGBA, halfFloatTexType); formatR = getSupportedFormat(gl, gl.RGBA, gl.RGBA, halfFloatTexType); } - // ga('send', 'event', isWebGL2 ? 'webgl2' : 'webgl', formatRGBA == null ? 'not supported' : 'supported'); - return { gl, ext: { @@ -167,12 +110,9 @@ function getWebGLContext (canvas) { }; } -function getSupportedFormat (gl, internalFormat, format, type) -{ - if (!supportRenderTextureFormat(gl, internalFormat, format, type)) - { - switch (internalFormat) - { +function getSupportedFormat(gl, internalFormat, format, type) { + if (!supportRenderTextureFormat(gl, internalFormat, format, type)) { + switch (internalFormat) { case gl.R16F: return getSupportedFormat(gl, gl.RG16F, gl.RG, type); case gl.RG16F: @@ -188,7 +128,7 @@ function getSupportedFormat (gl, internalFormat, format, type) } } -function supportRenderTextureFormat (gl, internalFormat, format, type) { +function supportRenderTextureFormat(gl, internalFormat, format, type) { let texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); @@ -205,46 +145,11 @@ function supportRenderTextureFormat (gl, internalFormat, format, type) { return status == gl.FRAMEBUFFER_COMPLETE; } -// function startGUI () { -// var gui = new dat.GUI({ width: 300 }); -// gui.add(config, 'DYE_RESOLUTION', { 'high': 1024, 'medium': 512, 'low': 256, 'very low': 128 }).name('quality').onFinishChange(initFramebuffers); -// gui.add(config, 'SIM_RESOLUTION', { '32': 32, '64': 64, '128': 128, '256': 256 }).name('sim resolution').onFinishChange(initFramebuffers); -// gui.add(config, 'DENSITY_DISSIPATION', 0, 4.0).name('density diffusion'); -// gui.add(config, 'VELOCITY_DISSIPATION', 0, 4.0).name('velocity diffusion'); -// gui.add(config, 'PRESSURE', 0.0, 1.0).name('pressure'); -// gui.add(config, 'CURL', 0, 50).name('vorticity').step(1); -// gui.add(config, 'SPLAT_RADIUS', 0.01, 1.0).name('splat radius'); -// gui.add(config, 'SHADING').name('shading').onFinishChange(updateKeywords); -// gui.add(config, 'COLORFUL').name('colorful'); -// gui.add(config, 'PAUSED').name('paused').listen(); - -// gui.add({ fun: () => { -// splatStack.push(parseInt(Math.random() * 20) + 5); -// } }, 'fun').name('Random splats'); - -// let bloomFolder = gui.addFolder('Bloom'); -// bloomFolder.add(config, 'BLOOM').name('enabled').onFinishChange(updateKeywords); -// bloomFolder.add(config, 'BLOOM_INTENSITY', 0.1, 2.0).name('intensity'); -// bloomFolder.add(config, 'BLOOM_THRESHOLD', 0.0, 1.0).name('threshold'); - -// let sunraysFolder = gui.addFolder('Sunrays'); -// sunraysFolder.add(config, 'SUNRAYS').name('enabled').onFinishChange(updateKeywords); -// sunraysFolder.add(config, 'SUNRAYS_WEIGHT', 0.3, 1.0).name('weight'); - -// let captureFolder = gui.addFolder('Capture'); -// captureFolder.addColor(config, 'BACK_COLOR').name('background color'); -// captureFolder.add(config, 'TRANSPARENT').name('transparent'); -// captureFolder.add({ fun: captureScreenshot }, 'fun').name('take screenshot'); - -// if (isMobile()) -// gui.close(); -// } - -function isMobile () { +function isMobile() { return /Mobi|Android/i.test(navigator.userAgent); } -function captureScreenshot () { +function captureScreenshot() { let res = getResolution(config.CAPTURE_RESOLUTION); let target = createFBO(res.width, res.height, ext.formatRGBA.internalFormat, ext.formatRGBA.format, ext.halfFloatTexType, gl.NEAREST); render(target); @@ -258,7 +163,7 @@ function captureScreenshot () { URL.revokeObjectURL(datauri); } -function framebufferToTexture (target) { +function framebufferToTexture(target) { gl.bindFramebuffer(gl.FRAMEBUFFER, target.fbo); let length = target.width * target.height * 4; let texture = new Float32Array(length); @@ -266,7 +171,7 @@ function framebufferToTexture (target) { return texture; } -function normalizeTexture (texture, width, height) { +function normalizeTexture(texture, width, height) { let result = new Uint8Array(texture.length); let id = 0; for (let i = height - 1; i >= 0; i--) { @@ -282,11 +187,11 @@ function normalizeTexture (texture, width, height) { return result; } -function clamp01 (input) { +function clamp01(input) { return Math.min(Math.max(input, 0), 1); } -function textureToCanvas (texture, width, height) { +function textureToCanvas(texture, width, height) { let captureCanvas = document.createElement('canvas'); let ctx = captureCanvas.getContext('2d'); captureCanvas.width = width; @@ -299,7 +204,7 @@ function textureToCanvas (texture, width, height) { return captureCanvas; } -function downloadURI (filename, uri) { +function downloadURI(filename, uri) { let link = document.createElement('a'); link.download = filename; link.href = uri; @@ -309,7 +214,7 @@ function downloadURI (filename, uri) { } class Material { - constructor (vertexShader, fragmentShaderSource) { + constructor(vertexShader, fragmentShaderSource) { this.vertexShader = vertexShader; this.fragmentShaderSource = fragmentShaderSource; this.programs = []; @@ -317,14 +222,13 @@ class Material { this.uniforms = []; } - setKeywords (keywords) { + setKeywords(keywords) { let hash = 0; for (let i = 0; i < keywords.length; i++) hash += hashCode(keywords[i]); let program = this.programs[hash]; - if (program == null) - { + if (program == null) { let fragmentShader = compileShader(gl.FRAGMENT_SHADER, this.fragmentShaderSource, keywords); program = createProgram(this.vertexShader, fragmentShader); this.programs[hash] = program; @@ -336,24 +240,24 @@ class Material { this.activeProgram = program; } - bind () { + bind() { gl.useProgram(this.activeProgram); } } class Program { - constructor (vertexShader, fragmentShader) { + constructor(vertexShader, fragmentShader) { this.uniforms = {}; this.program = createProgram(vertexShader, fragmentShader); this.uniforms = getUniforms(this.program); } - bind () { + bind() { gl.useProgram(this.program); } } -function createProgram (vertexShader, fragmentShader) { +function createProgram(vertexShader, fragmentShader) { let program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); @@ -365,7 +269,7 @@ function createProgram (vertexShader, fragmentShader) { return program; } -function getUniforms (program) { +function getUniforms(program) { let uniforms = []; let uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); for (let i = 0; i < uniformCount; i++) { @@ -375,7 +279,7 @@ function getUniforms (program) { return uniforms; } -function compileShader (type, source, keywords) { +function compileShader(type, source, keywords) { source = addKeywords(source, keywords); const shader = gl.createShader(type); @@ -388,7 +292,7 @@ function compileShader (type, source, keywords) { return shader; }; -function addKeywords (source, keywords) { +function addKeywords(source, keywords) { if (keywords == null) return source; let keywordsString = ''; keywords.forEach(keyword => { @@ -881,18 +785,15 @@ const blit = (() => { gl.enableVertexAttribArray(0); return (target, clear = false) => { - if (target == null) - { + if (target == null) { gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); gl.bindFramebuffer(gl.FRAMEBUFFER, null); } - else - { + else { gl.viewport(0, 0, target.width, target.height); gl.bindFramebuffer(gl.FRAMEBUFFER, target.fbo); } - if (clear) - { + if (clear) { gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); } @@ -901,7 +802,7 @@ const blit = (() => { } })(); -function CHECK_FRAMEBUFFER_STATUS () { +function CHECK_FRAMEBUFFER_STATUS() { let status = gl.checkFramebufferStatus(gl.FRAMEBUFFER); if (status != gl.FRAMEBUFFER_COMPLETE) console.trace("Framebuffer error: " + status); @@ -919,34 +820,34 @@ let sunraysTemp; let ditheringTexture = createTextureAsync('./assets/LDR_LLL1_0.png'); -const blurProgram = new Program(blurVertexShader, blurShader); -const copyProgram = new Program(baseVertexShader, copyShader); -const clearProgram = new Program(baseVertexShader, clearShader); -const colorProgram = new Program(baseVertexShader, colorShader); -const checkerboardProgram = new Program(baseVertexShader, checkerboardShader); -const bloomPrefilterProgram = new Program(baseVertexShader, bloomPrefilterShader); -const bloomBlurProgram = new Program(baseVertexShader, bloomBlurShader); -const bloomFinalProgram = new Program(baseVertexShader, bloomFinalShader); -const sunraysMaskProgram = new Program(baseVertexShader, sunraysMaskShader); -const sunraysProgram = new Program(baseVertexShader, sunraysShader); -const splatProgram = new Program(baseVertexShader, splatShader); -const advectionProgram = new Program(baseVertexShader, advectionShader); -const divergenceProgram = new Program(baseVertexShader, divergenceShader); -const curlProgram = new Program(baseVertexShader, curlShader); -const vorticityProgram = new Program(baseVertexShader, vorticityShader); -const pressureProgram = new Program(baseVertexShader, pressureShader); +const blurProgram = new Program(blurVertexShader, blurShader); +const copyProgram = new Program(baseVertexShader, copyShader); +const clearProgram = new Program(baseVertexShader, clearShader); +const colorProgram = new Program(baseVertexShader, colorShader); +const checkerboardProgram = new Program(baseVertexShader, checkerboardShader); +const bloomPrefilterProgram = new Program(baseVertexShader, bloomPrefilterShader); +const bloomBlurProgram = new Program(baseVertexShader, bloomBlurShader); +const bloomFinalProgram = new Program(baseVertexShader, bloomFinalShader); +const sunraysMaskProgram = new Program(baseVertexShader, sunraysMaskShader); +const sunraysProgram = new Program(baseVertexShader, sunraysShader); +const splatProgram = new Program(baseVertexShader, splatShader); +const advectionProgram = new Program(baseVertexShader, advectionShader); +const divergenceProgram = new Program(baseVertexShader, divergenceShader); +const curlProgram = new Program(baseVertexShader, curlShader); +const vorticityProgram = new Program(baseVertexShader, vorticityShader); +const pressureProgram = new Program(baseVertexShader, pressureShader); const gradienSubtractProgram = new Program(baseVertexShader, gradientSubtractShader); const displayMaterial = new Material(baseVertexShader, displayShaderSource); -function initFramebuffers () { +function initFramebuffers() { let simRes = getResolution(config.SIM_RESOLUTION); let dyeRes = getResolution(config.DYE_RESOLUTION); const texType = ext.halfFloatTexType; - const rgba = ext.formatRGBA; - const rg = ext.formatRG; - const r = ext.formatR; + const rgba = ext.formatRGBA; + const rg = ext.formatRG; + const r = ext.formatR; const filtering = ext.supportLinearFiltering ? gl.LINEAR : gl.NEAREST; gl.disable(gl.BLEND); @@ -961,15 +862,15 @@ function initFramebuffers () { else velocity = resizeDoubleFBO(velocity, simRes.width, simRes.height, rg.internalFormat, rg.format, texType, filtering); - divergence = createFBO (simRes.width, simRes.height, r.internalFormat, r.format, texType, gl.NEAREST); - curl = createFBO (simRes.width, simRes.height, r.internalFormat, r.format, texType, gl.NEAREST); - pressure = createDoubleFBO(simRes.width, simRes.height, r.internalFormat, r.format, texType, gl.NEAREST); + divergence = createFBO(simRes.width, simRes.height, r.internalFormat, r.format, texType, gl.NEAREST); + curl = createFBO(simRes.width, simRes.height, r.internalFormat, r.format, texType, gl.NEAREST); + pressure = createDoubleFBO(simRes.width, simRes.height, r.internalFormat, r.format, texType, gl.NEAREST); initBloomFramebuffers(); initSunraysFramebuffers(); } -function initBloomFramebuffers () { +function initBloomFramebuffers() { let res = getResolution(config.BLOOM_RESOLUTION); const texType = ext.halfFloatTexType; @@ -979,8 +880,7 @@ function initBloomFramebuffers () { bloom = createFBO(res.width, res.height, rgba.internalFormat, rgba.format, texType, filtering); bloomFramebuffers.length = 0; - for (let i = 0; i < config.BLOOM_ITERATIONS; i++) - { + for (let i = 0; i < config.BLOOM_ITERATIONS; i++) { let width = res.width >> (i + 1); let height = res.height >> (i + 1); @@ -991,18 +891,18 @@ function initBloomFramebuffers () { } } -function initSunraysFramebuffers () { +function initSunraysFramebuffers() { let res = getResolution(config.SUNRAYS_RESOLUTION); const texType = ext.halfFloatTexType; const r = ext.formatR; const filtering = ext.supportLinearFiltering ? gl.LINEAR : gl.NEAREST; - sunrays = createFBO(res.width, res.height, r.internalFormat, r.format, texType, filtering); + sunrays = createFBO(res.width, res.height, r.internalFormat, r.format, texType, filtering); sunraysTemp = createFBO(res.width, res.height, r.internalFormat, r.format, texType, filtering); } -function createFBO (w, h, internalFormat, format, type, param) { +function createFBO(w, h, internalFormat, format, type, param) { gl.activeTexture(gl.TEXTURE0); let texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); @@ -1028,7 +928,7 @@ function createFBO (w, h, internalFormat, format, type, param) { height: h, texelSizeX, texelSizeY, - attach (id) { + attach(id) { gl.activeTexture(gl.TEXTURE0 + id); gl.bindTexture(gl.TEXTURE_2D, texture); return id; @@ -1036,7 +936,7 @@ function createFBO (w, h, internalFormat, format, type, param) { }; } -function createDoubleFBO (w, h, internalFormat, format, type, param) { +function createDoubleFBO(w, h, internalFormat, format, type, param) { let fbo1 = createFBO(w, h, internalFormat, format, type, param); let fbo2 = createFBO(w, h, internalFormat, format, type, param); @@ -1045,19 +945,19 @@ function createDoubleFBO (w, h, internalFormat, format, type, param) { height: h, texelSizeX: fbo1.texelSizeX, texelSizeY: fbo1.texelSizeY, - get read () { + get read() { return fbo1; }, - set read (value) { + set read(value) { fbo1 = value; }, - get write () { + get write() { return fbo2; }, - set write (value) { + set write(value) { fbo2 = value; }, - swap () { + swap() { let temp = fbo1; fbo1 = fbo2; fbo2 = temp; @@ -1065,7 +965,7 @@ function createDoubleFBO (w, h, internalFormat, format, type, param) { } } -function resizeFBO (target, w, h, internalFormat, format, type, param) { +function resizeFBO(target, w, h, internalFormat, format, type, param) { let newFBO = createFBO(w, h, internalFormat, format, type, param); copyProgram.bind(); gl.uniform1i(copyProgram.uniforms.uTexture, target.attach(0)); @@ -1073,7 +973,7 @@ function resizeFBO (target, w, h, internalFormat, format, type, param) { return newFBO; } -function resizeDoubleFBO (target, w, h, internalFormat, format, type, param) { +function resizeDoubleFBO(target, w, h, internalFormat, format, type, param) { if (target.width == w && target.height == h) return target; target.read = resizeFBO(target.read, w, h, internalFormat, format, type, param); @@ -1085,7 +985,7 @@ function resizeDoubleFBO (target, w, h, internalFormat, format, type, param) { return target; } -function createTextureAsync (url) { +function createTextureAsync(url) { let texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); @@ -1098,7 +998,7 @@ function createTextureAsync (url) { texture, width: 1, height: 1, - attach (id) { + attach(id) { gl.activeTexture(gl.TEXTURE0 + id); gl.bindTexture(gl.TEXTURE_2D, texture); return id; @@ -1117,7 +1017,7 @@ function createTextureAsync (url) { return obj; } -function updateKeywords () { +function updateKeywords() { let displayKeywords = []; if (config.SHADING) displayKeywords.push("SHADING"); if (config.BLOOM) displayKeywords.push("BLOOM"); @@ -1133,7 +1033,7 @@ let lastUpdateTime = Date.now(); let colorUpdateTimer = 0.0; update(); -function update () { +function update() { const dt = calcDeltaTime(); if (resizeCanvas()) initFramebuffers(); @@ -1145,7 +1045,7 @@ function update () { requestAnimationFrame(update); } -function calcDeltaTime () { +function calcDeltaTime() { let now = Date.now(); let dt = (now - lastUpdateTime) / 1000; dt = Math.min(dt, 0.016666); @@ -1153,7 +1053,7 @@ function calcDeltaTime () { return dt; } -function resizeCanvas () { +function resizeCanvas() { let width = scaleByPixelRatio(canvas.clientWidth); let height = scaleByPixelRatio(canvas.clientHeight); if (canvas.width != width || canvas.height != height) { @@ -1164,7 +1064,7 @@ function resizeCanvas () { return false; } -function updateColors (dt) { +function updateColors(dt) { if (!config.COLORFUL) return; colorUpdateTimer += dt * config.COLOR_UPDATE_SPEED; @@ -1176,7 +1076,7 @@ function updateColors (dt) { } } -function applyInputs () { +function applyInputs() { if (splatStack.length > 0) multipleSplats(splatStack.pop()); @@ -1188,7 +1088,7 @@ function applyInputs () { }); } -function step (dt) { +function step(dt) { gl.disable(gl.BLEND); curlProgram.bind(); @@ -1253,7 +1153,7 @@ function step (dt) { dye.swap(); } -function render (target) { +function render(target) { if (config.BLOOM) applyBloom(dye.read, bloom); if (config.SUNRAYS) { @@ -1276,19 +1176,19 @@ function render (target) { drawDisplay(target); } -function drawColor (target, color) { +function drawColor(target, color) { colorProgram.bind(); gl.uniform4f(colorProgram.uniforms.color, color.r, color.g, color.b, 1); blit(target); } -function drawCheckerboard (target) { +function drawCheckerboard(target) { checkerboardProgram.bind(); gl.uniform1f(checkerboardProgram.uniforms.aspectRatio, canvas.width / canvas.height); blit(target); } -function drawDisplay (target) { +function drawDisplay(target) { let width = target == null ? gl.drawingBufferWidth : target.width; let height = target == null ? gl.drawingBufferHeight : target.height; @@ -1307,7 +1207,7 @@ function drawDisplay (target) { blit(target); } -function applyBloom (source, destination) { +function applyBloom(source, destination) { if (bloomFramebuffers.length < 2) return; @@ -1353,7 +1253,7 @@ function applyBloom (source, destination) { blit(destination); } -function applySunrays (source, mask, destination) { +function applySunrays(source, mask, destination) { gl.disable(gl.BLEND); sunraysMaskProgram.bind(); gl.uniform1i(sunraysMaskProgram.uniforms.uTexture, source.attach(0)); @@ -1365,7 +1265,7 @@ function applySunrays (source, mask, destination) { blit(destination); } -function blur (target, temp, iterations) { +function blur(target, temp, iterations) { blurProgram.bind(); for (let i = 0; i < iterations; i++) { gl.uniform2f(blurProgram.uniforms.texelSize, target.texelSizeX, 0.0); @@ -1378,13 +1278,13 @@ function blur (target, temp, iterations) { } } -function splatPointer (pointer) { +function splatPointer(pointer) { let dx = pointer.deltaX * config.SPLAT_FORCE; let dy = pointer.deltaY * config.SPLAT_FORCE; splat(pointer.texcoordX, pointer.texcoordY, dx, dy, pointer.color); } -function multipleSplats (amount) { +function multipleSplats(amount) { for (let i = 0; i < amount; i++) { const color = generateColor(); color.r *= 10.0; @@ -1398,7 +1298,7 @@ function multipleSplats (amount) { } } -function splat (x, y, dx, dy, color) { +function splat(x, y, dx, dy, color) { splatProgram.bind(); gl.uniform1i(splatProgram.uniforms.uTarget, velocity.read.attach(0)); gl.uniform1f(splatProgram.uniforms.aspectRatio, canvas.width / canvas.height); @@ -1414,7 +1314,7 @@ function splat (x, y, dx, dy, color) { dye.swap(); } -function correctRadius (radius) { +function correctRadius(radius) { let aspectRatio = canvas.width / canvas.height; if (aspectRatio > 1) radius *= aspectRatio; @@ -1468,8 +1368,7 @@ canvas.addEventListener('touchmove', e => { window.addEventListener('touchend', e => { const touches = e.changedTouches; - for (let i = 0; i < touches.length; i++) - { + for (let i = 0; i < touches.length; i++) { let pointer = pointers.find(p => p.id == touches[i].identifier); if (pointer == null) continue; updatePointerUpData(pointer); @@ -1483,7 +1382,7 @@ window.addEventListener('keydown', e => { splatStack.push(parseInt(Math.random() * 20) + 5); }); -function updatePointerDownData (pointer, id, posX, posY) { +function updatePointerDownData(pointer, id, posX, posY) { pointer.id = id; pointer.down = true; pointer.moved = false; @@ -1496,7 +1395,7 @@ function updatePointerDownData (pointer, id, posX, posY) { pointer.color = generateColor(); } -function updatePointerMoveData (pointer, posX, posY) { +function updatePointerMoveData(pointer, posX, posY) { pointer.prevTexcoordX = pointer.texcoordX; pointer.prevTexcoordY = pointer.texcoordY; pointer.texcoordX = posX / canvas.width; @@ -1506,23 +1405,23 @@ function updatePointerMoveData (pointer, posX, posY) { pointer.moved = Math.abs(pointer.deltaX) > 0 || Math.abs(pointer.deltaY) > 0; } -function updatePointerUpData (pointer) { +function updatePointerUpData(pointer) { pointer.down = false; } -function correctDeltaX (delta) { +function correctDeltaX(delta) { let aspectRatio = canvas.width / canvas.height; if (aspectRatio < 1) delta *= aspectRatio; return delta; } -function correctDeltaY (delta) { +function correctDeltaY(delta) { let aspectRatio = canvas.width / canvas.height; if (aspectRatio > 1) delta /= aspectRatio; return delta; } -function generateColor () { +function generateColor() { let c = HSVtoRGB(Math.random(), 1.0, 1.0); c.r *= 0.15; c.g *= 0.15; @@ -1530,7 +1429,7 @@ function generateColor () { return c; } -function HSVtoRGB (h, s, v) { +function HSVtoRGB(h, s, v) { let r, g, b, i, f, p, q, t; i = Math.floor(h * 6); f = h * 6 - i; @@ -1554,7 +1453,7 @@ function HSVtoRGB (h, s, v) { }; } -function normalizeColor (input) { +function normalizeColor(input) { let output = { r: input.r / 255, g: input.g / 255, @@ -1563,13 +1462,13 @@ function normalizeColor (input) { return output; } -function wrap (value, min, max) { +function wrap(value, min, max) { let range = max - min; if (range == 0) return min; return (value - min) % range + min; } -function getResolution (resolution) { +function getResolution(resolution) { let aspectRatio = gl.drawingBufferWidth / gl.drawingBufferHeight; if (aspectRatio < 1) aspectRatio = 1.0 / aspectRatio; @@ -1583,19 +1482,19 @@ function getResolution (resolution) { return { width: min, height: max }; } -function getTextureScale (texture, width, height) { +function getTextureScale(texture, width, height) { return { x: width / texture.width, y: height / texture.height }; } -function scaleByPixelRatio (input) { +function scaleByPixelRatio(input) { let pixelRatio = window.devicePixelRatio || 1; return Math.floor(input * pixelRatio); } -function hashCode (s) { +function hashCode(s) { if (s.length == 0) return 0; let hash = 0; for (let i = 0; i < s.length; i++) { @@ -1603,4 +1502,4 @@ function hashCode (s) { hash |= 0; // Convert to 32bit integer } return hash; -}; \ No newline at end of file +};