UNPKG

fluid-pointer

Version:

A fluid simulation web component with WebGL-based physics

280 lines (251 loc) 36.2 kB
(function(n,m){typeof exports=="object"&&typeof module<"u"?m(exports,require("lit"),require("lit/decorators.js")):typeof define=="function"&&define.amd?define(["exports","lit","lit/decorators.js"],m):(n=typeof globalThis<"u"?globalThis:n||self,m(n.FluidPointer={},n.Lit,n.LitDecorators))})(this,function(n,m,u){"use strict";function A(i){const t={alpha:!0,depth:!1,stencil:!1,antialias:!1,preserveDrawingBuffer:!1};let e=i.getContext("webgl2",t);const r=!!e;if(r||(e=i.getContext("webgl",t)||i.getContext("experimental-webgl",t)),!e)return console.error("WebGL not supported"),null;let o=null,s=null;if(r){const p=e;p.getExtension("EXT_color_buffer_float"),s=p.getExtension("OES_texture_float_linear")}else{const p=e;o=p.getExtension("OES_texture_half_float"),s=p.getExtension("OES_texture_half_float_linear")}e.clearColor(0,0,0,1);const a=r?e.HALF_FLOAT:o?o.HALF_FLOAT_OES:e.FLOAT,h=g(e,r?e.RGBA16F:e.RGBA,e.RGBA,a),l=g(e,r?e.RG16F:e.RGBA,r?e.RG:e.RGBA,a),d=g(e,r?e.R16F:e.RGBA,r?e.RED:e.RGBA,a);return{gl:e,ext:{formatRGBA:h,formatRG:l,formatR:d,halfFloatTexType:a,supportLinearFiltering:s}}}function g(i,t,e,r){if(!L(i,t,e,r))switch(t){case i.R16F:return g(i,i.RG16F,i.RG,r);case i.RG16F:return g(i,i.RGBA,i.RGBA,r);default:return null}return{internalFormat:t,format:e}}function L(i,t,e,r){const o=i.createTexture();i.bindTexture(i.TEXTURE_2D,o),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MIN_FILTER,i.NEAREST),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MAG_FILTER,i.NEAREST),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_S,i.CLAMP_TO_EDGE),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_T,i.CLAMP_TO_EDGE),i.texImage2D(i.TEXTURE_2D,0,t,4,4,0,e,r,null);const s=i.createFramebuffer();i.bindFramebuffer(i.FRAMEBUFFER,s),i.framebufferTexture2D(i.FRAMEBUFFER,i.COLOR_ATTACHMENT0,i.TEXTURE_2D,o,0);const h=i.checkFramebufferStatus(i.FRAMEBUFFER)===i.FRAMEBUFFER_COMPLETE;return i.deleteTexture(o),i.deleteFramebuffer(s),i.bindFramebuffer(i.FRAMEBUFFER,null),h}function R(i,t,e){const r=i.createShader(t);return r?(i.shaderSource(r,e),i.compileShader(r),i.getShaderParameter(r,i.COMPILE_STATUS)?r:(console.error("Shader compilation failed:"),console.error("Shader source:",e),console.error("Error:",i.getShaderInfoLog(r)),i.deleteShader(r),null)):null}function O(i,t,e){const r=i.createProgram();return r?(i.attachShader(r,t),i.attachShader(r,e),i.bindAttribLocation(r,0,"aPosition"),i.linkProgram(r),i.getProgramParameter(r,i.LINK_STATUS)?r:(console.error("Program linking failed:"),console.error("Error:",i.getProgramInfoLog(r)),i.deleteProgram(r),null)):null}function E(i,t){const e={},r=i.getProgramParameter(t,i.ACTIVE_UNIFORMS);for(let o=0;o<r;o++){const s=i.getActiveUniform(t,o);if(s){const a=i.getUniformLocation(t,s.name);a&&(e[s.name]=a)}}return e}class M{constructor(t,e,r){this.gl=t;const o=O(t,e,r);if(!o)throw new Error("Failed to create shader program");this.program=o,this.uniforms=E(t,o)}bind(){this.gl.useProgram(this.program)}}class U{constructor(t,e,r){this.programs=new Map,this.gl=t,this.vertexShader=e,this.fragmentShaderSource=r,this.program=this.createProgramVariant([]),this.uniforms=E(t,this.program)}createProgramVariant(t){const e=t.join("_");if(this.programs.has(e))return this.programs.get(e);let r=this.fragmentShaderSource;t.length>0&&(r=t.map(h=>`#define ${h}`).join(` `)+` `+r);const o=R(this.gl,this.gl.FRAGMENT_SHADER,r);if(!o)throw new Error("Failed to compile fragment shader variant");const s=O(this.gl,this.vertexShader,o);if(!s)throw new Error("Failed to create program variant");return this.programs.set(e,s),s}setKeywords(t){this.program=this.createProgramVariant(t),this.uniforms=E(this.gl,this.program)}bind(){this.gl.useProgram(this.program)}}function v(i,t,e){const r=R(i,i.VERTEX_SHADER,t),o=R(i,i.FRAGMENT_SHADER,e);if(!r||!o)throw new Error("Failed to compile shaders");return new M(i,r,o)}function D(i,t,e){const r=R(i,i.VERTEX_SHADER,t);if(!r)throw new Error("Failed to compile vertex shader");return new U(i,r,e)}let S=null,F=null;function B(i){S||(S=i.createBuffer(),i.bindBuffer(i.ARRAY_BUFFER,S),i.bufferData(i.ARRAY_BUFFER,new Float32Array([-1,-1,-1,1,1,1,1,-1]),i.STATIC_DRAW),F=i.createBuffer(),i.bindBuffer(i.ELEMENT_ARRAY_BUFFER,F),i.bufferData(i.ELEMENT_ARRAY_BUFFER,new Uint16Array([0,1,2,0,2,3]),i.STATIC_DRAW))}function f(i,t){S||B(i),i.bindBuffer(i.ARRAY_BUFFER,S),i.bindBuffer(i.ELEMENT_ARRAY_BUFFER,F),i.vertexAttribPointer(0,2,i.FLOAT,!1,0,0),i.enableVertexAttribArray(0),t==null?(i.viewport(0,0,i.drawingBufferWidth,i.drawingBufferHeight),i.bindFramebuffer(i.FRAMEBUFFER,null)):(i.viewport(0,0,t.width,t.height),i.bindFramebuffer(i.FRAMEBUFFER,t.fbo)),i.drawElements(i.TRIANGLES,6,i.UNSIGNED_SHORT,0)}function T(i,t,e,r,o,s,a){i.activeTexture(i.TEXTURE0);const h=i.createTexture();i.bindTexture(i.TEXTURE_2D,h),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MIN_FILTER,a),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MAG_FILTER,a),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_S,i.CLAMP_TO_EDGE),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_T,i.CLAMP_TO_EDGE),i.texImage2D(i.TEXTURE_2D,0,r,t,e,0,o,s,null);const l=i.createFramebuffer();i.bindFramebuffer(i.FRAMEBUFFER,l),i.framebufferTexture2D(i.FRAMEBUFFER,i.COLOR_ATTACHMENT0,i.TEXTURE_2D,h,0),i.viewport(0,0,t,e),i.clear(i.COLOR_BUFFER_BIT);const d=1/t,p=1/e;return{fbo:l,texture:h,width:t,height:e,texelSizeX:d,texelSizeY:p,attach(y){return i.activeTexture(i.TEXTURE0+y),i.bindTexture(i.TEXTURE_2D,h),y}}}function P(i,t,e,r,o,s,a){let h=T(i,t,e,r,o,s,a),l=T(i,t,e,r,o,s,a);return{read:h,write:l,swap(){const d=h;h=l,l=d,this.read=h,this.write=l}}}function w(i,t){let e=i.drawingBufferWidth/i.drawingBufferHeight;e<1&&(e=1/e);const r=Math.round(t),o=Math.round(t*e);return i.drawingBufferWidth>i.drawingBufferHeight?{width:o,height:r}:{width:r,height:o}}class _{constructor(t,e={}){this.pointers=[],this.canvas=t,this.onSplat=e.onSplat,this.onInteractionStart=e.onInteractionStart,this.onInteractionEnd=e.onInteractionEnd,this.pointers.push(this.createPointer()),this.setupEventListeners()}createPointer(){return{id:-1,texcoordX:0,texcoordY:0,prevTexcoordX:0,prevTexcoordY:0,deltaX:0,deltaY:0,down:!1,moved:!1,color:[1,0,0]}}setupEventListeners(){this.canvas.addEventListener("mousedown",this.handleMouseDown.bind(this)),this.canvas.addEventListener("mousemove",this.handleMouseMove.bind(this)),window.addEventListener("mouseup",this.handleMouseUp.bind(this)),this.canvas.addEventListener("touchstart",this.handleTouchStart.bind(this),{passive:!1}),this.canvas.addEventListener("touchmove",this.handleTouchMove.bind(this),{passive:!1}),window.addEventListener("touchend",this.handleTouchEnd.bind(this))}handleMouseDown(t){const e=this.scaleByPixelRatio(t.offsetX),r=this.scaleByPixelRatio(t.offsetY);let o=this.pointers.find(s=>s.id===-1);o||(o=this.createPointer(),this.pointers.push(o)),this.updatePointerDown(o,-1,e,r)}handleMouseMove(t){const e=this.pointers[0],r=this.scaleByPixelRatio(t.offsetX),o=this.scaleByPixelRatio(t.offsetY);this.updatePointerMove(e,r,o)}handleMouseUp(){this.updatePointerUp(this.pointers[0])}handleTouchStart(t){t.preventDefault();const e=t.targetTouches;for(let r=0;r<e.length;r++){let o=this.pointers[r]||this.createPointer();r>=this.pointers.length&&this.pointers.push(o);const s=this.scaleByPixelRatio(e[r].pageX),a=this.scaleByPixelRatio(e[r].pageY);this.updatePointerDown(o,e[r].identifier,s,a)}}handleTouchMove(t){t.preventDefault();const e=t.targetTouches;for(let r=0;r<e.length;r++){const o=this.pointers.find(h=>h.id===e[r].identifier);if(!o)continue;const s=this.scaleByPixelRatio(e[r].pageX),a=this.scaleByPixelRatio(e[r].pageY);this.updatePointerMove(o,s,a)}}handleTouchEnd(t){const e=t.changedTouches;for(let r=0;r<e.length;r++){const o=this.pointers.find(s=>s.id===e[r].identifier);o&&this.updatePointerUp(o)}}updatePointerDown(t,e,r,o){var s;t.id=e,t.down=!0,t.moved=!1,t.texcoordX=r/this.canvas.width,t.texcoordY=1-o/this.canvas.height,t.prevTexcoordX=t.texcoordX,t.prevTexcoordY=t.texcoordY,t.deltaX=0,t.deltaY=0,(s=this.onInteractionStart)==null||s.call(this,t)}updatePointerMove(t,e,r){t.prevTexcoordX=t.texcoordX,t.prevTexcoordY=t.texcoordY,t.texcoordX=e/this.canvas.width,t.texcoordY=1-r/this.canvas.height,t.deltaX=this.correctDelta(t.texcoordX-t.prevTexcoordX),t.deltaY=this.correctDelta(t.texcoordY-t.prevTexcoordY),t.moved=Math.abs(t.deltaX)>0||Math.abs(t.deltaY)>0}updatePointerUp(t){var e;t.down=!1,(e=this.onInteractionEnd)==null||e.call(this,t)}scaleByPixelRatio(t){return t*(window.devicePixelRatio||1)}correctDelta(t){const e=this.canvas.width/this.canvas.height;return e<1&&(t*=e),t}updatePointers(t=.001){this.pointers.forEach(e=>{var r;e.moved&&(e.moved=!1,Math.sqrt(e.deltaX*e.deltaX+e.deltaY*e.deltaY)>t&&((r=this.onSplat)==null||r.call(this,e)))})}setPointerColor(t){this.pointers.forEach(e=>{e.color=t})}getPointers(){return this.pointers}destroy(){this.canvas.removeEventListener("mousedown",this.handleMouseDown.bind(this)),this.canvas.removeEventListener("mousemove",this.handleMouseMove.bind(this)),window.removeEventListener("mouseup",this.handleMouseUp.bind(this)),this.canvas.removeEventListener("touchstart",this.handleTouchStart.bind(this)),this.canvas.removeEventListener("touchmove",this.handleTouchMove.bind(this)),window.removeEventListener("touchend",this.handleTouchEnd.bind(this))}}class I{constructor(t="rainbow",e=[]){this.currentHue=Math.random(),this.targetHue=Math.random(),this.colorUpdateTimer=0,this.fixedColor=null,this.colorMode=t,this.customColors=e}updateColors(t,e,r,o){if(!e)return[1,1,1];if(this.fixedColor)return this.fixedColor;this.colorUpdateTimer+=t*r;const s=o*t;return Math.abs(this.targetHue-this.currentHue)>s?this.targetHue>this.currentHue?this.currentHue+=s:this.currentHue-=s:this.currentHue=this.targetHue,this.colorUpdateTimer>=1&&(this.colorUpdateTimer=0,this.targetHue=this.generateNewTargetHue()),this.generateColor(this.currentHue)}generateNewTargetHue(){switch(this.colorMode){case"rainbow":return Math.random();case"monochrome":return .6+(Math.random()-.5)*.1;case"custom":if(this.customColors.length>0){const e=this.customColors[Math.floor(Math.random()*this.customColors.length)];return this.rgbToHue(e)}return Math.random();default:return Math.random()}}generateColor(t){switch(this.colorMode){case"rainbow":return this.hsvToRgb(t,1,1);case"monochrome":return this.hsvToRgb(t,.8,1);case"custom":return this.customColors.length>0?this.customColors[Math.floor(Math.random()*this.customColors.length)]:this.hsvToRgb(t,1,1);default:return this.hsvToRgb(t,1,1)}}hsvToRgb(t,e,r){let o,s,a;const h=Math.floor(t*6),l=t*6-h,d=r*(1-e),p=r*(1-l*e),y=r*(1-(1-l)*e);switch(h%6){case 0:o=r,s=y,a=d;break;case 1:o=p,s=r,a=d;break;case 2:o=d,s=r,a=y;break;case 3:o=d,s=p,a=r;break;case 4:o=y,s=d,a=r;break;case 5:o=r,s=d,a=p;break;default:o=s=a=0}return[o,s,a]}rgbToHue(t){const[e,r,o]=t,s=Math.max(e,r,o),a=Math.min(e,r,o),h=s-a;if(h===0)return 0;let l;return s===e?l=(r-o)/h%6:s===r?l=(o-e)/h+2:l=(e-r)/h+4,l/=6,l<0&&(l+=1),l}setColorMode(t){this.colorMode=t}setCustomColors(t){this.customColors=t}setFixedColor(t){this.fixedColor=t}clearFixedColor(){this.fixedColor=null}generateRandomColor(){return this.fixedColor?this.fixedColor:this.generateColor(Math.random())}reset(){this.currentHue=Math.random(),this.targetHue=Math.random(),this.colorUpdateTimer=0}}function x(i){const t=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(i);return t?[parseInt(t[1],16)/255,parseInt(t[2],16)/255,parseInt(t[3],16)/255]:null}function z(i){const[t,e,r]=i.map(o=>Math.round(o*255));return`#${t.toString(16).padStart(2,"0")}${e.toString(16).padStart(2,"0")}${r.toString(16).padStart(2,"0")}`}function N(i){try{const t=JSON.parse(i);if(Array.isArray(t))return t.filter(e=>Array.isArray(e)&&e.length===3&&e.every(r=>typeof r=="number"&&r>=0&&r<=1))}catch(t){console.warn("Failed to parse custom colors:",t)}return[]}const C={black:[0,0,0],white:[1,1,1],red:[1,0,0],green:[0,.5,0],blue:[0,0,1],yellow:[1,1,0],cyan:[0,1,1],magenta:[1,0,1],orange:[1,.647,0],purple:[.5,0,.5],pink:[1,.753,.796],brown:[.647,.165,.165],gray:[.5,.5,.5],grey:[.5,.5,.5],lime:[0,1,0],navy:[0,0,.5],maroon:[.5,0,0],olive:[.5,.5,0],teal:[0,.5,.5],silver:[.753,.753,.753],aqua:[0,1,1],fuchsia:[1,0,1],crimson:[.863,.078,.235],gold:[1,.843,0],indigo:[.294,0,.51],coral:[1,.498,.314],salmon:[.98,.502,.447],violet:[.933,.51,.933],turquoise:[.251,.878,.816],khaki:[.941,.902,.549],plum:[.867,.627,.867],orchid:[.855,.439,.839]};function b(i){if(!i||typeof i!="string")return null;const t=i.trim().toLowerCase();if(t.startsWith("#"))return x(t);if(C[t])return C[t];if(/^[0-9a-f]{6}$/i.test(t))return x("#"+t);if(/^[0-9a-f]{3}$/i.test(t)){const e=t.split("").map(r=>r+r).join("");return x("#"+e)}return null}const X=` precision highp float; attribute vec2 aPosition; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform vec2 texelSize; void main () { vUv = aPosition * 0.5 + 0.5; vL = vUv - vec2(texelSize.x, 0.0); vR = vUv + vec2(texelSize.x, 0.0); vT = vUv + vec2(0.0, texelSize.y); vB = vUv - vec2(0.0, texelSize.y); gl_Position = vec4(aPosition, 0.0, 1.0); } `,H=` precision mediump float; precision mediump sampler2D; varying highp vec2 vUv; uniform sampler2D uTexture; uniform float value; void main () { gl_FragColor = value * texture2D(uTexture, vUv); } `,Y=` precision highp float; precision highp sampler2D; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform sampler2D uTexture; uniform vec2 texelSize; vec3 linearToGamma (vec3 color) { color = max(color, vec3(0)); return max(1.055 * pow(color, vec3(0.416666667)) - 0.055, vec3(0)); } void main () { vec3 c = texture2D(uTexture, vUv).rgb; #ifdef SHADING vec3 lc = texture2D(uTexture, vL).rgb; vec3 rc = texture2D(uTexture, vR).rgb; vec3 tc = texture2D(uTexture, vT).rgb; vec3 bc = texture2D(uTexture, vB).rgb; float dx = length(rc) - length(lc); float dy = length(tc) - length(bc); vec3 n = normalize(vec3(dx, dy, length(texelSize))); vec3 l = vec3(0.0, 0.0, 1.0); float diffuse = clamp(dot(n, l) + 0.7, 0.7, 1.0); c *= diffuse; #endif float a = max(c.r, max(c.g, c.b)); gl_FragColor = vec4(c, a); } `,G=` precision highp float; precision highp sampler2D; varying vec2 vUv; uniform sampler2D uTarget; uniform float aspectRatio; uniform vec3 color; uniform vec2 point; uniform float radius; void main () { vec2 p = vUv - point.xy; p.x *= aspectRatio; vec3 splat = exp(-dot(p, p) / radius) * color; vec3 base = texture2D(uTarget, vUv).xyz; gl_FragColor = vec4(base + splat, 1.0); } `,V=` precision highp float; precision highp sampler2D; varying vec2 vUv; uniform sampler2D uVelocity; uniform sampler2D uSource; uniform vec2 texelSize; uniform vec2 dyeTexelSize; uniform float dt; uniform float dissipation; vec4 bilerp (sampler2D sam, vec2 uv, vec2 tsize) { vec2 st = uv / tsize - 0.5; vec2 iuv = floor(st); vec2 fuv = fract(st); vec4 a = texture2D(sam, (iuv + vec2(0.5, 0.5)) * tsize); vec4 b = texture2D(sam, (iuv + vec2(1.5, 0.5)) * tsize); vec4 c = texture2D(sam, (iuv + vec2(0.5, 1.5)) * tsize); vec4 d = texture2D(sam, (iuv + vec2(1.5, 1.5)) * tsize); return mix(mix(a, b, fuv.x), mix(c, d, fuv.x), fuv.y); } void main () { #ifdef MANUAL_FILTERING vec2 coord = vUv - dt * bilerp(uVelocity, vUv, texelSize).xy * texelSize; vec4 result = bilerp(uSource, coord, dyeTexelSize); #else vec2 coord = vUv - dt * texture2D(uVelocity, vUv).xy * texelSize; vec4 result = texture2D(uSource, coord); #endif float decay = 1.0 + dissipation * dt; gl_FragColor = result / decay; } `,W=` precision mediump float; precision mediump sampler2D; varying highp vec2 vUv; varying highp vec2 vL; varying highp vec2 vR; varying highp vec2 vT; varying highp vec2 vB; uniform sampler2D uVelocity; void main () { float L = texture2D(uVelocity, vL).x; float R = texture2D(uVelocity, vR).x; float T = texture2D(uVelocity, vT).y; float B = texture2D(uVelocity, vB).y; vec2 C = texture2D(uVelocity, vUv).xy; if (vL.x < 0.0) { L = -C.x; } if (vR.x > 1.0) { R = -C.x; } if (vT.y > 1.0) { T = -C.y; } if (vB.y < 0.0) { B = -C.y; } float div = 0.5 * (R - L + T - B); gl_FragColor = vec4(div, 0.0, 0.0, 1.0); } `,k=` precision mediump float; precision mediump sampler2D; varying highp vec2 vUv; varying highp vec2 vL; varying highp vec2 vR; varying highp vec2 vT; varying highp vec2 vB; uniform sampler2D uVelocity; void main () { float L = texture2D(uVelocity, vL).y; float R = texture2D(uVelocity, vR).y; float T = texture2D(uVelocity, vT).x; float B = texture2D(uVelocity, vB).x; float vorticity = R - L - T + B; gl_FragColor = vec4(0.5 * vorticity, 0.0, 0.0, 1.0); } `,q=` precision highp float; precision highp sampler2D; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform sampler2D uVelocity; uniform sampler2D uCurl; uniform float curl; uniform float dt; void main () { float L = texture2D(uCurl, vL).x; float R = texture2D(uCurl, vR).x; float T = texture2D(uCurl, vT).x; float B = texture2D(uCurl, vB).x; float C = texture2D(uCurl, vUv).x; vec2 force = 0.5 * vec2(abs(T) - abs(B), abs(R) - abs(L)); force /= length(force) + 0.0001; force *= curl * C; force.y *= -1.0; vec2 velocity = texture2D(uVelocity, vUv).xy; velocity += force * dt; velocity = min(max(velocity, -1000.0), 1000.0); gl_FragColor = vec4(velocity, 0.0, 1.0); } `,$=` precision mediump float; precision mediump sampler2D; varying highp vec2 vUv; varying highp vec2 vL; varying highp vec2 vR; varying highp vec2 vT; varying highp vec2 vB; uniform sampler2D uPressure; uniform sampler2D uDivergence; void main () { float L = texture2D(uPressure, vL).x; float R = texture2D(uPressure, vR).x; float T = texture2D(uPressure, vT).x; float B = texture2D(uPressure, vB).x; float C = texture2D(uPressure, vUv).x; float divergence = texture2D(uDivergence, vUv).x; float pressure = (L + R + B + T - divergence) * 0.25; gl_FragColor = vec4(pressure, 0.0, 0.0, 1.0); } `,K=` precision mediump float; precision mediump sampler2D; varying highp vec2 vUv; varying highp vec2 vL; varying highp vec2 vR; varying highp vec2 vT; varying highp vec2 vB; uniform sampler2D uPressure; uniform sampler2D uVelocity; void main () { float L = texture2D(uPressure, vL).x; float R = texture2D(uPressure, vR).x; float T = texture2D(uPressure, vT).x; float B = texture2D(uPressure, vB).x; vec2 velocity = texture2D(uVelocity, vUv).xy; velocity.xy -= vec2(R - L, T - B); gl_FragColor = vec4(velocity, 0.0, 1.0); } `;class Q{constructor(t,e){this.lastTime=0,this.canvas=t,this.config={...e},console.log(e)}async initialize(){const t=A(this.canvas);if(!t)throw new Error("Failed to initialize WebGL context");this.gl=t.gl,this.ext=t.ext,B(this.gl),this.initializeShaders(),this.initializeFBOs(),this.initializeUtilities(),this.addRandomSplats(Math.floor(Math.random()*20)+5)}initializeShaders(){const t=this.gl,e=X;this.clearProgram=v(t,e,H),this.splatProgram=v(t,e,G),this.divergenceProgram=v(t,e,W),this.curlProgram=v(t,e,k),this.vorticityProgram=v(t,e,q),this.pressureProgram=v(t,e,$),this.gradientSubtractProgram=v(t,e,K);const r=this.ext.supportLinearFiltering?[]:["MANUAL_FILTERING"];this.advectionProgram=D(t,e,V),this.advectionProgram.setKeywords(r),this.displayMaterial=D(t,e,Y)}initializeFBOs(){const t=this.gl,e=w(t,this.config.SIM_RESOLUTION),r=w(t,this.config.DYE_RESOLUTION),o=this.ext.halfFloatTexType,s=this.ext.formatRGBA,a=this.ext.formatRG,h=this.ext.formatR,l=this.ext.supportLinearFiltering?t.LINEAR:t.NEAREST;if(!s||!a||!h)throw new Error("Required texture formats not supported");this.velocityFBO=P(t,e.width,e.height,a.internalFormat,a.format,o,l),this.dyeFBO=P(t,r.width,r.height,s.internalFormat,s.format,o,l),this.pressureFBO=P(t,e.width,e.height,h.internalFormat,h.format,o,t.NEAREST),this.divergenceFBO=T(t,e.width,e.height,h.internalFormat,h.format,o,t.NEAREST),this.curlFBO=T(t,e.width,e.height,h.internalFormat,h.format,o,t.NEAREST)}initializeUtilities(){if(this.colorManager=new I(this.config.COLOR_MODE),this.config.COLOR){const t=b(this.config.COLOR);t?this.colorManager.setFixedColor(t):console.warn(`Invalid color value: ${this.config.COLOR}`)}this.pointerTracker=new _(this.canvas,{onSplat:t=>{this.splat(t.texcoordX,t.texcoordY,t.deltaX*this.config.SPLAT_FORCE,t.deltaY*this.config.SPLAT_FORCE,t.color)}})}step(){const t=this.calcDeltaTime();if(this.config.PAUSED)return;if(!this.colorManager||!this.pointerTracker){console.warn("Simulation not fully initialized, skipping step");return}const e=this.colorManager.updateColors(t,this.config.COLORFUL,this.config.COLOR_UPDATE_SPEED,this.config.COLOR_TRANSITION_SPEED);this.pointerTracker.setPointerColor(e),this.pointerTracker.updatePointers(.001),this.stepFluid(t)}stepFluid(t){const e=this.gl;e.disable(e.BLEND),this.curlProgram.bind(),e.uniform2f(this.curlProgram.uniforms.texelSize,this.velocityFBO.read.texelSizeX,this.velocityFBO.read.texelSizeY),e.uniform1i(this.curlProgram.uniforms.uVelocity,this.velocityFBO.read.attach(0)),f(e,this.curlFBO),this.vorticityProgram.bind(),e.uniform2f(this.vorticityProgram.uniforms.texelSize,this.velocityFBO.read.texelSizeX,this.velocityFBO.read.texelSizeY),e.uniform1i(this.vorticityProgram.uniforms.uVelocity,this.velocityFBO.read.attach(0)),e.uniform1i(this.vorticityProgram.uniforms.uCurl,this.curlFBO.attach(1)),e.uniform1f(this.vorticityProgram.uniforms.curl,this.config.CURL),e.uniform1f(this.vorticityProgram.uniforms.dt,t),f(e,this.velocityFBO.write),this.velocityFBO.swap(),this.divergenceProgram.bind(),e.uniform2f(this.divergenceProgram.uniforms.texelSize,this.velocityFBO.read.texelSizeX,this.velocityFBO.read.texelSizeY),e.uniform1i(this.divergenceProgram.uniforms.uVelocity,this.velocityFBO.read.attach(0)),f(e,this.divergenceFBO),this.clearProgram.bind(),e.uniform1i(this.clearProgram.uniforms.uTexture,this.pressureFBO.read.attach(0)),e.uniform1f(this.clearProgram.uniforms.value,this.config.PRESSURE),f(e,this.pressureFBO.write),this.pressureFBO.swap(),this.pressureProgram.bind(),e.uniform2f(this.pressureProgram.uniforms.texelSize,this.velocityFBO.read.texelSizeX,this.velocityFBO.read.texelSizeY),e.uniform1i(this.pressureProgram.uniforms.uDivergence,this.divergenceFBO.attach(0));for(let r=0;r<this.config.PRESSURE_ITERATIONS;r++)e.uniform1i(this.pressureProgram.uniforms.uPressure,this.pressureFBO.read.attach(1)),f(e,this.pressureFBO.write),this.pressureFBO.swap();this.gradientSubtractProgram.bind(),e.uniform2f(this.gradientSubtractProgram.uniforms.texelSize,this.velocityFBO.read.texelSizeX,this.velocityFBO.read.texelSizeY),e.uniform1i(this.gradientSubtractProgram.uniforms.uPressure,this.pressureFBO.read.attach(0)),e.uniform1i(this.gradientSubtractProgram.uniforms.uVelocity,this.velocityFBO.read.attach(1)),f(e,this.velocityFBO.write),this.velocityFBO.swap(),this.advectionProgram.bind(),e.uniform2f(this.advectionProgram.uniforms.texelSize,this.velocityFBO.read.texelSizeX,this.velocityFBO.read.texelSizeY),this.ext.supportLinearFiltering||e.uniform2f(this.advectionProgram.uniforms.dyeTexelSize,this.velocityFBO.read.texelSizeX,this.velocityFBO.read.texelSizeY),e.uniform1i(this.advectionProgram.uniforms.uVelocity,this.velocityFBO.read.attach(0)),e.uniform1i(this.advectionProgram.uniforms.uSource,this.velocityFBO.read.attach(0)),e.uniform1f(this.advectionProgram.uniforms.dt,t),e.uniform1f(this.advectionProgram.uniforms.dissipation,this.config.VELOCITY_DISSIPATION),f(e,this.velocityFBO.write),this.velocityFBO.swap(),e.uniform2f(this.advectionProgram.uniforms.texelSize,this.velocityFBO.read.texelSizeX,this.velocityFBO.read.texelSizeY),this.ext.supportLinearFiltering||e.uniform2f(this.advectionProgram.uniforms.dyeTexelSize,this.dyeFBO.read.texelSizeX,this.dyeFBO.read.texelSizeY),e.uniform1i(this.advectionProgram.uniforms.uVelocity,this.velocityFBO.read.attach(0)),e.uniform1i(this.advectionProgram.uniforms.uSource,this.dyeFBO.read.attach(1)),e.uniform1f(this.advectionProgram.uniforms.dt,t),e.uniform1f(this.advectionProgram.uniforms.dissipation,this.config.DENSITY_DISSIPATION),f(e,this.dyeFBO.write),this.dyeFBO.swap()}render(){const t=this.gl;t.blendFunc(t.ONE,t.ONE_MINUS_SRC_ALPHA),t.enable(t.BLEND);const e=t.drawingBufferWidth,r=t.drawingBufferHeight;this.displayMaterial.bind(),this.config.SHADING?this.displayMaterial.setKeywords(["SHADING"]):this.displayMaterial.setKeywords([]),this.displayMaterial.bind(),this.config.SHADING&&t.uniform2f(this.displayMaterial.uniforms.texelSize,1/e,1/r),t.uniform1i(this.displayMaterial.uniforms.uTexture,this.dyeFBO.read.attach(0)),f(t,null)}splat(t,e,r,o,s){const a=this.gl,h=s||this.colorManager.generateRandomColor();this.splatProgram.bind(),a.uniform1i(this.splatProgram.uniforms.uTarget,this.velocityFBO.read.attach(0)),a.uniform1f(this.splatProgram.uniforms.aspectRatio,this.canvas.width/this.canvas.height),a.uniform2f(this.splatProgram.uniforms.point,t,e),a.uniform3f(this.splatProgram.uniforms.color,r,o,0),a.uniform1f(this.splatProgram.uniforms.radius,this.config.SPLAT_RADIUS/100),f(a,this.velocityFBO.write),this.velocityFBO.swap(),a.uniform1i(this.splatProgram.uniforms.uTarget,this.dyeFBO.read.attach(0)),a.uniform3f(this.splatProgram.uniforms.color,h[0],h[1],h[2]),f(a,this.dyeFBO.write),this.dyeFBO.swap()}addRandomSplats(t){for(let e=0;e<t;e++){const r=this.colorManager.generateRandomColor(),o=Math.random(),s=Math.random(),a=this.config.SPLAT_FORCE*.1*(Math.random()-.5),h=this.config.SPLAT_FORCE*.1*(Math.random()-.5);this.splat(o,s,a,h,r)}}updateConfig(t){if(this.config={...this.config,...t},t.COLOR_MODE&&this.colorManager&&this.colorManager.setColorMode(t.COLOR_MODE),t.COLOR!==void 0&&this.colorManager)if(t.COLOR){const e=b(t.COLOR);e?this.colorManager.setFixedColor(e):(console.warn(`Invalid color value: ${t.COLOR}`),this.colorManager.clearFixedColor())}else this.colorManager.clearFixedColor()}handleResize(t,e){this.initializeFBOs()}calcDeltaTime(){const t=Date.now();let e=(t-this.lastTime)/1e3;return e=Math.min(e,.016666),this.lastTime=t,e}destroy(){var t;(t=this.pointerTracker)==null||t.destroy()}}var J=Object.defineProperty,Z=Object.getOwnPropertyDescriptor,c=(i,t,e,r)=>{for(var o=r>1?void 0:r?Z(t,e):t,s=i.length-1,a;s>=0;s--)(a=i[s])&&(o=(r?a(t,e,o):a(o))||o);return r&&o&&J(t,e,o),o};n.FluidPointer=class extends m.LitElement{constructor(){super(...arguments),this.width="100%",this.height="400px",this.aspectRatio="",this.minWidth="200px",this.minHeight="150px",this.maxWidth="none",this.maxHeight="none",this.responsive=!1,this.maintainAspectRatio=!1,this.simResolution=128,this.dyeResolution=1024,this.densityDissipation=3.5,this.velocityDissipation=5,this.pressure=.8,this.pressureIterations=20,this.curl=3,this.splatRadius=.25,this.splatForce=6e3,this.mouseInteraction=!0,this.touchInteraction=!0,this.interactionMode="movement",this.velocityThreshold=.001,this.shading=!0,this.colorful=!0,this.colorUpdateSpeed=2,this.colorTransitionSpeed=.2,this.colorMode="rainbow",this.color="",this.paused=!1,this.autoStart=!0}firstUpdated(t){super.firstUpdated(t),this.updateCSSVariables(),this.setupResizeObserver(),this.autoStart&&this.initializeFluidSimulation()}updated(t){const e=["width","height","aspectRatio","minWidth","minHeight","maxWidth","maxHeight"];this.hasPropertyChanged(t,e)&&this.updateCSSVariables(),super.updated(t),this.fluidSimulation&&this.hasPropertyChanged(t,["densityDissipation","velocityDissipation","pressure","pressureIterations","curl","splatRadius","splatForce","colorUpdateSpeed","colorTransitionSpeed","colorful","colorMode","color","shading"])&&this.updateSimulationConfig(),t.has("paused")&&(this.paused?this.pauseSimulation():this.resumeSimulation())}hasPropertyChanged(t,e){return e.some(r=>t.has(r))}updateCSSVariables(){const t=this;t.style.setProperty("--fluid-width",this.width),t.style.setProperty("--fluid-height",this.height),t.style.setProperty("--fluid-min-width",this.minWidth),t.style.setProperty("--fluid-min-height",this.minHeight),t.style.setProperty("--fluid-max-width",this.maxWidth),t.style.setProperty("--fluid-max-height",this.maxHeight),this.aspectRatio&&t.style.setProperty("--fluid-aspect-ratio",this.aspectRatio.replace(":"," / "))}setupResizeObserver(){this.resizeObserver=new ResizeObserver(t=>{for(const e of t)this.handleResize(e.contentRect)}),this.resizeObserver.observe(this)}handleResize(t){var e,r;if(this.canvas&&this.fluidSimulation){const o=window.devicePixelRatio||1;this.canvas.width=t.width*o,this.canvas.height=t.height*o;try{(r=(e=this.fluidSimulation).handleResize)==null||r.call(e,t.width,t.height)}catch(s){console.error("Error during resize:",s),this.initializeFluidSimulation()}}}async initializeFluidSimulation(){try{this.fluidSimulation=new Q(this.canvas,this.getSimulationConfig()),await this.fluidSimulation.initialize(),this.autoStart&&this.startAnimationLoop();const t=new CustomEvent("fluid-ready",{detail:{width:this.canvas.width,height:this.canvas.height}});this.dispatchEvent(t)}catch(t){console.error("Failed to initialize fluid simulation:",t)}}getSimulationConfig(){return console.log("Getting simulation config"),console.log("Density dissipation:",this.densityDissipation),console.log("html density-dissipation",this.getAttribute("density-dissipation")),{SIM_RESOLUTION:this.simResolution,DYE_RESOLUTION:this.dyeResolution,DENSITY_DISSIPATION:this.densityDissipation,VELOCITY_DISSIPATION:this.velocityDissipation,PRESSURE:this.pressure,PRESSURE_ITERATIONS:this.pressureIterations,CURL:this.curl,SPLAT_RADIUS:this.splatRadius,SPLAT_FORCE:this.splatForce,SHADING:this.shading,COLORFUL:this.colorful,COLOR_UPDATE_SPEED:this.colorUpdateSpeed,COLOR_TRANSITION_SPEED:this.colorTransitionSpeed,COLOR_MODE:this.colorMode,COLOR:this.color,PAUSED:this.paused}}updateSimulationConfig(){this.fluidSimulation&&this.fluidSimulation.updateConfig(this.getSimulationConfig())}pauseSimulation(){this.animationId&&(cancelAnimationFrame(this.animationId),this.animationId=void 0)}resumeSimulation(){!this.animationId&&this.fluidSimulation&&this.startAnimationLoop()}startAnimationLoop(){if(!this.fluidSimulation||this.paused)return;const t=()=>{if(!(this.paused||!this.fluidSimulation)){try{this.fluidSimulation.step(),this.fluidSimulation.render()}catch(e){console.error("Error in animation loop:",e),this.pauseSimulation();return}this.animationId=requestAnimationFrame(t)}};this.animationId=requestAnimationFrame(t)}play(){this.paused=!1}pause(){this.paused=!0}reset(){this.initializeFluidSimulation()}addSplat(t,e,r,o,s){if(this.fluidSimulation){this.fluidSimulation.splat(t,e,r,o,s);const a=new CustomEvent("fluid-splat",{detail:{x:t,y:e,velocityX:r,velocityY:o,color:s||[1,1,1],force:Math.sqrt(r*r+o*o)}});this.dispatchEvent(a)}}addRandomSplats(t){for(let e=0;e<t;e++)this.addSplat(Math.random(),Math.random(),(Math.random()-.5)*this.splatForce*.1,(Math.random()-.5)*this.splatForce*.1)}connectedCallback(){super.connectedCallback(),console.log(this.getAttribute("density-dissipation")),console.log(this.densityDissipation)}disconnectedCallback(){var t;super.disconnectedCallback(),(t=this.resizeObserver)==null||t.disconnect(),this.pauseSimulation()}render(){return m.html`<canvas></canvas>`}},n.FluidPointer.styles=m.css` :host { display: block; width: var(--fluid-width); height: var(--fluid-height); min-width: var(--fluid-min-width); min-height: var(--fluid-min-height); max-width: var(--fluid-max-width); max-height: var(--fluid-max-height); aspect-ratio: var(--fluid-aspect-ratio, auto); contain: layout style paint; box-sizing: border-box; position: relative; overflow: hidden; cursor: var(--fluid-cursor, crosshair); } canvas { width: 100%; height: 100%; display: block; object-fit: cover; } :host([responsive]) { width: 100%; height: auto; } :host([aspect-ratio="16:9"]) { aspect-ratio: 16 / 9; } :host([aspect-ratio="1:1"]) { aspect-ratio: 1 / 1; } :host([aspect-ratio="4:3"]) { aspect-ratio: 4 / 3; } :host([paused]) { cursor: var(--fluid-cursor-paused, default); } `,c([u.property({type:String})],n.FluidPointer.prototype,"width",2),c([u.property({type:String})],n.FluidPointer.prototype,"height",2),c([u.property({type:String})],n.FluidPointer.prototype,"aspectRatio",2),c([u.property({type:String})],n.FluidPointer.prototype,"minWidth",2),c([u.property({type:String})],n.FluidPointer.prototype,"minHeight",2),c([u.property({type:String})],n.FluidPointer.prototype,"maxWidth",2),c([u.property({type:String})],n.FluidPointer.prototype,"maxHeight",2),c([u.property({type:Boolean})],n.FluidPointer.prototype,"responsive",2),c([u.property({type:Boolean})],n.FluidPointer.prototype,"maintainAspectRatio",2),c([u.property({type:Number,attribute:"sim-resolution"})],n.FluidPointer.prototype,"simResolution",2),c([u.property({type:Number,attribute:"dye-resolution"})],n.FluidPointer.prototype,"dyeResolution",2),c([u.property({type:Number,attribute:"density-dissipation"})],n.FluidPointer.prototype,"densityDissipation",2),c([u.property({type:Number,attribute:"velocity-dissipation"})],n.FluidPointer.prototype,"velocityDissipation",2),c([u.property({type:Number})],n.FluidPointer.prototype,"pressure",2),c([u.property({type:Number,attribute:"pressure-iterations"})],n.FluidPointer.prototype,"pressureIterations",2),c([u.property({type:Number})],n.FluidPointer.prototype,"curl",2),c([u.property({type:Number})],n.FluidPointer.prototype,"splatRadius",2),c([u.property({type:Number,attribute:"splat-force"})],n.FluidPointer.prototype,"splatForce",2),c([u.property({type:Boolean})],n.FluidPointer.prototype,"mouseInteraction",2),c([u.property({type:Boolean})],n.FluidPointer.prototype,"touchInteraction",2),c([u.property({type:String,attribute:"interaction-mode"})],n.FluidPointer.prototype,"interactionMode",2),c([u.property({type:Number,attribute:"velocity-threshold"})],n.FluidPointer.prototype,"velocityThreshold",2),c([u.property({type:Boolean})],n.FluidPointer.prototype,"shading",2),c([u.property({type:Boolean})],n.FluidPointer.prototype,"colorful",2),c([u.property({type:Number,attribute:"color-update-speed"})],n.FluidPointer.prototype,"colorUpdateSpeed",2),c([u.property({type:Number,attribute:"color-transition-speed"})],n.FluidPointer.prototype,"colorTransitionSpeed",2),c([u.property({type:String,attribute:"color-mode"})],n.FluidPointer.prototype,"colorMode",2),c([u.property({type:String})],n.FluidPointer.prototype,"color",2),c([u.property({type:Boolean})],n.FluidPointer.prototype,"paused",2),c([u.property({type:Boolean})],n.FluidPointer.prototype,"autoStart",2),c([u.query("canvas")],n.FluidPointer.prototype,"canvas",2),n.FluidPointer=c([u.customElement("fluid-pointer")],n.FluidPointer);const j={low:{simResolution:64,dyeResolution:512,pressureIterations:10},medium:{simResolution:128,dyeResolution:1024,pressureIterations:20},high:{simResolution:256,dyeResolution:2048,pressureIterations:30},ultra:{simResolution:512,dyeResolution:4096,pressureIterations:40}};n.QUALITY_PRESETS=j,n.hexToRgb=x,n.parseCSSColor=b,n.parseColorArray=N,n.rgbToHex=z,Object.defineProperty(n,Symbol.toStringTag,{value:"Module"})}); //# sourceMappingURL=fluid-pointer.umd.js.map