p5 2.2.2-rc.2 → 2.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/accessibility/color_namer.js +4 -4
- package/dist/accessibility/index.js +4 -4
- package/dist/app.js +4 -4
- package/dist/color/color_conversion.js +4 -4
- package/dist/color/index.js +1 -1
- package/dist/color/setting.js +1 -1
- package/dist/{constants--VqmEGYW.js → constants-BxjhKpTv.js} +1 -1
- package/dist/core/constants.js +1 -1
- package/dist/core/environment.js +1 -1
- package/dist/core/filterShaders.js +1 -1
- package/dist/core/friendly_errors/fes_core.js +1 -1
- package/dist/core/friendly_errors/file_errors.js +1 -1
- package/dist/core/friendly_errors/index.js +1 -1
- package/dist/core/friendly_errors/param_validator.js +1 -1
- package/dist/core/friendly_errors/sketch_verifier.js +1 -1
- package/dist/core/helpers.js +1 -1
- package/dist/core/init.js +4 -4
- package/dist/core/internationalization.js +1 -1
- package/dist/core/legacy.js +4 -4
- package/dist/core/main.js +4 -4
- package/dist/core/p5.Graphics.js +3 -3
- package/dist/core/p5.Renderer.js +2 -2
- package/dist/core/p5.Renderer2D.js +4 -4
- package/dist/core/p5.Renderer3D.js +3 -3
- package/dist/core/rendering.js +3 -3
- package/dist/dom/dom.js +1 -1
- package/dist/dom/index.js +1 -1
- package/dist/dom/p5.Element.js +1 -1
- package/dist/dom/p5.MediaElement.js +1 -1
- package/dist/image/const.js +1 -1
- package/dist/image/filterRenderer2D.js +3 -3
- package/dist/image/image.js +3 -3
- package/dist/image/index.js +3 -3
- package/dist/image/loading_displaying.js +3 -3
- package/dist/image/p5.Image.js +2 -2
- package/dist/io/files.js +3 -3
- package/dist/io/index.js +3 -3
- package/dist/{main-DifEeQiX.js → main-DDs4QOnh.js} +3 -3
- package/dist/math/Matrices/Matrix.js +1 -1
- package/dist/math/Matrices/MatrixNumjs.js +1 -1
- package/dist/math/index.js +1 -1
- package/dist/math/p5.Matrix.js +1 -1
- package/dist/math/p5.Vector.js +1 -1
- package/dist/math/trigonometry.js +1 -1
- package/dist/{p5.Renderer-BuaG4snH.js → p5.Renderer-BSGddFv7.js} +1 -1
- package/dist/{rendering-uHkWQXtu.js → rendering-C9g7uSQ5.js} +2 -2
- package/dist/shape/2d_primitives.js +1 -1
- package/dist/shape/attributes.js +1 -1
- package/dist/shape/custom_shapes.js +1 -1
- package/dist/shape/index.js +1 -1
- package/dist/type/index.js +2 -2
- package/dist/type/p5.Font.js +2 -2
- package/dist/type/textCore.js +2 -2
- package/dist/webgl/3d_primitives.js +3 -3
- package/dist/webgl/GeometryBuilder.js +1 -1
- package/dist/webgl/ShapeBuilder.js +1 -1
- package/dist/webgl/enums.js +1 -1
- package/dist/webgl/index.js +3 -3
- package/dist/webgl/interaction.js +1 -1
- package/dist/webgl/light.js +3 -3
- package/dist/webgl/loading.js +3 -3
- package/dist/webgl/material.js +3 -3
- package/dist/webgl/p5.Camera.js +3 -3
- package/dist/webgl/p5.Framebuffer.js +3 -3
- package/dist/webgl/p5.Geometry.js +1 -1
- package/dist/webgl/p5.Quat.js +1 -1
- package/dist/webgl/p5.RendererGL.js +3 -3
- package/dist/webgl/p5.Texture.js +3 -3
- package/dist/webgl/text.js +3 -3
- package/dist/webgl/utils.js +3 -3
- package/dist/webgpu/index.js +1 -1
- package/dist/webgpu/p5.RendererWebGPU.js +1 -1
- package/lib/p5.esm.js +2 -2
- package/lib/p5.esm.min.js +1 -1
- package/lib/p5.js +2 -2
- package/lib/p5.min.js +1 -1
- package/lib/p5.webgpu.esm.js +1 -1
- package/lib/p5.webgpu.js +1 -1
- package/lib/p5.webgpu.min.js +1 -1
- package/package.json +1 -1
package/lib/p5.webgpu.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(){"use strict";const e=Math.PI,t="webgpu",n=e/2,r=e,i=e/4,o=2*e,s=2*e,a=e/180,u=180/e,l="source-over",p="destination-out",f="lighter",c="darken",d="lighten",m="subtract",h="exclusion",g="multiply",x="screen",v="copy",T="linear",y="nearest",b="repeat",C="clamp",_="mirror",O="unsigned-byte",w="unsigned-int",S="float",F="half-float",A=Symbol("include"),I=Symbol("exclude"),L=Symbol("join");var P=Object.freeze({__proto__:null,ADD:f,ALT:"Alt",ARROW:"default",AUTO:"auto",AXES:"axes",BACKSPACE:"Backspace",BASELINE:"alphabetic",BEVEL:"bevel",BEZIER:"bezier",BLEND:l,BLUR:"blur",BOLD:"bold",BOLDITALIC:"bold italic",BOTTOM:"bottom",BURN:"color-burn",CENTER:"center",CHAR:"CHAR",CHORD:"chord",CLAMP:C,CLOSE:"close",CONTAIN:"contain",CONTROL:"Control",CORNER:"corner",CORNERS:"corners",COVER:"cover",CROSS:"crosshair",CURVE:"curve",DARKEST:c,DEG_TO_RAD:a,DELETE:"Delete",DIFFERENCE:"difference",DILATE:"dilate",DODGE:"color-dodge",DOWN_ARROW:"ArrowDown",EMPTY_PATH:7,ENTER:"Enter",ERODE:"erode",ESCAPE:"Escape",EXCLUDE:I,EXCLUSION:h,FALLBACK:"fallback",FILL:"fill",FLAT:"flat",FLOAT:S,FULL:"full",GRAY:"gray",GRID:"grid",HALF_FLOAT:F,HALF_PI:n,HAND:"pointer",HARD_LIGHT:"hard-light",IMAGE:"image",IMMEDIATE:"immediate",INCLUDE:A,INVERT:"invert",ITALIC:"italic",JOIN:L,LABEL:"label",LANDSCAPE:"landscape",LEFT:"left",LEFT_ARROW:"ArrowLeft",LIGHTEST:d,LINEAR:T,LINEAR_MIPMAP:"linear_mipmap",LINES:1,LINE_LOOP:2,LINE_STRIP:3,MIRROR:_,MITER:"miter",MOVE:"move",MULTIPLY:g,NEAREST:y,NORMAL:"normal",OPAQUE:"opaque",OPEN:"open",OPTION:"Alt",OVERLAY:"overlay",P2D:"p2d",P2DHDR:"p2d-hdr",PATH:8,PI:r,PIE:"pie",POINTS:0,PORTRAIT:"portrait",POSTERIZE:"posterize",PROJECT:"square",QUADRATIC:"quadratic",QUADS:"quads",QUAD_STRIP:"quad_strip",QUARTER_PI:i,RADIUS:"radius",RAD_TO_DEG:u,REMOVE:p,REPEAT:b,REPLACE:v,RETURN:"Enter",RIGHT:"right",RIGHT_ARROW:"ArrowRight",ROUND:"round",SCREEN:x,SHIFT:"Shift",SIMPLE:"simple",SMOOTH:"smooth",SOFT_LIGHT:"soft-light",SQUARE:"butt",STROKE:"stroke",SUBTRACT:m,TAB:"Tab",TAU:o,TESS:"tess",TEXT:"text",TEXTURE:"texture",THRESHOLD:"threshold",TOP:"top",TRIANGLES:4,TRIANGLE_FAN:6,TRIANGLE_STRIP:5,TWO_PI:s,UNSIGNED_BYTE:O,UNSIGNED_INT:w,UP_ARROW:"ArrowUp",VERSION:"2.2.2-rc.2",WAIT:"wait",WEBGL:"webgl",WEBGL2:"webgl2",WEBGPU:t,WORD:"WORD",_CTX_MIDDLE:"middle",_DEFAULT_FILL:"#FFFFFF",_DEFAULT_LEADMULT:1.25,_DEFAULT_STROKE:"#000000",_DEFAULT_TEXT_FILL:"#000000"});const E={OPERATION:"operation",LITERAL:"literal",VARIABLE:"variable",CONSTANT:"constant",STRUCT:"struct",PHI:"phi",STATEMENT:"statement",ASSIGNMENT:"assignment"},D=Object.fromEntries(Object.entries(E).map((([e,t])=>[t,e]))),R={[E.OPERATION]:["opCode","dependsOn","dimension","baseType"],[E.LITERAL]:["value","dimension","baseType"],[E.VARIABLE]:["identifier","dimension","baseType"],[E.CONSTANT]:["value","dimension","baseType"],[E.STRUCT]:[""],[E.PHI]:["dependsOn","phiBlocks","dimension","baseType"],[E.STATEMENT]:["statementType"],[E.ASSIGNMENT]:["dependsOn"]},V="discard",N="break",M="early_return",U="expression",B="empty",k="float",z="int",G="bool",$="mat",j="defer",H="sampler2D",W="sampler",q={[k]:3,[z]:2,[G]:1,[$]:0,[j]:-1,[H]:-10,[W]:-11},K={float1:{fnName:"float",baseType:k,dimension:1,priority:3},float2:{fnName:"vec2",baseType:k,dimension:2,priority:3},float3:{fnName:"vec3",baseType:k,dimension:3,priority:3},float4:{fnName:"vec4",baseType:k,dimension:4,priority:3},int1:{fnName:"int",baseType:z,dimension:1,priority:2},int2:{fnName:"ivec2",baseType:z,dimension:2,priority:2},int3:{fnName:"ivec3",baseType:z,dimension:3,priority:2},int4:{fnName:"ivec4",baseType:z,dimension:4,priority:2},bool1:{fnName:"bool",baseType:G,dimension:1,priority:1},bool2:{fnName:"bvec2",baseType:G,dimension:2,priority:1},bool3:{fnName:"bvec3",baseType:G,dimension:3,priority:1},bool4:{fnName:"bvec4",baseType:G,dimension:4,priority:1},mat2:{fnName:"mat2x2",baseType:$,dimension:2,priority:0},mat3:{fnName:"mat3x3",baseType:$,dimension:3,priority:0},mat4:{fnName:"mat4x4",baseType:$,dimension:4,priority:0},defer:{fnName:null,baseType:j,dimension:null,priority:-1},sampler2D:{fnName:"sampler2D",baseType:H,dimension:1,priority:-10},sampler:{fnName:"sampler",baseType:W,dimension:1,priority:-11}};const Y={FLOAT:{baseType:k,dimension:null,priority:3},INT:{baseType:z,dimension:null,priority:2},BOOL:{baseType:G,dimension:null,priority:1}};Object.fromEntries(Object.values(K).filter((e=>null!==e.fnName)).map((e=>[e.fnName,e])));const X={ADD:0,SUBTRACT:1,MULTIPLY:2,DIVIDE:3,MODULO:4,EQUAL:5,NOT_EQUAL:6,GREATER_THAN:7,GREATER_EQUAL:8,LESS_THAN:9,LESS_EQUAL:10,LOGICAL_AND:11,LOGICAL_OR:12,MEMBER_ACCESS:13},Q={LOGICAL_NOT:100,NEGATE:101,PLUS:102,SWIZZLE:103},J={FUNCTION_CALL:200,CONSTRUCTOR:201},Z=[{arity:"unary",name:"not",symbol:"!",opCode:Q.LOGICAL_NOT},{arity:"unary",name:"neg",symbol:"-",opCode:Q.NEGATE},{arity:"unary",name:"plus",symbol:"+",opCode:Q.PLUS},{arity:"binary",name:"add",symbol:"+",opCode:X.ADD},{arity:"binary",name:"sub",symbol:"-",opCode:X.SUBTRACT},{arity:"binary",name:"mult",symbol:"*",opCode:X.MULTIPLY},{arity:"binary",name:"div",symbol:"/",opCode:X.DIVIDE},{arity:"binary",name:"mod",symbol:"%",opCode:X.MODULO},{arity:"binary",name:"equalTo",symbol:"==",opCode:X.EQUAL},{arity:"binary",name:"notEqual",symbol:"!=",opCode:X.NOT_EQUAL},{arity:"binary",name:"greaterThan",symbol:">",opCode:X.GREATER_THAN},{arity:"binary",name:"greaterEqual",symbol:">=",opCode:X.GREATER_EQUAL},{arity:"binary",name:"lessThan",symbol:"<",opCode:X.LESS_THAN},{arity:"binary",name:"lessEqual",symbol:"<=",opCode:X.LESS_EQUAL},{arity:"binary",name:"and",symbol:"&&",opCode:X.LOGICAL_AND},{arity:"binary",name:"or",symbol:"||",opCode:X.LOGICAL_OR}],ee={};for(const{symbol:e,opCode:t,name:n,arity:r}of Z)ee[t]=e;const te={GLOBAL:"global",FUNCTION:"function",BRANCH:"branch",IF_COND:"if_cond",IF_BODY:"if_body",ELSE_COND:"else_cond",SCOPE_START:"scope_start",SCOPE_END:"scope_end",FOR:"for",MERGE:"merge",DEFAULT:"default"};Object.fromEntries(Object.entries(te).map((([e,t])=>[t,e])));const ne="\n// Group 0: Material Properties\nstruct MaterialUniforms {\n uUseVertexColor: u32,\n}\n\n// Group 1: Model Transform\nstruct ModelUniforms {\n// @p5 ifdef Vertex getWorldInputs\n uModelMatrix: mat4x4<f32>,\n uModelNormalMatrix: mat3x3<f32>,\n// @p5 endif\n// @p5 ifndef Vertex getWorldInputs\n uModelViewMatrix: mat4x4<f32>,\n uNormalMatrix: mat3x3<f32>,\n// @p5 endif\n uMaterialColor: vec4<f32>,\n}\n\n// Group 2: Camera and Projection\nstruct CameraUniforms {\n uProjectionMatrix: mat4x4<f32>,\n// @p5 ifdef Vertex getWorldInputs\n uViewMatrix: mat4x4<f32>,\n// @p5 endif\n uCameraNormalMatrix: mat3x3<f32>,\n}\n",re=`\nstruct VertexInput {\n @location(0) aPosition: vec3<f32>,\n @location(1) aNormal: vec3<f32>,\n @location(2) aTexCoord: vec2<f32>,\n @location(3) aVertexColor: vec4<f32>,\n};\n\nstruct VertexOutput {\n @builtin(position) Position: vec4<f32>,\n @location(0) vVertexNormal: vec3<f32>,\n @location(1) vVertTexCoord: vec2<f32>,\n @location(2) vColor: vec4<f32>,\n};\n\n${ne}\n@group(0) @binding(0) var<uniform> material: MaterialUniforms;\n@group(1) @binding(0) var<uniform> model: ModelUniforms;\n@group(2) @binding(0) var<uniform> camera: CameraUniforms;\n\nstruct Vertex {\n position: vec3<f32>,\n normal: vec3<f32>,\n texCoord: vec2<f32>,\n color: vec4<f32>,\n}\n\n@vertex\nfn main(input: VertexInput) -> VertexOutput {\n HOOK_beforeVertex();\n var output: VertexOutput;\n\n let useVertexColor = (material.uUseVertexColor != 0 && input.aVertexColor.x >= 0.0);\n var inputs = Vertex(\n input.aPosition,\n input.aNormal,\n input.aTexCoord,\n select(model.uMaterialColor, input.aVertexColor, useVertexColor)\n );\n\n// @p5 ifdef Vertex getObjectInputs\n inputs = HOOK_getObjectInputs(inputs);\n// @p5 endif\n\n// @p5 ifdef Vertex getWorldInputs\n inputs.position = (model.uModelMatrix * vec4<f32>(inputs.position, 1.0)).xyz;\n inputs.normal = model.uModelNormalMatrix * inputs.normal;\n inputs = HOOK_getWorldInputs(inputs);\n// @p5 endif\n\n// @p5 ifdef Vertex getWorldInputs\n // Already multiplied by the model matrix, just apply view\n inputs.position = (camera.uViewMatrix * vec4<f32>(inputs.position, 1.0)).xyz;\n inputs.normal = camera.uCameraNormalMatrix * inputs.normal;\n// @p5 endif\n// @p5 ifndef Vertex getWorldInputs\n // Apply both at once\n inputs.position = (model.uModelViewMatrix * vec4<f32>(inputs.position, 1.0)).xyz;\n inputs.normal = model.uNormalMatrix * inputs.normal;\n// @p5 endif\n\n// @p5 ifdef Vertex getCameraInputs\n inputs = HOOK_getCameraInputs(inputs);\n// @p5 endif\n\n output.vVertTexCoord = inputs.texCoord;\n output.vVertexNormal = normalize(inputs.normal);\n output.vColor = inputs.color;\n\n output.Position = camera.uProjectionMatrix * vec4<f32>(inputs.position, 1.0);\n\n HOOK_afterVertex();\n return output;\n}\n`,ie=`\nstruct FragmentInput {\n @location(0) vVertexNormal: vec3<f32>,\n @location(1) vVertTexCoord: vec2<f32>,\n @location(2) vColor: vec4<f32>,\n};\n\n${ne}\n@group(0) @binding(0) var<uniform> material: MaterialUniforms;\n@group(1) @binding(0) var<uniform> model: ModelUniforms;\n@group(2) @binding(0) var<uniform> camera: CameraUniforms;\n\n\n@fragment\nfn main(input: FragmentInput) -> @location(0) vec4<f32> {\n HOOK_beforeFragment();\n var outColor = HOOK_getFinalColor(input.vColor);\n outColor = vec4<f32>(outColor.rgb * outColor.a, outColor.a);\n HOOK_afterFragment();\n return outColor;\n}\n`,oe="\n// Group 0: Stroke Properties\nstruct StrokeUniforms {\n uStrokeWeight: f32,\n uUseLineColor: f32,\n uSimpleLines: f32,\n uStrokeCap: u32,\n uStrokeJoin: u32,\n}\n\n// Group 1: Model Transform\nstruct ModelUniforms {\n// @p5 ifdef StrokeVertex getWorldInputs\n uModelMatrix: mat4x4<f32>,\n uViewMatrix: mat4x4<f32>,\n// @p5 endif\n// @p5 ifndef StrokeVertex getWorldInputs\n uModelViewMatrix: mat4x4<f32>,\n// @p5 endif\n uMaterialColor: vec4<f32>,\n}\n\n// Group 2: Camera and Projection\nstruct CameraUniforms {\n uProjectionMatrix: mat4x4<f32>,\n uViewport: vec4<f32>,\n uPerspective: u32,\n}\n",se=`\nstruct StrokeVertexInput {\n @location(0) aPosition: vec3<f32>,\n @location(1) aSide: f32,\n @location(2) aTangentIn: vec3<f32>,\n @location(3) aTangentOut: vec3<f32>,\n @location(4) aVertexColor: vec4<f32>,\n};\n\nstruct StrokeVertexOutput {\n @builtin(position) Position: vec4<f32>,\n @location(0) vColor: vec4<f32>,\n @location(1) vTangent: vec2<f32>,\n @location(2) vCenter: vec2<f32>,\n @location(3) vPosition: vec2<f32>,\n @location(4) vMaxDist: f32,\n @location(5) vCap: f32,\n @location(6) vJoin: f32,\n @location(7) vStrokeWeight: f32,\n};\n\n${oe}\n@group(0) @binding(0) var<uniform> stroke: StrokeUniforms;\n@group(1) @binding(0) var<uniform> model: ModelUniforms;\n@group(2) @binding(0) var<uniform> camera: CameraUniforms;\n\nstruct StrokeVertex {\n position: vec3<f32>,\n tangentIn: vec3<f32>,\n tangentOut: vec3<f32>,\n color: vec4<f32>,\n weight: f32,\n}\n\nfn lineIntersection(aPoint: vec2f, aDir: vec2f, bPoint: vec2f, bDir: vec2f) -> vec2f {\n // Rotate and translate so a starts at the origin and goes out to the right\n var bMutPoint = bPoint;\n bMutPoint -= aPoint;\n var rotatedBFrom = vec2<f32>(\n bMutPoint.x*aDir.x + bMutPoint.y*aDir.y,\n bMutPoint.y*aDir.x - bMutPoint.x*aDir.y\n );\n var bTo = bMutPoint + bDir;\n var rotatedBTo = vec2<f32>(\n bTo.x*aDir.x + bTo.y*aDir.y,\n bTo.y*aDir.x - bTo.x*aDir.y\n );\n var intersectionDistance =\n rotatedBTo.x + (rotatedBFrom.x - rotatedBTo.x) * rotatedBTo.y /\n (rotatedBTo.y - rotatedBFrom.y);\n return aPoint + aDir * intersectionDistance;\n}\n\n@vertex\nfn main(input: StrokeVertexInput) -> StrokeVertexOutput {\n HOOK_beforeVertex();\n var output: StrokeVertexOutput;\n let simpleLines = (stroke.uSimpleLines != 0.);\n if (!simpleLines) {\n if (all(input.aTangentIn == vec3<f32>()) != all(input.aTangentOut == vec3<f32>())) {\n output.vCap = 1.;\n } else {\n output.vCap = 0.;\n }\n let conditionA = any(input.aTangentIn != vec3<f32>());\n let conditionB = any(input.aTangentOut != vec3<f32>());\n let conditionC = any(input.aTangentIn != input.aTangentOut);\n if (conditionA && conditionB && conditionC) {\n output.vJoin = 1.;\n } else {\n output.vJoin = 0.;\n }\n }\n var lineColor: vec4<f32>;\n if (stroke.uUseLineColor != 0.) {\n lineColor = input.aVertexColor;\n } else {\n lineColor = model.uMaterialColor;\n }\n var inputs = StrokeVertex(\n input.aPosition.xyz,\n input.aTangentIn,\n input.aTangentOut,\n lineColor,\n stroke.uStrokeWeight\n );\n\n// @p5 ifdef StrokeVertex getObjectInputs\n inputs = HOOK_getObjectInputs(inputs);\n// @p5 endif\n\n// @p5 ifdef StrokeVertex getWorldInputs\n inputs.position = (model.uModelMatrix * vec4<f32>(inputs.position, 1.)).xyz;\n inputs.tangentIn = (model.uModelMatrix * vec4<f32>(input.aTangentIn, 1.)).xyz;\n inputs.tangentOut = (model.uModelMatrix * vec4<f32>(input.aTangentOut, 1.)).xyz;\n inputs = HOOK_getWorldInputs(inputs);\n// @p5 endif\n\n// @p5 ifdef StrokeVertex getWorldInputs\n // Already multiplied by the model matrix, just apply view\n inputs.position = (model.uViewMatrix * vec4<f32>(inputs.position, 1.)).xyz;\n inputs.tangentIn = (model.uViewMatrix * vec4<f32>(input.aTangentIn, 0.)).xyz;\n inputs.tangentOut = (model.uViewMatrix * vec4<f32>(input.aTangentOut, 0.)).xyz;\n// @p5 endif\n// @p5 ifndef StrokeVertex getWorldInputs\n // Apply both at once\n inputs.position = (model.uModelViewMatrix * vec4<f32>(inputs.position, 1.)).xyz;\n inputs.tangentIn = (model.uModelViewMatrix * vec4<f32>(input.aTangentIn, 0.)).xyz;\n inputs.tangentOut = (model.uModelViewMatrix * vec4<f32>(input.aTangentOut, 0.)).xyz;\n// @p5 endif\n// @p5 ifdef StrokeVertex getCameraInputs\n inputs = HOOK_getCameraInputs(inputs);\n// @p5 endif\n\n var posp = vec4<f32>(inputs.position, 1.);\n var posqIn = vec4<f32>(inputs.position + inputs.tangentIn, 1.);\n var posqOut = vec4<f32>(inputs.position + inputs.tangentOut, 1.);\n output.vStrokeWeight = inputs.weight;\n\n var facingCamera = pow(\n // The word space tangent's z value is 0 if it's facing the camera\n abs(normalize(posqIn-posp).z),\n\n // Using pow() here to ramp 'facingCamera' up from 0 to 1 really quickly\n // so most lines get scaled and don't get clipped\n 0.25\n );\n\n // Moving vertices slightly toward the camera\n // to avoid depth-fighting with the fill triangles.\n // A mix of scaling and offsetting is used based on distance\n // Discussion here:\n // https://github.com/processing/p5.js/issues/7200\n\n // using a scale <1 moves the lines towards nearby camera\n // in order to prevent popping effects due to half of\n // the line disappearing behind the geometry faces.\n var zDistance = -posp.z;\n var distanceFactor = smoothstep(0., 800., zDistance);\n\n // Discussed here:\n // http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=252848\n var scale = mix(1., 0.995, facingCamera);\n var dynamicScale = mix(scale, 1.0, distanceFactor); // Closer = more scale, farther = less\n\n posp = vec4<f32>(posp.xyz * dynamicScale, posp.w);\n posqIn = vec4<f32>(posqIn.xyz * dynamicScale, posqIn.w);\n posqOut= vec4<f32>(posqOut.xyz * dynamicScale, posqOut.w);\n\n // Moving vertices slightly toward camera when far away\n // https://github.com/processing/p5.js/issues/6956\n var zOffset = mix(0., -1., facingCamera);\n var dynamicZAdjustment = mix(0., zOffset, distanceFactor); // Closer = less zAdjustment, farther = more\n\n posp.z -= dynamicZAdjustment;\n posqIn.z -= dynamicZAdjustment;\n posqOut.z -= dynamicZAdjustment;\n\n var p = camera.uProjectionMatrix * posp;\n var qIn = camera.uProjectionMatrix * posqIn;\n var qOut = camera.uProjectionMatrix * posqOut;\n\n var tangentIn = normalize((qIn.xy * p.w - p.xy * qIn.w) * camera.uViewport.zw);\n var tangentOut = normalize((qOut.xy * p.w - p.xy * qOut.w) * camera.uViewport.zw);\n\n var curPerspScale = vec2<f32>();\n if (camera.uPerspective == 1) {\n // Perspective ---\n // convert from world to clip by multiplying with projection scaling factor\n // to get the right thickness (see https://github.com/processing/processing/issues/5182)\n\n // The y value of the projection matrix may be flipped if rendering to a Framebuffer.\n // Multiplying again by its sign here negates the flip to get just the scale.\n curPerspScale = (camera.uProjectionMatrix * vec4(1., sign(camera.uProjectionMatrix[1][1]), 0., 0.)).xy;\n } else {\n // No Perspective ---\n // multiply by W (to cancel out division by W later in the pipeline) and\n // convert from screen to clip (derived from clip to screen above)\n curPerspScale = p.w / (0.5 * camera.uViewport.zw);\n }\n\n var offset = vec2<f32>();\n if (output.vJoin == 1. && !simpleLines) {\n output.vTangent = normalize(tangentIn + tangentOut);\n var normalIn = vec2<f32>(-tangentIn.y, tangentIn.x);\n var normalOut = vec2<f32>(-tangentOut.y, tangentOut.x);\n var side = sign(input.aSide);\n var sideEnum = abs(input.aSide);\n\n // We generate vertices for joins on either side of the centerline, but\n // the "elbow" side is the only one needing a join. By not setting the\n // offset for the other side, all its vertices will end up in the same\n // spot and not render, effectively discarding it.\n if (sign(dot(tangentOut, vec2<f32>(-tangentIn.y, tangentIn.x))) != side) {\n // Side enums:\n // 1: the side going into the join\n // 2: the middle of the join\n // 3: the side going out of the join\n if (sideEnum == 2.) {\n // Calculate the position + tangent on either side of the join, and\n // find where the lines intersect to find the elbow of the join\n var c = (posp.xy / posp.w + vec2<f32>(1.)) * 0.5 * camera.uViewport.zw;\n\n var intersection = lineIntersection(\n c + (side * normalIn * inputs.weight / 2.),\n tangentIn,\n c + (side * normalOut * inputs.weight / 2.),\n tangentOut\n );\n offset = intersection - c;\n\n\n // When lines are thick and the angle of the join approaches 180, the\n // elbow might be really far from the center. We'll apply a limit to\n // the magnitude to avoid lines going across the whole screen when this\n // happens.\n var mag = length(offset);\n var maxMag = 3. * inputs.weight;\n if (mag > maxMag) {\n offset *= maxMag / mag;\n }\n } else if (sideEnum == 1.) {\n offset = side * normalIn * inputs.weight / 2.;\n } else if (sideEnum == 3.) {\n offset = side * normalOut * inputs.weight / 2.;\n }\n }\n if (stroke.uStrokeJoin == 2) {\n var avgNormal = vec2<f32>(-output.vTangent.y, output.vTangent.x);\n output.vMaxDist = abs(dot(avgNormal, normalIn * inputs.weight / 2.));\n } else {\n output.vMaxDist = inputs.weight / 2.;\n }\n } else {\n var tangent: vec2<f32>;\n if (all(input.aTangentIn == vec3<f32>())) {\n tangent = tangentOut;\n } else {\n tangent = tangentIn;\n }\n output.vTangent = tangent;\n var normal = vec2<f32>(-tangent.y, tangent.x);\n\n var normalOffset = sign(input.aSide);\n // Caps will have side values of -2 or 2 on the edge of the cap that\n // extends out from the line\n var tangentOffset = abs(input.aSide) - 1.;\n offset = (normal * normalOffset + tangent * tangentOffset) *\n inputs.weight * 0.5;\n output.vMaxDist = inputs.weight / 2.;\n }\n output.vCenter = p.xy;\n output.vPosition = output.vCenter + offset;\n output.vColor = inputs.color;\n\n output.Position = vec4<f32>(\n p.xy + offset.xy * curPerspScale,\n p.zw\n );\n HOOK_afterVertex();\n return output;\n}`,ae=`\nstruct StrokeFragmentInput {\n @location(0) vColor: vec4<f32>,\n @location(1) vTangent: vec2<f32>,\n @location(2) vCenter: vec2<f32>,\n @location(3) vPosition: vec2<f32>,\n @location(4) vMaxDist: f32,\n @location(5) vCap: f32,\n @location(6) vJoin: f32,\n @location(7) vStrokeWeight: f32,\n}\n\n${oe}\n@group(0) @binding(0) var<uniform> stroke: StrokeUniforms;\n@group(1) @binding(0) var<uniform> model: ModelUniforms;\n@group(2) @binding(0) var<uniform> camera: CameraUniforms;\n\n\nfn distSquared(a: vec2<f32>, b: vec2<f32>) -> f32 {\n return dot(b - a, b - a);\n}\n\nstruct Inputs {\n color: vec4<f32>,\n tangent: vec2<f32>,\n center: vec2<f32>,\n position: vec2<f32>,\n strokeWeight: f32,\n}\n\n@fragment\nfn main(input: StrokeFragmentInput) -> @location(0) vec4<f32> {\n HOOK_beforeFragment();\n\n var inputs: Inputs;\n inputs.color = input.vColor;\n inputs.tangent = input.vTangent;\n inputs.center = input.vCenter;\n inputs.position = input.vPosition;\n inputs.strokeWeight = input.vStrokeWeight;\n inputs = HOOK_getPixelInputs(inputs);\n\n if (input.vCap > 0.) {\n if (\n stroke.uStrokeCap == STROKE_CAP_ROUND &&\n HOOK_shouldDiscard(distSquared(inputs.position, inputs.center) > inputs.strokeWeight * inputs.strokeWeight * 0.25)\n ) {\n discard;\n } else if (\n stroke.uStrokeCap == STROKE_CAP_SQUARE &&\n HOOK_shouldDiscard(dot(inputs.position - inputs.center, inputs.tangent) > 0.)\n ) {\n discard;\n } else if (HOOK_shouldDiscard(false)) {\n discard;\n }\n } else if (input.vJoin > 0.) {\n if (\n stroke.uStrokeJoin == STROKE_JOIN_ROUND &&\n HOOK_shouldDiscard(distSquared(inputs.position, inputs.center) > inputs.strokeWeight * inputs.strokeWeight * 0.25)\n ) {\n discard;\n } else if (stroke.uStrokeJoin == STROKE_JOIN_BEVEL) {\n let normal = vec2<f32>(-inputs.tangent.y, -inputs.tangent.x);\n if (HOOK_shouldDiscard(abs(dot(inputs.position - inputs.center, normal)) > input.vMaxDist)) {\n discard;\n }\n } else if (HOOK_shouldDiscard(false)) {\n discard;\n }\n }\n var col = HOOK_getFinalColor(inputs.color);\n col = vec4<f32>(col.rgb, 1.0) * col.a;\n HOOK_afterFragment();\n return vec4<f32>(col);\n}\n`,ue="\n// Group 0: Material Properties\nstruct MaterialUniforms {\n uUseVertexColor: u32,\n uHasSetAmbient: u32,\n uAmbientColor: vec3<f32>,\n uSpecularMatColor: vec4<f32>,\n uAmbientMatColor: vec4<f32>,\n uEmissiveMatColor: vec4<f32>,\n uTint: vec4<f32>,\n isTexture: u32,\n uSpecular: u32,\n uShininess: f32,\n uMetallic: f32,\n}\n\n// Group 0: Lighting\nstruct LightingUniforms {\n uDirectionalLightCount: i32,\n uLightingDirection: array<vec3<f32>, 5>,\n uDirectionalDiffuseColors: array<vec3<f32>, 5>,\n uDirectionalSpecularColors: array<vec3<f32>, 5>,\n uPointLightCount: i32,\n uPointLightLocation: array<vec3<f32>, 5>,\n uPointLightDiffuseColors: array<vec3<f32>, 5>,\n uPointLightSpecularColors: array<vec3<f32>, 5>,\n uSpotLightCount: i32,\n uSpotLightAngle: vec4<f32>,\n uSpotLightConc: vec4<f32>,\n uSpotLightDiffuseColors: array<vec3<f32>, 4>,\n uSpotLightSpecularColors: array<vec3<f32>, 4>,\n uSpotLightLocation: array<vec3<f32>, 4>,\n uSpotLightDirection: array<vec3<f32>, 4>,\n uConstantAttenuation: f32,\n uLinearAttenuation: f32,\n uQuadraticAttenuation: f32,\n uUseImageLight: u32,\n uUseLighting: u32,\n}\n\n// Group 1: Model Transform\nstruct ModelUniforms {\n// @p5 ifdef Vertex getWorldInputs\n uModelMatrix: mat4x4<f32>,\n uModelNormalMatrix: mat3x3<f32>,\n// @p5 endif\n// @p5 ifndef Vertex getWorldInputs\n uModelViewMatrix: mat4x4<f32>,\n uNormalMatrix: mat3x3<f32>,\n// @p5 endif\n uMaterialColor: vec4<f32>,\n}\n\n// Group 2: Camera and Projection\nstruct CameraUniforms {\n uViewMatrix: mat4x4<f32>,\n uProjectionMatrix: mat4x4<f32>,\n uCameraNormalMatrix: mat3x3<f32>,\n}\n",le=`\nstruct VertexInput {\n @location(0) aPosition: vec3<f32>,\n @location(1) aNormal: vec3<f32>,\n @location(2) aTexCoord: vec2<f32>,\n @location(3) aVertexColor: vec4<f32>,\n};\n\nstruct VertexOutput {\n @builtin(position) Position: vec4<f32>,\n @location(0) vNormal: vec3<f32>,\n @location(1) vTexCoord: vec2<f32>,\n @location(2) vViewPosition: vec3<f32>,\n @location(4) vColor: vec4<f32>,\n};\n\n${ue}\n@group(0) @binding(0) var<uniform> material: MaterialUniforms;\n@group(0) @binding(1) var<uniform> lighting: LightingUniforms;\n@group(1) @binding(0) var<uniform> model: ModelUniforms;\n@group(2) @binding(0) var<uniform> camera: CameraUniforms;\n\nstruct Vertex {\n position: vec3<f32>,\n normal: vec3<f32>,\n texCoord: vec2<f32>,\n color: vec4<f32>,\n}\n\n@vertex\nfn main(input: VertexInput) -> VertexOutput {\n HOOK_beforeVertex();\n var output: VertexOutput;\n\n let useVertexColor = (material.uUseVertexColor != 0 && input.aVertexColor.x >= 0.0);\n var inputs = Vertex(\n input.aPosition,\n input.aNormal,\n input.aTexCoord,\n select(model.uMaterialColor, input.aVertexColor, useVertexColor)\n );\n\n// @p5 ifdef Vertex getObjectInputs\n inputs = HOOK_getObjectInputs(inputs);\n// @p5 endif\n\n// @p5 ifdef Vertex getWorldInputs\n inputs.position = (model.uModelMatrix * vec4<f32>(inputs.position, 1.0)).xyz;\n inputs.normal = model.uModelNormalMatrix * inputs.normal;\n inputs = HOOK_getWorldInputs(inputs);\n// @p5 endif\n\n// @p5 ifdef Vertex getWorldInputs\n // Already multiplied by the model matrix, just apply view\n inputs.position = (camera.uViewMatrix * vec4<f32>(inputs.position, 1.0)).xyz;\n inputs.normal = camera.uCameraNormalMatrix * inputs.normal;\n// @p5 endif\n// @p5 ifndef Vertex getWorldInputs\n // Apply both at once\n inputs.position = (model.uModelViewMatrix * vec4<f32>(inputs.position, 1.0)).xyz;\n inputs.normal = model.uNormalMatrix * inputs.normal;\n// @p5 endif\n\n// @p5 ifdef Vertex getCameraInputs\n inputs = HOOK_getCameraInputs(inputs);\n// @p5 endif\n\n output.vViewPosition = inputs.position;\n output.vTexCoord = inputs.texCoord;\n output.vNormal = normalize(inputs.normal);\n output.vColor = inputs.color;\n\n output.Position = camera.uProjectionMatrix * vec4<f32>(inputs.position, 1.0);\n\n HOOK_afterVertex();\n return output;\n}\n`,pe=`\nstruct FragmentInput {\n @location(0) vNormal: vec3<f32>,\n @location(1) vTexCoord: vec2<f32>,\n @location(2) vViewPosition: vec3<f32>,\n @location(4) vColor: vec4<f32>,\n};\n\n${ue}\n@group(0) @binding(0) var<uniform> material: MaterialUniforms;\n@group(0) @binding(1) var<uniform> lighting: LightingUniforms;\n@group(0) @binding(2) var uSampler: texture_2d<f32>;\n@group(0) @binding(3) var uSampler_sampler: sampler;\n@group(0) @binding(4) var environmentMapDiffused: texture_2d<f32>;\n@group(0) @binding(5) var environmentMapDiffused_sampler: sampler;\n@group(0) @binding(6) var environmentMapSpecular: texture_2d<f32>;\n@group(0) @binding(7) var environmentMapSpecular_sampler: sampler;\n@group(1) @binding(0) var<uniform> model: ModelUniforms;\n@group(2) @binding(0) var<uniform> camera: CameraUniforms;\n\nstruct ColorComponents {\n baseColor: vec3<f32>,\n opacity: f32,\n ambientColor: vec3<f32>,\n specularColor: vec3<f32>,\n diffuse: vec3<f32>,\n ambient: vec3<f32>,\n specular: vec3<f32>,\n emissive: vec3<f32>,\n}\n\nstruct Inputs {\n normal: vec3<f32>,\n texCoord: vec2<f32>,\n ambientLight: vec3<f32>,\n ambientMaterial: vec3<f32>,\n specularMaterial: vec3<f32>,\n emissiveMaterial: vec3<f32>,\n color: vec4<f32>,\n shininess: f32,\n metalness: f32,\n}\n\n\nstruct LightResult {\n diffuse: vec3<f32>,\n specular: vec3<f32>,\n}\nstruct LightIntensityResult {\n diffuse: f32,\n specular: f32,\n}\n\nconst specularFactor = 2.0;\nconst diffuseFactor = 0.73;\nconst PI = 3.14159265359;\n\nfn mapTextureToNormal(v: vec3<f32>) -> vec2<f32> {\n // x = r sin(phi) cos(theta)\n // y = r cos(phi)\n // z = r sin(phi) sin(theta)\n let phi = acos(v.y);\n // if phi is 0, then there are no x, z components\n var theta = 0.0;\n // else\n theta = acos(v.x / sin(phi));\n let sinTheta = v.z / sin(phi);\n if (sinTheta < 0.0) {\n // Turn it into -theta, but in the 0-2PI range\n theta = 2.0 * PI - theta;\n }\n theta = theta / (2.0 * PI);\n let phiNorm = phi / PI;\n\n let angles = vec2<f32>(fract(theta + 0.25), 1.0 - phiNorm);\n return angles;\n}\n\nfn calculateImageDiffuse(vNormal: vec3<f32>, vViewPosition: vec3<f32>, metallic: f32) -> vec3<f32> {\n // make 2 seperate builds\n let worldCameraPosition = vec3<f32>(0.0, 0.0, 0.0); // hardcoded world camera position\n let worldNormal = normalize(vNormal * camera.uCameraNormalMatrix);\n let newTexCoord = mapTextureToNormal(worldNormal);\n let texture = textureSample(environmentMapDiffused, environmentMapDiffused_sampler, newTexCoord);\n // this is to make the darker sections more dark\n // png and jpg usually flatten the brightness so it is to reverse that\n return mix(smoothstep(vec3<f32>(0.0), vec3<f32>(1.0), texture.xyz), vec3<f32>(0.0), metallic);\n}\n\nfn calculateImageSpecular(vNormal: vec3<f32>, vViewPosition: vec3<f32>, shininess: f32, metallic: f32) -> vec3<f32> {\n let worldCameraPosition = vec3<f32>(0.0, 0.0, 0.0);\n let worldNormal = normalize(vNormal);\n let lightDirection = normalize(vViewPosition - worldCameraPosition);\n let R = reflect(lightDirection, worldNormal) * camera.uCameraNormalMatrix;\n let newTexCoord = mapTextureToNormal(R);\n\n // In p5js the range of shininess is >= 1,\n // Therefore roughness range will be ([0,1]*8)*20 or [0, 160]\n // The factor of 8 is because currently the getSpecularTexture\n // only calculated 8 different levels of roughness\n // The factor of 20 is just to spread up this range so that,\n // [1, max] of shininess is converted to [0,160] of roughness\n let roughness = 20.0 / shininess;\n let outColor = textureSampleLevel(environmentMapSpecular, environmentMapSpecular_sampler, newTexCoord, roughness * 8.0 - 1.);\n\n // this is to make the darker sections more dark\n // png and jpg usually flatten the brightness so it is to reverse that\n return mix(\n pow(outColor.xyz, vec3<f32>(10.0)),\n pow(outColor.xyz, vec3<f32>(1.2)),\n metallic\n );\n}\n\nfn phongSpecular(\n lightDirection: vec3<f32>,\n viewDirection: vec3<f32>,\n surfaceNormal: vec3<f32>,\n shininess: f32\n) -> f32 {\n let R = reflect(lightDirection, surfaceNormal);\n return pow(max(0.0, dot(R, viewDirection)), shininess);\n}\n\nfn lambertDiffuse(lightDirection: vec3<f32>, surfaceNormal: vec3<f32>) -> f32 {\n return max(0.0, dot(-lightDirection, surfaceNormal));\n}\n\nfn singleLight(\n viewDirection: vec3<f32>,\n normal: vec3<f32>,\n lightVector: vec3<f32>,\n shininess: f32,\n metallic: f32\n) -> LightIntensityResult {\n let lightDir = normalize(lightVector);\n let specularIntensity = mix(1.0, 0.4, metallic);\n let diffuseIntensity = mix(1.0, 0.1, metallic);\n let diffuse = lambertDiffuse(lightDir, normal) * diffuseIntensity;\n let specular = select(\n 0.,\n phongSpecular(lightDir, viewDirection, normal, shininess) * specularIntensity,\n material.uSpecular == 1\n );\n return LightIntensityResult(diffuse, specular);\n}\n\nfn totalLight(\n modelPosition: vec3<f32>,\n normal: vec3<f32>,\n shininess: f32,\n metallic: f32\n) -> LightResult {\n var totalSpecular = vec3<f32>(0.0, 0.0, 0.0);\n var totalDiffuse = vec3<f32>(0.0, 0.0, 0.0);\n\n if (lighting.uUseLighting == 0) {\n return LightResult(vec3<f32>(1.0, 1.0, 1.0), totalSpecular);\n }\n\n let viewDirection = normalize(-modelPosition);\n\n for (var j = 0; j < 5; j++) {\n if (j < lighting.uDirectionalLightCount) {\n let lightVector = (camera.uViewMatrix * vec4<f32>(\n lighting.uLightingDirection[j],\n 0.0\n )).xyz;\n let lightColor = lighting.uDirectionalDiffuseColors[j];\n let specularColor = lighting.uDirectionalSpecularColors[j];\n let result = singleLight(viewDirection, normal, lightVector, shininess, metallic);\n totalDiffuse += result.diffuse * lightColor;\n totalSpecular += result.specular * specularColor;\n }\n\n if (j < lighting.uPointLightCount) {\n let lightPosition = (camera.uViewMatrix * vec4<f32>(\n lighting.uPointLightLocation[j],\n 1.0\n )).xyz;\n let lightVector = modelPosition - lightPosition;\n let lightDistance = length(lightVector);\n let lightFalloff = 1.0 / (\n lighting.uConstantAttenuation +\n lightDistance * lighting.uLinearAttenuation +\n lightDistance * lightDistance * lighting.uQuadraticAttenuation\n );\n let lightColor = lighting.uPointLightDiffuseColors[j] * lightFalloff;\n let specularColor = lighting.uPointLightSpecularColors[j] * lightFalloff;\n let result = singleLight(viewDirection, normal, lightVector, shininess, metallic);\n totalDiffuse += result.diffuse * lightColor;\n totalSpecular += result.specular * specularColor;\n }\n\n if (j < lighting.uSpotLightCount) {\n let lightPosition = (camera.uViewMatrix * vec4<f32>(\n lighting.uSpotLightLocation[j],\n 1.0\n )).xyz;\n let lightVector = modelPosition - lightPosition;\n let lightDistance = length(lightVector);\n var lightFalloff = 1.0 / (\n lighting.uConstantAttenuation +\n lightDistance * lighting.uLinearAttenuation +\n lightDistance * lightDistance * lighting.uQuadraticAttenuation\n );\n let lightDirection = (camera.uViewMatrix * vec4<f32>(\n lighting.uSpotLightDirection[j],\n 0.0\n )).xyz;\n let spotDot = dot(normalize(lightVector), normalize(lightDirection));\n let spotFalloff = select(\n 0.0,\n pow(spotDot, lighting.uSpotLightConc[j]),\n spotDot < lighting.uSpotLightAngle[j]\n );\n lightFalloff *= spotFalloff;\n let lightColor = lighting.uSpotLightDiffuseColors[j];\n let specularColor = lighting.uSpotLightSpecularColors[j];\n let result = singleLight(viewDirection, normal, lightVector, shininess, metallic);\n totalDiffuse += result.diffuse * lightColor;\n totalSpecular += result.specular * specularColor;\n }\n }\n\n // Image light contribution\n if (lighting.uUseImageLight != 0) {\n totalDiffuse += calculateImageDiffuse(normal, modelPosition, metallic);\n totalSpecular += calculateImageSpecular(normal, modelPosition, shininess, metallic);\n }\n\n return LightResult(\n totalDiffuse * diffuseFactor,\n totalSpecular * specularFactor\n );\n}\n\n@fragment\nfn main(input: FragmentInput) -> @location(0) vec4<f32> {\n HOOK_beforeFragment();\n\n let color = select(\n input.vColor,\n textureSample(uSampler, uSampler_sampler, input.vTexCoord) * (material.uTint/255.0),\n material.isTexture == 1\n ); // TODO: check isTexture and apply tint\n var inputs = Inputs(\n normalize(input.vNormal),\n input.vTexCoord,\n material.uAmbientColor,\n select(color.rgb, material.uAmbientMatColor.rgb, material.uHasSetAmbient == 1),\n material.uSpecularMatColor.rgb,\n material.uEmissiveMatColor.rgb,\n color,\n material.uShininess,\n material.uMetallic\n );\n inputs = HOOK_getPixelInputs(inputs);\n\n let light = totalLight(\n input.vViewPosition,\n inputs.normal,\n inputs.shininess,\n inputs.metalness\n );\n\n let baseColor = inputs.color;\n let components = ColorComponents(\n baseColor.rgb,\n baseColor.a,\n inputs.ambientMaterial,\n inputs.specularMaterial,\n light.diffuse,\n inputs.ambientLight,\n light.specular,\n inputs.emissiveMaterial\n );\n\n var outColor = HOOK_getFinalColor(\n HOOK_combineColors(components)\n );\n outColor = vec4<f32>(outColor.rgb * outColor.a, outColor.a);\n HOOK_afterFragment();\n return outColor;\n}\n`,fe="\n// Group 0: Font Properties\nstruct FontUniforms {\n uStrokeImageSize: vec2<i32>,\n uCellsImageSize: vec2<i32>,\n uGridImageSize: vec2<i32>,\n uGridOffset: vec2<i32>,\n uGridSize: vec2<i32>,\n uGlyphRect: vec4<f32>,\n uGlyphOffset: f32,\n uMaterialColor: vec4<f32>,\n}\n\n// Group 1: Model Transform\nstruct ModelUniforms {\n uModelViewMatrix: mat4x4<f32>,\n}\n\n// Group 2: Camera and Projection\nstruct CameraUniforms {\n uProjectionMatrix: mat4x4<f32>,\n}\n",ce=`\nstruct VertexInput {\n @location(0) aPosition: vec3<f32>,\n @location(1) aTexCoord: vec2<f32>,\n};\n\nstruct VertexOutput {\n @builtin(position) Position: vec4<f32>,\n @location(0) vTexCoord: vec2<f32>,\n};\n\n${fe}\n@group(0) @binding(0) var<uniform> font: FontUniforms;\n@group(1) @binding(0) var<uniform> model: ModelUniforms;\n@group(2) @binding(0) var<uniform> camera: CameraUniforms;\n\n@vertex\nfn main(input: VertexInput) -> VertexOutput {\n var output: VertexOutput;\n var positionVec4 = vec4<f32>(input.aPosition, 1.0);\n\n // scale by the size of the glyph's rectangle\n positionVec4.x = positionVec4.x * (font.uGlyphRect.z - font.uGlyphRect.x);\n positionVec4.y = positionVec4.y * (font.uGlyphRect.w - font.uGlyphRect.y);\n\n // Expand glyph bounding boxes by 1px on each side to give a bit of room\n // for antialiasing\n let newOrigin = (model.uModelViewMatrix * vec4<f32>(0.0, 0.0, 0.0, 1.0)).xyz;\n let newDX = (model.uModelViewMatrix * vec4<f32>(1.0, 0.0, 0.0, 1.0)).xyz;\n let newDY = (model.uModelViewMatrix * vec4<f32>(0.0, 1.0, 0.0, 1.0)).xyz;\n let pixelScale = vec2<f32>(\n 1.0 / length(newOrigin - newDX),\n 1.0 / length(newOrigin - newDY)\n );\n let offset = pixelScale * normalize(input.aTexCoord - vec2<f32>(0.5, 0.5));\n let textureOffset = offset * (1.0 / vec2<f32>(\n font.uGlyphRect.z - font.uGlyphRect.x,\n font.uGlyphRect.w - font.uGlyphRect.y\n ));\n\n // move to the corner of the glyph\n positionVec4.x = positionVec4.x + font.uGlyphRect.x;\n positionVec4.y = positionVec4.y + font.uGlyphRect.y;\n\n // move to the letter's line offset\n positionVec4.x = positionVec4.x + font.uGlyphOffset;\n\n positionVec4.x = positionVec4.x + offset.x;\n positionVec4.y = positionVec4.y + offset.y;\n\n output.Position = camera.uProjectionMatrix * model.uModelViewMatrix * positionVec4;\n output.vTexCoord = input.aTexCoord + textureOffset;\n\n return output;\n}\n`,de=`\nstruct FragmentInput {\n @location(0) vTexCoord: vec2<f32>,\n};\n\n${fe}\n@group(0) @binding(0) var<uniform> font: FontUniforms;\n@group(0) @binding(1) var uSamplerStrokes: texture_2d<f32>;\n@group(0) @binding(2) var uSamplerStrokes_sampler: sampler;\n@group(0) @binding(3) var uSamplerRowStrokes: texture_2d<f32>;\n@group(0) @binding(4) var uSamplerRowStrokes_sampler: sampler;\n@group(0) @binding(5) var uSamplerRows: texture_2d<f32>;\n@group(0) @binding(6) var uSamplerRows_sampler: sampler;\n@group(0) @binding(7) var uSamplerColStrokes: texture_2d<f32>;\n@group(0) @binding(8) var uSamplerColStrokes_sampler: sampler;\n@group(0) @binding(9) var uSamplerCols: texture_2d<f32>;\n@group(0) @binding(10) var uSamplerCols_sampler: sampler;\n@group(1) @binding(0) var<uniform> model: ModelUniforms;\n@group(2) @binding(0) var<uniform> camera: CameraUniforms;\n\n// some helper functions\nfn ROUND_f32(v: f32) -> i32 { return i32(floor(v + 0.5)); }\nfn ROUND_vec2(v: vec2<f32>) -> vec2<i32> { return vec2<i32>(floor(v + 0.5)); }\nfn saturate_f32(v: f32) -> f32 { return clamp(v, 0.0, 1.0); }\nfn saturate_vec2(v: vec2<f32>) -> vec2<f32> { return clamp(v, vec2<f32>(0.0), vec2<f32>(1.0)); }\n\nfn mul_f32_i32(v1: f32, v2: i32) -> i32 {\n return i32(floor(v1 * f32(v2)));\n}\n\nfn mul_vec2_ivec2(v1: vec2<f32>, v2: vec2<i32>) -> vec2<i32> {\n return vec2<i32>(floor(v1 * vec2<f32>(v2) + 0.5));\n}\n\n// unpack a 16-bit integer from a float vec2\nfn getInt16(v: vec2<f32>) -> i32 {\n let iv = ROUND_vec2(v * 255.0);\n return iv.x * 128 + iv.y;\n}\n\nconst minDistance: f32 = 1.0/8192.0;\nconst hardness: f32 = 1.05; // amount of antialias\n\n// the maximum number of curves in a glyph\nconst N: i32 = 250;\n\n// retrieves an indexed pixel from a texture\nfn getTexel(texture: texture_2d<f32>, samp: sampler, pos: i32, size: vec2<i32>) -> vec4<f32> {\n let width = size.x;\n let x = pos % width;\n let y = pos / width;\n\n return textureLoad(texture, vec2<i32>(x, y), 0);\n}\n\nfn calculateCrossings(p0: vec2<f32>, p1: vec2<f32>, p2: vec2<f32>, vTexCoord: vec2<f32>, pixelScale: vec2<f32>) -> array<vec2<f32>, 2> {\n // get the coefficients of the quadratic in t\n var a = p0 - p1 * 2.0 + p2;\n var b = p0 - p1;\n a = vec2<f32>(\n select(a.x, sign(a.x) * 1e-6, abs(a.x) < 1e-6),\n select(a.y, sign(a.y) * 1e-6, abs(a.y) < 1e-6)\n );\n b = vec2<f32>(\n select(b.x, sign(b.x) * 1e-6, abs(b.x) < 1e-6),\n select(b.y, sign(b.y) * 1e-6, abs(b.y) < 1e-6)\n );\n let c = p0 - vTexCoord;\n\n // found out which values of 't' it crosses the axes\n let surd = sqrt(max(vec2<f32>(0.0), b * b - a * c));\n let t1 = ((b - surd) / a).yx;\n let t2 = ((b + surd) / a).yx;\n\n // approximate straight lines to avoid rounding errors\n var t1_fixed = t1;\n var t2_fixed = t2;\n if (abs(a.y) < 0.001) {\n t1_fixed.x = c.y / (2.0 * b.y);\n t2_fixed.x = c.y / (2.0 * b.y);\n }\n\n if (abs(a.x) < 0.001) {\n t1_fixed.y = c.x / (2.0 * b.x);\n t2_fixed.y = c.x / (2.0 * b.x);\n }\n\n // plug into quadratic formula to find the coordinates of the crossings\n let C1 = ((a * t1_fixed - b * 2.0) * t1_fixed + c) * pixelScale;\n let C2 = ((a * t2_fixed - b * 2.0) * t2_fixed + c) * pixelScale;\n\n return array<vec2<f32>, 2>(C1, C2);\n}\n\nfn coverageX(p0: vec2<f32>, p1: vec2<f32>, p2: vec2<f32>, vTexCoord: vec2<f32>, pixelScale: vec2<f32>, coverage: ptr<function, vec2<f32>>, weight: ptr<function, vec2<f32>>) {\n let crossings = calculateCrossings(p0, p1, p2, vTexCoord, pixelScale);\n let C1 = crossings[0];\n let C2 = crossings[1];\n\n // determine on which side of the x-axis the points lie\n let y0 = p0.y > vTexCoord.y;\n let y1 = p1.y > vTexCoord.y;\n let y2 = p2.y > vTexCoord.y;\n\n // could we be under the curve (after t1)?\n if ((y1 && !y2) || (!y1 && y0)) {\n // add the coverage for t1\n (*coverage).x = (*coverage).x + saturate_f32(C1.x + 0.5);\n // calculate the anti-aliasing for t1\n (*weight).x = min((*weight).x, abs(C1.x));\n }\n\n // are we outside the curve (after t2)?\n if ((y1 && !y0) || (!y1 && y2)) {\n // subtract the coverage for t2\n (*coverage).x = (*coverage).x - saturate_f32(C2.x + 0.5);\n // calculate the anti-aliasing for t2\n (*weight).x = min((*weight).x, abs(C2.x));\n }\n}\n\n// this is essentially the same as coverageX, but with the axes swapped\nfn coverageY(p0: vec2<f32>, p1: vec2<f32>, p2: vec2<f32>, vTexCoord: vec2<f32>, pixelScale: vec2<f32>, coverage: ptr<function, vec2<f32>>, weight: ptr<function, vec2<f32>>) {\n let crossings = calculateCrossings(p0, p1, p2, vTexCoord, pixelScale);\n let C1 = crossings[0];\n let C2 = crossings[1];\n\n let x0 = p0.x > vTexCoord.x;\n let x1 = p1.x > vTexCoord.x;\n let x2 = p2.x > vTexCoord.x;\n\n if ((x1 && !x2) || (!x1 && x0)) {\n (*coverage).y = (*coverage).y - saturate_f32(C1.y + 0.5);\n weight.y = min(weight.y, abs(C1.y));\n }\n\n if ((x1 && !x0) || (!x1 && x2)) {\n (*coverage).y = (*coverage).y + saturate_f32(C2.y + 0.5);\n (*weight).y = min((*weight).y, abs(C2.y));\n }\n}\n\n@fragment\nfn main(input: FragmentInput) -> @location(0) vec4<f32> {\n // var pixelScale: vec2<f32>;\n var coverage: vec2<f32> = vec2<f32>(0.0);\n var weight: vec2<f32> = vec2<f32>(0.5);\n let pixelScale = hardness / fwidth(input.vTexCoord);\n\n // which grid cell is this pixel in?\n let gridCoord = vec2<i32>(floor(input.vTexCoord * vec2<f32>(font.uGridSize)));\n\n // intersect curves in this row\n {\n // the index into the row info bitmap\n let rowIndex = gridCoord.y + font.uGridOffset.y;\n // fetch the info texel\n let rowInfo = getTexel(uSamplerRows, uSamplerRows_sampler, rowIndex, font.uGridImageSize);\n // unpack the rowInfo\n let rowStrokeIndex = getInt16(rowInfo.xy);\n let rowStrokeCount = getInt16(rowInfo.zw);\n\n for (var iRowStroke = 0; iRowStroke < N; iRowStroke = iRowStroke + 1) {\n if (iRowStroke >= rowStrokeCount) {\n break;\n }\n\n // each stroke is made up of 3 points: the start and control point\n // and the start of the next curve.\n // fetch the indices of this pair of strokes:\n let strokeIndices = getTexel(uSamplerRowStrokes, uSamplerRowStrokes_sampler, rowStrokeIndex + iRowStroke, font.uCellsImageSize);\n\n // unpack the stroke index\n let strokePos = getInt16(strokeIndices.xy);\n\n // fetch the two strokes\n let stroke0 = getTexel(uSamplerStrokes, uSamplerStrokes_sampler, strokePos + 0, font.uStrokeImageSize);\n let stroke1 = getTexel(uSamplerStrokes, uSamplerStrokes_sampler, strokePos + 1, font.uStrokeImageSize);\n\n // calculate the coverage\n coverageX(stroke0.xy, stroke0.zw, stroke1.xy, input.vTexCoord, pixelScale, &coverage, &weight);\n }\n }\n\n // intersect curves in this column\n {\n let colIndex = gridCoord.x + font.uGridOffset.x;\n let colInfo = getTexel(uSamplerCols, uSamplerCols_sampler, colIndex, font.uGridImageSize);\n let colStrokeIndex = getInt16(colInfo.xy);\n let colStrokeCount = getInt16(colInfo.zw);\n\n for (var iColStroke = 0; iColStroke < N; iColStroke = iColStroke + 1) {\n if (iColStroke >= colStrokeCount) {\n break;\n }\n\n let strokeIndices = getTexel(uSamplerColStrokes, uSamplerColStrokes_sampler, colStrokeIndex + iColStroke, font.uCellsImageSize);\n\n let strokePos = getInt16(strokeIndices.xy);\n let stroke0 = getTexel(uSamplerStrokes, uSamplerStrokes_sampler, strokePos + 0, font.uStrokeImageSize);\n let stroke1 = getTexel(uSamplerStrokes, uSamplerStrokes_sampler, strokePos + 1, font.uStrokeImageSize);\n coverageY(stroke0.xy, stroke0.zw, stroke1.xy, input.vTexCoord, pixelScale, &coverage, &weight);\n }\n }\n\n weight = saturate_vec2(vec2<f32>(1.0) - weight * 2.0);\n let distance = max(weight.x + weight.y, minDistance); // manhattan approx.\n let antialias = abs(dot(coverage, weight) / distance);\n let cover = min(abs(coverage.x), abs(coverage.y));\n var outColor = vec4<f32>(font.uMaterialColor.rgb, 1.0) * font.uMaterialColor.a;\n outColor = outColor * saturate_f32(max(antialias, cover));\n return outColor;\n}\n`,me="\nstruct Uniforms {\n uModelViewMatrix: mat4x4<f32>,\n uProjectionMatrix: mat4x4<f32>,\n};\n",he=`\nstruct VertexInput {\n @location(0) aPosition: vec3<f32>,\n @location(1) aNormal: vec3<f32>,\n @location(2) aTexCoord: vec2<f32>,\n @location(3) aVertexColor: vec4<f32>,\n};\n\nstruct VertexOutput {\n @builtin(position) Position: vec4<f32>,\n @location(0) vTexCoord: vec2<f32>,\n};\n\n${me}\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\n\n@vertex\nfn main(input: VertexInput) -> VertexOutput {\n var output: VertexOutput;\n output.vTexCoord = input.aTexCoord;\n let positionVec4 = vec4<f32>(input.aPosition, 1.0);\n output.Position = uniforms.uProjectionMatrix * uniforms.uModelViewMatrix * positionVec4;\n return output;\n}\n`,ge=`\nstruct FragmentInput {\n @location(0) vTexCoord: vec2<f32>,\n};\n\n${me}\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\n@group(0) @binding(1) var uSampler: texture_2d<f32>;\n@group(0) @binding(2) var uSampler_sampler: sampler;\n\n@fragment\nfn main(input: FragmentInput) -> @location(0) vec4<f32> {\n return textureSample(uSampler, uSampler_sampler, input.vTexCoord);\n}\n`;function xe(e){throw new Error(`[p5.strands internal error]: ${e}`)}function ve(e,t){throw new Error(`[p5.strands ${e}]: ${t}`)}function Te(e,t){const n=function(e,t){const n=e.nextID++;e.nodeTypes[n]=t.nodeType,e.opCodes[n]=t.opCode,e.values[n]=t.value,e.identifiers[n]=t.identifier,e.dependsOn[n]=t.dependsOn.slice(),e.usedBy[n]=t.usedBy,e.phiBlocks[n]=t.phiBlocks.slice(),e.baseTypes[n]=t.baseType,e.dimensions[n]=t.dimension,e.statementTypes[n]=t.statementType,e.swizzles[n]=t.swizzle;for(const r of t.dependsOn)Array.isArray(e.usedBy[r])||(e.usedBy[r]=[]),e.usedBy[r].push(n);return n}(e,t);return n}function ye(e={}){const t={nodeType:e.nodeType??null,baseType:e.baseType??null,dimension:e.dimension??null,opCode:e.opCode??null,value:e.value??null,identifier:e.identifier??null,statementType:e.statementType??null,swizzle:e.swizzle??null,dependsOn:Array.isArray(e.dependsOn)?e.dependsOn:[],usedBy:Array.isArray(e.usedBy)?e.usedBy:[],phiBlocks:Array.isArray(e.phiBlocks)?e.phiBlocks:[]};return function(e){const t=e.nodeType,n=R[t];2===n.length&&xe(`Required fields for node type '${D[t]}' not defined. Please add them to the utils.js file in p5.strands!`);const r=[];for(const t of n)null===e[t]&&r.push(t);if(e.dependsOn?.some((e=>void 0===e)))throw new Error("Undefined dependency!");r.length>0&&xe(`Missing fields ${r.join(", ")} for a node type '${D[t]}'.`)}(t),t}function be(e,t){return{id:t,nodeType:e.nodeTypes[t],opCode:e.opCodes[t],value:e.values[t],identifier:e.identifiers[t],dependsOn:e.dependsOn[t],usedBy:e.usedBy[t],phiBlocks:e.phiBlocks[t],dimension:e.dimensions[t],baseType:e.baseTypes[t],statementType:e.statementTypes[t],swizzle:e.swizzles[t]}}function Ce(e,t){return{baseType:e.baseTypes[t],dimension:e.dimensions[t],priority:q[e.baseTypes[t]]}}function _e(e,t,n){void 0===n&&xe("undefined nodeID in `recordInBasicBlock()`"),void 0===t&&xe("undefined blockID in `recordInBasicBlock()"),e.blockInstructions[t]=e.blockInstructions[t]||[],e.blockInstructions[t].push(n)}class Oe{constructor(e,t,n){this.id=e,this.strandsContext=n,this.dimension=t,this.structProperties=null,this.isStrandsNode=!0;const r=be(this.strandsContext.dag,this.id);r&&r.identifier&&(this._originalIdentifier=r.identifier),r&&(this._originalBaseType=r.baseType,this._originalDimension=r.dimension)}withStructProperties(e){return this.structProperties=e,this}copy(){return we(this.id,this.dimension,this.strandsContext)}typeInfo(){return{baseType:this._originalBaseType||k,dimension:this.dimension}}bridge(e){const{dag:t,cfg:n}=this.strandsContext,r=be(t,this.id),i=r?.baseType??k;let o;if(e?.isStrandsNode)o=e.id;else{o=Pe(this.strandsContext,{baseType:i,dimension:this.dimension},e).id}if(this._originalIdentifier){const{id:e}=Ae(this.strandsContext,{baseType:this._originalBaseType,dimension:this._originalDimension},this._originalIdentifier),r=Te(t,ye({nodeType:E.ASSIGNMENT,dependsOn:[e,o],phiBlocks:[]}));_e(n,n.currentBlock,r),this.strandsContext.globalAssignments.push(r);const i=Te(t,ye({nodeType:E.VARIABLE,baseType:this._originalBaseType,dimension:this._originalDimension,identifier:this._originalIdentifier}));this.id=i}else this.id=o;return this}bridgeSwizzle(e,t){const{dag:n,cfg:r}=this.strandsContext,i=be(n,this.id),o=i?.baseType??k;let s;if(t?.isStrandsNode)s=t.id;else{s=Pe(this.strandsContext,{baseType:o,dimension:this.dimension},t).id}if(this._originalIdentifier){const{id:t}=Ae(this.strandsContext,{baseType:this._originalBaseType,dimension:this._originalDimension},this._originalIdentifier),i=Te(n,ye({nodeType:E.OPERATION,opCode:Q.SWIZZLE,baseType:this._originalBaseType,dimension:e.length,swizzle:e,dependsOn:[t]})),o=Te(n,ye({nodeType:E.ASSIGNMENT,dependsOn:[i,s],phiBlocks:[]}));_e(r,r.currentBlock,o),this.strandsContext.globalAssignments.push(o);const a=Te(n,ye({nodeType:E.VARIABLE,baseType:this._originalBaseType,dimension:this._originalDimension,identifier:this._originalIdentifier}));this.id=a}else this.id=s;return this}getValue(){if(this._originalIdentifier){const{id:e,dimension:t}=Ae(this.strandsContext,{baseType:this._originalBaseType,dimension:this._originalDimension},this._originalIdentifier);return we(e,t,this.strandsContext)}return this}}function we(e,t,n,r){return new Proxy(new Oe(e,t,n),function(e,t,n){const r=[["x","y","z","w"],["r","g","b","a"],["s","t","p","q"]].map((e=>e.slice(0,t))),i={get(e,t,i){if(t in e)return Reflect.get(...arguments);for(const i of r)if([...t.toString()].every((e=>i.includes(e)))){const o=[...t].map((e=>{const t=i.indexOf(e);return r[0][t]})).join(""),s=De(n,e,o);return we(s.id,s.dimension,n)}},set(e,t,i,o){for(const o of r){const r=[...t];if(!(r.every((e=>o.includes(e)))&&new Set(r).size===r.length&&e.dimension>=r.length))continue;const s=e.dimension,a=new Array(s);for(let t=0;t<s;t++){const{id:r,dimension:i}=De(n,e,"xyzw"[t]);a[t]=we(r,i,n)}let u=[];if(i?.isStrandsNode)if(1===i.dimension)u=Array(r.length).fill(i);else if(i.dimension===r.length)for(let e=0;e<r.length;e++){const{id:t,dimension:r}=De(n,i,"xyzw"[e]);u.push(we(t,r,n))}else ve("type error",`Swizzle assignment: RHS vector does not match LHS vector (need ${r.length}, got ${i.dimension}).`);else if(Array.isArray(i)){const e=i.flat(1/0);1===e.length?u=Array(r.length).fill(e[0]):e.length===r.length?u=e:ve("type error",`Swizzle assignment: RHS length ${e.length} does not match ${r.length}.`)}else"number"==typeof i?u=Array(r.length).fill(i):ve("type error",`Unsupported RHS for swizzle assignment: ${i}`);for(let e=0;e<r.length;e++){a[o.indexOf(r[e])]=u[e]}const l=be(n.dag,e.id),p=l?.baseType??k,{id:f}=Pe(n,{baseType:p,dimension:s},a);return e.id=f,!0}return Reflect.set(...arguments)}};return i}(0,t,n))}const Se={...{acos:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],acosh:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],asin:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],asinh:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],atan:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0},{params:[Y.FLOAT,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],atanh:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],cos:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],cosh:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],degrees:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],radians:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],sin:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],sinh:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],tan:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],tanh:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],abs:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0},{params:[Y.FLOAT],returnType:Y.INT,isp5Function:!0}],ceil:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],clamp:[{params:[Y.FLOAT,Y.FLOAT,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1},{params:[Y.FLOAT,K.float1,K.float1],returnType:Y.FLOAT,isp5Function:!1},{params:[Y.INT,Y.INT,Y.INT],returnType:Y.INT,isp5Function:!1},{params:[Y.INT,K.int1,K.int1],returnType:Y.INT,isp5Function:!1}],dFdx:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],dFdy:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],exp:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],exp2:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],floor:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],fma:[{params:[Y.FLOAT,Y.FLOAT,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],fract:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],fwidth:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],inversesqrt:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],log:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],log2:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],max:[{params:[Y.FLOAT,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0},{params:[Y.FLOAT,K.float1],returnType:Y.FLOAT,isp5Function:!0},{params:[Y.INT,Y.INT],returnType:Y.INT,isp5Function:!0},{params:[Y.INT,K.int1],returnType:Y.INT,isp5Function:!0}],min:[{params:[Y.FLOAT,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0},{params:[Y.FLOAT,K.float1],returnType:Y.FLOAT,isp5Function:!0},{params:[Y.INT,Y.INT],returnType:Y.INT,isp5Function:!0},{params:[Y.INT,K.int1],returnType:Y.INT,isp5Function:!0}],mix:[{params:[Y.FLOAT,Y.FLOAT,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1},{params:[Y.FLOAT,Y.FLOAT,K.float1],returnType:Y.FLOAT,isp5Function:!1},{params:[Y.FLOAT,Y.FLOAT,Y.BOOL],returnType:Y.FLOAT,isp5Function:!1}],mod:[{params:[Y.FLOAT,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0},{params:[Y.FLOAT,K.float1],returnType:Y.FLOAT,isp5Function:!0}],pow:[{params:[Y.FLOAT,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],round:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],roundEven:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],sign:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1},{params:[Y.INT],returnType:Y.INT,isp5Function:!1}],smoothstep:[{params:[Y.FLOAT,Y.FLOAT,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1},{params:[K.float1,K.float1,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],sqrt:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],step:[{params:[Y.FLOAT,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],trunc:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],cross:[{params:[K.float3,K.float3],returnType:K.float3,isp5Function:!0}],distance:[{params:[Y.FLOAT,Y.FLOAT],returnType:K.float1,isp5Function:!0}],dot:[{params:[Y.FLOAT,Y.FLOAT],returnType:K.float1,isp5Function:!0}],equal:[{params:[Y.FLOAT,Y.FLOAT],returnType:Y.BOOL,isp5Function:!1},{params:[Y.INT,Y.INT],returnType:Y.BOOL,isp5Function:!1},{params:[Y.BOOL,Y.BOOL],returnType:Y.BOOL,isp5Function:!1}],faceforward:[{params:[Y.FLOAT,Y.FLOAT,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],length:[{params:[Y.FLOAT],returnType:K.float1,isp5Function:!1}],normalize:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],notEqual:[{params:[Y.FLOAT,Y.FLOAT],returnType:Y.BOOL,isp5Function:!1},{params:[Y.INT,Y.INT],returnType:Y.BOOL,isp5Function:!1},{params:[Y.BOOL,Y.BOOL],returnType:Y.BOOL,isp5Function:!1}],reflect:[{params:[Y.FLOAT,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],refract:[{params:[Y.FLOAT,Y.FLOAT,K.float1],returnType:Y.FLOAT,isp5Function:!1}]}};function Fe(e,t,n){const{cfg:r,dag:i}=e;let{dimension:o,baseType:s}=t;1!==o&&xe("Created a scalar literal node with dimension > 1.");const a=Te(i,ye({nodeType:E.LITERAL,dimension:o,baseType:s,value:n}));return _e(r,r.currentBlock,a),{id:a,dimension:o}}function Ae(e,t,n){const{cfg:r,dag:i}=e,{dimension:o,baseType:s}=t,a=Te(i,ye({nodeType:E.VARIABLE,dimension:o,baseType:s,identifier:n}));return _e(r,r.currentBlock,a),{id:a,dimension:o}}function Ie(e,t,n){const r=Array.isArray(n)?n:[n],i=[];let{dimension:o,baseType:s}=t;const a=e.dag;let u=0,l=null;for(const t of r.flat(1/0))if(t&&t.isStrandsNode){const e=be(a,t.id);if(l=t.id,s=e.baseType,e.opCode===J.CONSTRUCTOR)for(const t of e.dependsOn)i.push(t);else i.push(t.id);u+=e.dimension}else if("number"!=typeof t)ve("type error",`You've tried to construct a scalar or vector type with a non-numeric value: ${t}`);else{const{id:n,dimension:r}=Fe(e,{dimension:1,baseType:s},t);i.push(n),u+=r}null===o?o=u:o>u&&1===u?u=o:1!==u&&u!==o&&ve("type error",`You've tried to construct a ${s+o} with ${u} components`);return{originalNodeID:l,mappedDependencies:i,inferredTypeInfo:{dimension:o,baseType:s,priority:q[s]}}}function Le(e,t,n){const r=ye({nodeType:E.OPERATION,opCode:J.CONSTRUCTOR,dimension:t.dimension,baseType:t.baseType,dependsOn:n});return Te(e.dag,r)}function Pe(e,t,n){const r=e.cfg;n=(Array.isArray(n)?n:[n]).flat(1/0).map((t=>t.isStrandsNode&&t.typeInfo().baseType===z&&1===t.typeInfo().dimension?function(e,t){const{id:n,dimension:r}=Ee(e,e.backend.getTypeName("float",t.typeInfo().dimension),[t],{overloads:[{params:[t.typeInfo()],returnType:{...t.typeInfo(),baseType:k}}]});return we(n,r,e)}(e,t):t));const{mappedDependencies:i,inferredTypeInfo:o}=Ie(e,t,n),s={baseType:t.baseType,dimension:o.dimension},a=Le(e,s,i);return t.baseType!==j&&_e(r,r.currentBlock,a),{id:a,dimension:s.dimension,components:i}}function Ee(e,t,n,{overloads:r}={}){const{cfg:i,dag:o}=e,s=r||Se[t],a=n.map((t=>Ie(e,K.defer,t))),u=s.filter((e=>e.params.length===a.length));if(0===u.length){const e=new Set,n=[];s.forEach((t=>e.add(t.params.length))),e.forEach((e=>n.push(`${e}`)));const r=n.join(", or ");ve("parameter validation error",`Function '${t}' has ${s.length} variants which expect ${r} arguments, but ${a.length} arguments were provided.`)}const l=e=>null===e.dimension;let p=null,f=0,c=null,d=null;for(const e of u){let t=!0,n=0;for(let r=0;r<a.length;r++){const i=a[r].inferredTypeInfo,o=e.params[r];let s=o.dimension;l(o)?(null!==d&&1!==d||(d=i.dimension),d===i.dimension||1===i.dimension&&d>=1||(t=!1),s=d):i.dimension>s&&(t=!1),i.baseType===o.baseType?n+=2:o.priority>i.priority&&(n+=1)}t&&(!p||n>f)&&(p=e,f=n,c={...e.returnType},l(c)&&(c.dimension=d))}null===p&&ve("parameter validation",`No matching overload for ${t} was found!`);let m=[];for(let t=0;t<p.params.length;t++){const n=a[t],r={...p.params[t]};if(l(r)&&(r.dimension=d),n.originalNodeID&&(h=n.inferredTypeInfo,g=r,h.dimension===g.dimension&&h.baseType===g.baseType))m.push(n.originalNodeID);else{const t=Le(e,r,n.mappedDependencies);_e(i,i.currentBlock,t),m.push(t)}}var h,g;const x=Te(o,ye({nodeType:E.OPERATION,opCode:J.FUNCTION_CALL,identifier:t,dependsOn:m,baseType:c.baseType,dimension:c.dimension}));return _e(i,i.currentBlock,x),{id:x,dimension:c.dimension}}function De(e,t,n){const{dag:r,cfg:i}=e,o=r.baseTypes[t.id],s=Te(r,ye({nodeType:E.OPERATION,baseType:o,dimension:n.length,opCode:Q.SWIZZLE,dependsOn:[t.id],swizzle:n}));return _e(i,i.currentBlock,s),{id:s,dimension:n.length}}function Re(e,t){if(e.nodeTypes[t]!==E.OPERATION)return!1;if(e.baseTypes[t]===H)return!1;return(e.usedBy[t]||[]).length>1}const Ve={float1:"f32",float2:"vec2<f32>",float3:"vec3<f32>",float4:"vec4<f32>",int1:"i32",int2:"vec2<i32>",int3:"vec3<i32>",int4:"vec4<i32>",bool1:"bool",bool2:"vec2<bool>",bool3:"vec3<bool>",bool4:"vec4<bool>",mat2:"mat2x2<f32>",mat3:"mat3x3<f32>",mat4:"mat4x4<f32>"},Ne={[te.DEFAULT]:(e,t,n)=>{const{dag:r,cfg:i}=t,o=i.blockInstructions[e]||[];for(const e of o){const t=r.nodeTypes[e];if(Re(r,e)){const t=Me.generateDeclaration(n,r,e);n.write(t)}t===E.STATEMENT&&Me.generateStatement(n,r,e),t===E.ASSIGNMENT&&(Me.generateAssignment(n,r,e),n.visitedNodes.add(e))}},[te.BRANCH](e,t,n){const{dag:r,cfg:i}=t,o=i.blockInstructions[e]||[];for(const e of o){const t=be(r,e);if(t.nodeType===E.PHI){const i=t.dependsOn||[];if(i.length>0){const t=i[0],r=n.tempNames[t];if(r){n.tempNames[e]=r;continue}}const o="T"+n.nextTempID++;n.tempNames[e]=o;const s=Ce(r,e),a=Me.getTypeName(s.baseType,s.dimension);let u;if(1===s.dimension)u="float"===s.baseType?"0.0":"0";else{const e="float"===s.baseType?"0.0":"0";u=`${a}(${Array(s.dimension).fill(e).join(", ")})`}n.write(`var ${o}: ${a} = ${u};`)}}this[te.DEFAULT](e,t,n)},[te.IF_COND](e,t,n){const{dag:r,cfg:i}=t,o=i.blockConditions[e],s=Me.generateExpression(n,r,o);n.write(`if (${s})`),this[te.DEFAULT](e,t,n)},[te.ELSE_COND](e,t,n){n.write("else"),this[te.DEFAULT](e,t,n)},[te.IF_BODY](e,t,n){this[te.DEFAULT](e,t,n),this.assignPhiNodeValues(e,t,n)},[te.SCOPE_START](e,t,n){n.write("{"),n.indent++},[te.SCOPE_END](e,t,n){n.indent--,n.write("}")},[te.MERGE](e,t,n){this[te.DEFAULT](e,t,n)},[te.FUNCTION](e,t,n){this[te.DEFAULT](e,t,n)},[te.FOR](e,t,n){const{dag:r,cfg:i}=t,o=i.blockInstructions[e]||[];n.write("for (");const s=n.suppressSemicolon;for(let e=0;e<o.length;e++){const t=o[e],i=be(r,t),s=e===o.length-1;if(n.suppressSemicolon=s,Re(r,t)){const e=Me.generateDeclaration(n,r,t);n.write(e)}i.nodeType===E.STATEMENT&&Me.generateStatement(n,r,t),i.nodeType===E.ASSIGNMENT&&(Me.generateAssignment(n,r,t),n.visitedNodes.add(t))}n.suppressSemicolon=s,n.write(")")},assignPhiNodeValues(e,t,n){const{dag:r,cfg:i}=t,o=i.outgoingEdges[e]||[];for(const t of o){const o=i.blockInstructions[t]||[];for(const t of o){const i=be(r,t);if(i.nodeType===E.PHI){const o=i.phiBlocks?.indexOf(e);if(-1!==o&&o<i.dependsOn.length){const e=i.dependsOn[o],s=n.tempNames[t];if(s&&null!==e){const t=Me.generateExpression(n,r,e);n.write(`${s} = ${t};`)}}}}}}},Me={hookEntry(e){const t=`(${e.parameters.map((e=>`${e.type.properties?`_p5_strands_raw_${e.name}`:e.name}: ${e.type.typeName}`)).join(", ")}) {`,n=e.parameters.filter((e=>e.type.properties)).map((e=>` var ${e.name} = _p5_strands_raw_${e.name};`)).join("\n");return n?t+"\n"+n:t},addTextureBindingsToDeclarations(e){if(!e.renderer||!e.baseShader)return;let t=e.renderer.getNextBindingIndex({vert:e.baseShader.vertSrc(),frag:e.baseShader.fragSrc()});for(const{name:n,typeInfo:r}of e.uniforms)if("sampler2D"===r.baseType){const r=`@group(0) @binding(${t}) var ${n}: texture_2d<f32>;`,i=`@group(0) @binding(${t+1}) var ${n}_sampler: sampler;`;e.vertexDeclarations.add(r),e.vertexDeclarations.add(i),e.fragmentDeclarations.add(r),e.fragmentDeclarations.add(i),t+=2}},getTypeName(e,t){const n=Ve[e+t];return n||e},generateHookUniformKey(e,t){return"sampler2D"===t.baseType?`${e}: sampler2D`:`${e}: ${this.getTypeName(t.baseType,t.dimension)}`},generateVaryingVariable(e,t){return`${e}: ${this.getTypeName(t.baseType,t.dimension)}`},generateLocalDeclaration(e,t){return`var<private> ${e}: ${this.getTypeName(t.baseType,t.dimension)};`},generateStatement(e,t,n){const r=be(t,n),i=e.suppressSemicolon?"":";";if(r.statementType===V)e.write(`discard${i}`);else if(r.statementType===N)e.write(`break${i}`);else if(r.statementType===U){const n=r.dependsOn[0],o=this.generateExpression(e,t,n);e.write(`${o}${i}`)}else if(r.statementType===B)e.write(i);else if(r.statementType===M){const n=r.dependsOn[0],o=this.generateExpression(e,t,n);e.write(`return ${o}${i}`)}},generateAssignment(e,t,n){const r=be(t,n),i=r.dependsOn[0],o=r.dependsOn[1],s=be(t,i),a=e.suppressSemicolon?"":";";if(s.opCode===Q.SWIZZLE){const n=s.dependsOn[0],r=be(t,n),i=this.generateExpression(e,t,n),u=s.swizzle,l=r.dimension,p=this.generateExpression(e,t,o),f=[];for(let e=0;e<l;e++)f[e]={target:"self",index:e};const c=e=>"xyzw".includes(e)?"xyzw".indexOf(e):"rgba".includes(e)?"rgba".indexOf(e):-1;for(let e=0;e<u.length;e++){const t=c(u[e]);t>=0&&t<l&&(f[t]={target:"rhs",index:e})}const d=this.getTypeName(r.baseType,l),m=f.map((({target:e,index:t})=>`${"self"===e?i:p}.${"xyzw"[t]}`));e.write(`${i} = ${d}(${m.join(", ")})${a}`)}else{const n=this.generateExpression(e,t,i),r=this.generateExpression(e,t,o);n&&r&&n!==r&&e.write(`${n} = ${r}${a}`)}},generateDeclaration(e,t,n){const r=this.generateExpression(e,t,n),i="T"+e.nextTempID++;e.tempNames[n]=i;const o=Ce(t,n);return`var ${i}: ${this.getTypeName(o.baseType,o.dimension)} = ${r};`},generateReturnStatement(e,t,n,r){const i=e.dag,o=be(i,n);if((s=r)&&s.properties){const e=r;for(let n=0;n<e.properties.length;n++){const r=e.properties[n],s=this.generateExpression(t,i,o.dependsOn[n]);r.name!==s&&t.write(`${o.identifier}.${r.name} = ${s};`)}}var s;t.write(`return ${this.generateExpression(t,i,n)};`)},generateExpression(e,t,n){const r=be(t,n);if(e.tempNames?.[n])return e.tempNames[n];switch(r.nodeType){case E.LITERAL:return r.baseType===k?r.value.toFixed(4):r.value;case E.VARIABLE:if(e.shaderContext&&e.strandsContext?.sharedVariables?.has(r.identifier)){const t=e.strandsContext.sharedVariables.get(r.identifier);"vertex"===e.shaderContext?t.usedInVertex=!0:"fragment"===e.shaderContext&&(t.usedInFragment=!0)}const i=e.strandsContext?.uniforms?.find((e=>e.name===r.identifier));return i&&"sampler2D"!==i.typeInfo.baseType?`hooks.${r.identifier}`:r.identifier;case E.OPERATION:const o=r.usedBy.length>0;if(r.opCode===J.CONSTRUCTOR){if(r.baseType===H)return this.generateExpression(e,t,r.dependsOn[0]);return`${this.getTypeName(r.baseType,r.dimension)}(${r.dependsOn.map((n=>this.generateExpression(e,t,n))).join(", ")})`}if(r.opCode===J.FUNCTION_CALL){if("mod"===r.identifier&&2===r.dependsOn.length){const[n,i]=r.dependsOn,o=this.generateExpression(e,t,n),s=this.generateExpression(e,t,i);return r.usedBy.length>0?`(${o} % ${s})`:`${o} % ${s}`}if("atan"===r.identifier&&2===r.dependsOn.length){return`atan2(${r.dependsOn.map((n=>this.generateExpression(e,t,n))).join(", ")})`}const n=r.dependsOn.map((n=>this.generateExpression(e,t,n)));return`${r.identifier}(${n.join(", ")})`}if(r.opCode===X.MEMBER_ACCESS){const[n,i]=r.dependsOn;return`${this.generateExpression(e,t,n)}.${this.generateExpression(e,t,i)}`}if(r.opCode===Q.SWIZZLE){const n=r.dependsOn[0];return`${this.generateExpression(e,t,n)}.${r.swizzle}`}if(2===r.dependsOn.length){const[n,i]=r.dependsOn,s=this.generateExpression(e,t,n),a=this.generateExpression(e,t,i);if(r.opCode===X.MODULO)return`(${s} % ${a})`;const u=ee[r.opCode];return o?`(${s} ${u} ${a})`:`${s} ${u} ${a}`}if(r.opCode===Q.LOGICAL_NOT||r.opCode===Q.NEGATE||r.opCode===Q.PLUS){const[n]=r.dependsOn,i=this.generateExpression(e,t,n);return`${ee[r.opCode]}${i}`}case E.PHI:if(r.identifier)return r.identifier;if(e.tempNames?.[n])return e.tempNames[n];{const n=r.dependsOn.filter((e=>null!==e));if(n.length>0)return this.generateExpression(e,t,n[0]);throw new Error("No valid inputs for node")}case E.ASSIGNMENT:xe("ASSIGNMENT nodes should not be used as expressions");default:xe(`${D[r.nodeType]} code generation not implemented yet`)}},generateBlock(e,t,n){const r=t.cfg.blockTypes[e];(Ne[r]||Ne[te.DEFAULT]).call(Ne,e,t,n)},createGetTextureCall(e,t){const n=t[0],r=t[1],{dag:i}=e,o=be(i,n.id).identifier+"_sampler",s=Ae(e,{baseType:W,dimension:1},o),a=[n,we(s.id,s.dimension,e),r],{id:u,dimension:l}=Ee(e,"textureSample",a,{overloads:[{params:[K.sampler2D,K.sampler,K.float2],returnType:K.float4}]});return{id:u,dimension:l}},instanceIdReference:()=>"instanceID"};const Ue="\n// Group 0: Filter Properties\nstruct FilterUniforms {\n canvasSize: vec2<f32>,\n texelSize: vec2<f32>,\n}\n\n// Group 1: Model Transform\nstruct ModelUniforms {\n uModelViewMatrix: mat4x4<f32>,\n}\n\n// Group 2: Camera and Projection\nstruct CameraUniforms {\n uProjectionMatrix: mat4x4<f32>,\n}\n\n@group(0) @binding(0) var<uniform> filterParams: FilterUniforms;\n@group(0) @binding(1) var tex0: texture_2d<f32>;\n@group(0) @binding(2) var tex0_sampler: sampler;\n@group(1) @binding(0) var<uniform> model: ModelUniforms;\n@group(2) @binding(0) var<uniform> camera: CameraUniforms;\n",Be=Ue+"\nstruct VertexInput {\n @location(0) aPosition: vec3<f32>,\n @location(1) aTexCoord: vec2<f32>,\n}\n\nstruct VertexOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) vTexCoord: vec2<f32>,\n}\n\n@vertex\nfn main(input: VertexInput) -> VertexOutput {\n var output: VertexOutput;\n\n // transferring texcoords for the frag shader\n output.vTexCoord = input.aTexCoord;\n\n // copy position with a fourth coordinate for projection (1.0 is normal)\n let positionVec4 = vec4<f32>(input.aPosition, 1.0);\n\n // project to 3D space\n output.position = camera.uProjectionMatrix * model.uModelViewMatrix * positionVec4;\n\n return output;\n}\n",ke=Ue+"\nstruct FilterInputs {\n texCoord: vec2<f32>,\n canvasSize: vec2<f32>,\n texelSize: vec2<f32>,\n}\n\nstruct FragmentInput {\n @location(0) vTexCoord: vec2<f32>,\n}\n\nstruct FragmentOutput {\n @location(0) color: vec4<f32>,\n}\n\n@fragment\nfn main(input: FragmentInput) -> FragmentOutput {\n var output: FragmentOutput;\n var inputs: FilterInputs;\n inputs.texCoord = input.vTexCoord;\n inputs.canvasSize = filterParams.canvasSize;\n inputs.texelSize = filterParams.texelSize;\n\n var outColor = HOOK_getColor(inputs, tex0, tex0_sampler);\n outColor = vec4<f32>(outColor.rgb * outColor.a, outColor.a);\n output.color = outColor;\n\n return output;\n}\n",ze="\nstruct Uniforms {\n uModelViewMatrix: mat4x4<f32>,\n uProjectionMatrix: mat4x4<f32>,\n uNormalMatrix: mat3x3<f32>,\n roughness: f32,\n};\n",Ge="\nconst PI = 3.14159265359;\n\nfn nTOE(v: vec3<f32>) -> vec2<f32> {\n // x = r sin(phi) cos(theta)\n // y = r cos(phi)\n // z = r sin(phi) sin(theta)\n let phi = acos(v.y);\n // if phi is 0, then there are no x, z components\n var theta = 0.0;\n // else\n theta = acos(v.x / sin(phi));\n let sinTheta = v.z / sin(phi);\n if (sinTheta < 0.0) {\n // Turn it into -theta, but in the 0-2PI range\n theta = 2.0 * PI - theta;\n }\n theta = theta / (2.0 * PI);\n let phiNorm = phi / PI;\n\n return vec2<f32>(phiNorm, theta);\n}\n\nfn random(p: vec2<f32>) -> f32 {\n let p3 = fract(vec3<f32>(p.x, p.y, p.x) * 0.1031);\n let dotP3 = dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n",$e=`\nstruct VertexInput {\n @location(0) aPosition: vec3<f32>,\n @location(1) aNormal: vec3<f32>,\n @location(2) aTexCoord: vec2<f32>,\n}\n\nstruct VertexOutput {\n @builtin(position) Position: vec4<f32>,\n @location(0) localPos: vec3<f32>,\n @location(1) vWorldNormal: vec3<f32>,\n @location(2) vWorldPosition: vec3<f32>,\n @location(3) vTexCoord: vec2<f32>,\n}\n\n${ze}\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\n\n@vertex\nfn main(input: VertexInput) -> VertexOutput {\n var output: VertexOutput;\n\n // Multiply the position by the matrix\n let viewModelPosition = uniforms.uModelViewMatrix * vec4<f32>(input.aPosition, 1.0);\n output.Position = uniforms.uProjectionMatrix * viewModelPosition;\n\n // Orient the normals and pass to the fragment shader\n output.vWorldNormal = uniforms.uNormalMatrix * input.aNormal;\n\n // Send the view position to the fragment shader\n output.vWorldPosition = viewModelPosition.xyz;\n\n output.localPos = output.vWorldPosition;\n output.vTexCoord = input.aTexCoord;\n\n return output;\n}\n`,je=`\nstruct FragmentInput {\n @location(0) localPos: vec3<f32>,\n @location(3) vTexCoord: vec2<f32>,\n}\n\n${ze}\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\n@group(0) @binding(1) var environmentMap: texture_2d<f32>;\n@group(0) @binding(2) var environmentMap_sampler: sampler;\n\n${Ge}\n\n@fragment\nfn main(input: FragmentInput) -> @location(0) vec4<f32> {\n // The sample direction equals the hemisphere's orientation\n let phi = input.vTexCoord.x * 2.0 * PI;\n let theta = input.vTexCoord.y * PI;\n let x = sin(theta) * cos(phi);\n let y = sin(theta) * sin(phi);\n let z = cos(theta);\n let normal = vec3<f32>(x, y, z);\n\n // Discretely sampling the hemisphere given the integral's\n // spherical coordinates translates to the following fragment code:\n var irradiance = vec3<f32>(0.0);\n let up = vec3<f32>(0.0, 1.0, 0.0);\n let right = normalize(cross(up, normal));\n let upNorm = normalize(cross(normal, right));\n\n // We specify a fixed sampleDelta delta value to traverse\n // the hemisphere; decreasing or increasing the sample delta\n // will increase or decrease the accuracy respectively.\n let sampleDelta = 0.100;\n var nrSamples = 0.0;\n let randomOffset = random(input.vTexCoord) * sampleDelta;\n\n for (var rawPhi = 0.0; rawPhi < 2.0 * PI; rawPhi += sampleDelta) {\n let phiSample = rawPhi + randomOffset;\n for (var rawTheta = 0.0; rawTheta < 0.5 * PI; rawTheta += sampleDelta) {\n let thetaSample = rawTheta + randomOffset;\n // spherical to cartesian (in tangent space) // tangent space to world // add each sample result to irradiance\n let xSample = sin(thetaSample) * cos(phiSample);\n let ySample = sin(thetaSample) * sin(phiSample);\n let zSample = cos(thetaSample);\n let tangentSample = vec3<f32>(xSample, ySample, zSample);\n\n let sampleVec = tangentSample.x * right + tangentSample.y * upNorm + tangentSample.z * normal;\n let envSample = textureSample(environmentMap, environmentMap_sampler, nTOE(sampleVec));\n irradiance += envSample.xyz * cos(thetaSample) * sin(thetaSample);\n nrSamples += 1.0;\n }\n }\n // divide by the total number of samples taken, giving us the average sampled irradiance.\n irradiance = PI * irradiance * (1.0 / nrSamples);\n\n return vec4<f32>(irradiance, 1.0);\n}\n`,He=`\nstruct FragmentInput {\n @location(0) localPos: vec3<f32>,\n @location(3) vTexCoord: vec2<f32>,\n}\n\n${ze}\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\n@group(0) @binding(1) var environmentMap: texture_2d<f32>;\n@group(0) @binding(2) var environmentMap_sampler: sampler;\n\n${Ge}\n\nfn VanDerCorput(nIn: i32, base: i32) -> f32 {\n // Use the bit manipulation version for WebGPU (equivalent to WEBGL2 version)\n var n = u32(nIn);\n n = (n << 16u) | (n >> 16u);\n n = ((n & 0x55555555u) << 1u) | ((n & 0xAAAAAAAAu) >> 1u);\n n = ((n & 0x33333333u) << 2u) | ((n & 0xCCCCCCCCu) >> 2u);\n n = ((n & 0x0F0F0F0Fu) << 4u) | ((n & 0xF0F0F0F0u) >> 4u);\n n = ((n & 0x00FF00FFu) << 8u) | ((n & 0xFF00FF00u) >> 8u);\n return f32(n) * 2.3283064365386963e-10; // / 0x100000000\n}\n\nfn HammersleyNoBitOps(i: i32, N: i32) -> vec2<f32> {\n return vec2<f32>(f32(i) / f32(N), VanDerCorput(i, 2));\n}\n\nfn ImportanceSampleGGX(Xi: vec2<f32>, N: vec3<f32>, roughness: f32) -> vec3<f32> {\n let a = roughness * roughness;\n\n let phi = 2.0 * PI * Xi.x;\n let cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a * a - 1.0) * Xi.y));\n let sinTheta = sqrt(1.0 - cosTheta * cosTheta);\n\n // from spherical coordinates to cartesian coordinates\n var H: vec3<f32>;\n H.x = cos(phi) * sinTheta;\n H.y = sin(phi) * sinTheta;\n H.z = cosTheta;\n\n // from tangent-space vector to world-space sample vector\n let up = select(vec3<f32>(0.0, 0.0, 1.0), vec3<f32>(1.0, 0.0, 0.0), abs(N.z) < 0.999);\n let tangent = normalize(cross(up, N));\n let bitangent = cross(N, tangent);\n\n let sampleVec = tangent * H.x + bitangent * H.y + N * H.z;\n return normalize(sampleVec);\n}\n\n@fragment\nfn main(input: FragmentInput) -> @location(0) vec4<f32> {\n let SAMPLE_COUNT = 400i; // 4096\n let lowRoughnessLimit = i32(pow(2.0, (uniforms.roughness + 0.1) * 20.0));\n var totalWeight = 0.0;\n var prefilteredColor = vec3<f32>(0.0);\n let phi = input.vTexCoord.x * 2.0 * PI;\n let theta = input.vTexCoord.y * PI;\n let x = sin(theta) * cos(phi);\n let y = sin(theta) * sin(phi);\n let z = cos(theta);\n let N = vec3<f32>(x, y, z);\n let V = N;\n\n for (var i = 0i; i < SAMPLE_COUNT; i++) {\n // break at smaller sample numbers for low roughness levels\n if (i == lowRoughnessLimit) {\n break;\n }\n let Xi = HammersleyNoBitOps(i, SAMPLE_COUNT);\n let H = ImportanceSampleGGX(Xi, N, uniforms.roughness);\n let L = normalize(2.0 * dot(V, H) * H - V);\n\n let NdotL = max(dot(N, L), 0.0);\n // Always sample the texture to maintain uniform control flow\n let envSample = textureSample(environmentMap, environmentMap_sampler, nTOE(L));\n // Only add to accumulators if NdotL > 0\n if (NdotL > 0.0) {\n prefilteredColor += envSample.xyz * NdotL;\n totalWeight += NdotL;\n }\n }\n prefilteredColor = prefilteredColor / totalWeight;\n\n return vec4<f32>(prefilteredColor, 1.0);\n}\n`,We=0,qe=1,Ke=2;function Ye(e,n){const{lineDefs:r}=function(e){const t={},n={};let r="";const i=function(n,i){r+=e(`STROKE_CAP_${n}`,`${i}`,"u32"),t[P[n]]=i},o=function(t,i){r+=e(`STROKE_JOIN_${t}`,`${i}`,"u32"),n[P[t]]=i};return i("ROUND",0),i("PROJECT",1),i("SQUARE",2),o("ROUND",0),o("MITER",1),o("BEVEL",2),{STROKE_CAP_ENUM:t,STROKE_JOIN_ENUM:n,lineDefs:r}}(((e,t,n)=>`const ${e}: ${n} = ${t};\n`)),{Renderer3D:i,Shader:o,Texture:s,MipmapTexture:a,Image:u,Camera:A,RGBA:I}=e;class L extends i{constructor(e,t,n,r,i){super(e,t,n,r,i),this.activeRenderPass=null,this.activeRenderPassEncoder=null,this.activeShaderOptions=null,this.activeShader=null,this.samplers=new Map,this.uniformBufferAlignment=256,this.activeUniformBuffers=[],this.currentUniformBuffer=void 0,this.uniformBufferPool=[],this.resettingUniformBuffers=[],this.dynamicEntryOffsets=new Uint32Array(64),this.currentCanvasColorTexture=null,this.currentCanvasColorTextureView=null,this.pixelReadBuffer=null,this.pixelReadBufferSize=0,this.strandsBackend=Me,this._shadersWithPools=[],this._geometriesWithPools=[],this._hasPendingDraws=!1,this._pendingCommandEncoders=[],this._postSubmitCallbacks=[],this._retiredBuffers=[],this._pixelReadCanvas=null,this._pixelReadCtx=null,this.mainFramebuffer=null,this._frameState=We,this.finalCamera=new A(this),this.finalCamera._computeCameraDefaultSettings(),this.finalCamera._setDefaultCamera(),this.depthFormat="depth24plus-stencil8",this.depthTexture=null,this.depthTextureView=null}async setupContext(){this._setAttributeDefaults(this._pInst),await this._initContext()}_setAttributeDefaults(e){const t={forceFallbackAdapter:!1,powerPreference:"high-performance"};null===e._webgpuAttributes?e._webgpuAttributes=t:e._webgpuAttributes=Object.assign(t,e._webgpuAttributes)}async _initContext(){if(this.adapter=await(navigator.gpu?.requestAdapter(this._webgpuAttributes)),this.device=await(this.adapter?.requestDevice({requiredFeatures:["depth32float-stencil8"]})),!this.device)throw new Error("Your browser does not support WebGPU.");this.queue=this.device.queue,this.drawingContext=this.canvas.getContext("webgpu"),this.presentationFormat=navigator.gpu.getPreferredCanvasFormat(),this.drawingContext.configure({device:this.device,format:this.presentationFormat,usage:GPUTextureUsage.RENDER_ATTACHMENT|GPUTextureUsage.COPY_SRC,alphaMode:"premultiplied"}),this.mainFramebuffer=this.createFramebuffer({_useCanvasFormat:!0}),this._updateSize(),this._update(),this.flushDraw()}async _setAttributes(t,n){if(void 0===this._pInst._webgpuAttributes)return void console.log("You are trying to use setAttributes on a p5.Graphics object that does not use a WebGPU renderer.");let r=!0;void 0!==n?(null===this._pInst._webgpuAttributes&&(this._pInst._webgpuAttributes={}),this._pInst._webgpuAttributes[t]!==n&&(this._webgpuAttributes[t]=n,r=!1)):t instanceof Object&&this._pInst._webgpuAttributes!==t&&(this._pInst._webgpuAttributes=t,r=!1),this.isP3D&&!r&&(!this._pInst._setupDone&&this.geometryBufferCache.numCached()>0?e._friendlyError("Sorry, Could not set the attributes, you need to call setAttributes() before calling the other drawing methods in setup()"):(await this._resetContext(null,null,L),this.states.curCamera&&(this.states.curCamera._renderer=this._renderer)))}_updateSize(){if(this.device&&this.depthFormat){if(this.depthTexture&&this.depthTexture.destroy){this.flushDraw();const e=this.depthTexture;this._postSubmitCallbacks.push((()=>e.destroy())),this.depthTextureView=null}this.depthTexture=this.device.createTexture({size:{width:Math.ceil(this.width*this._pixelDensity),height:Math.ceil(this.height*this._pixelDensity),depthOrArrayLayers:1},format:this.depthFormat,usage:GPUTextureUsage.RENDER_ATTACHMENT|GPUTextureUsage.COPY_SRC}),this.depthTextureView=this.depthTexture.createView(),this.clear()}}_getCanvasColorTextureView(){const e=this.drawingContext.getCurrentTexture();return this.currentCanvasColorTexture!==e&&(this.currentCanvasColorTexture=e,this.currentCanvasColorTextureView=e.createView()),this.currentCanvasColorTextureView}_beginActiveRenderPass(){if(this.activeRenderPass)return;const e=this.activeFramebuffer(),t={view:e?e.aaColorTexture?e.aaColorTextureView:e.colorTextureView:this._getCanvasColorTextureView(),loadOp:"load",storeOp:"store",resolveTarget:e&&e.aaColorTexture?e.colorTextureView:void 0},n=e?e.aaDepthTexture?e.aaDepthTextureView:e.depthTextureView:this.depthTextureView,r={colorAttachments:[t],depthStencilAttachment:n?{view:n,depthLoadOp:"load",depthStoreOp:"store",depthClearValue:1,stencilLoadOp:"load",stencilStoreOp:"store",depthReadOnly:!1,stencilReadOnly:!1}:void 0},i=this.device.createCommandEncoder(),o=i.beginRenderPass(r);this.activeRenderPassEncoder=i,this.activeRenderPass=o}_finishActiveRenderPass(){if(!this.activeRenderPass)return;const e=this.activeRenderPassEncoder;this.activeRenderPass.end(),this._pendingCommandEncoders.push(e.finish()),this.activeRenderPassEncoder=null,this.activeRenderPass=null,this.activeShader=null,this.activeShaderOptions=null}clear(...e){if(!this.device||!this.drawingContext)return;const t=e[0]||0,n=e[1]||0,r=e[2]||0,i=e[3]||0;this._frameState!==We||this.activeFramebuffer()||(this._frameState=qe),this._finishActiveRenderPass();const o=this.device.createCommandEncoder(),s=this.activeFramebuffer(),a={view:s?s.aaColorTexture?s.aaColorTextureView:s.colorTextureView:this._getCanvasColorTextureView(),clearValue:{r:t*i,g:n*i,b:r*i,a:i},loadOp:"clear",storeOp:"store",resolveTarget:s&&s.aaColorTexture?s.colorTextureView:void 0},u=s?s.aaDepthTexture?s.aaDepthTextureView:s.depthTextureView:this.depthTextureView,l=u?{view:u,depthClearValue:1,depthLoadOp:"clear",depthStoreOp:"store",stencilLoadOp:"load",stencilStoreOp:"store"}:void 0,p={colorAttachments:[a],...l?{depthStencilAttachment:l}:{}};o.beginRenderPass(p).end(),this._pendingCommandEncoders.push(o.finish()),this._hasPendingDraws=!0}clearDepth(e=1){if(!this.device||!this.depthTextureView)return;this._finishActiveRenderPass();const t=this.device.createCommandEncoder(),n=this.activeFramebuffer(),r=n?n.aaDepthTexture?n.aaDepthTextureView:n.depthTextureView:this.depthTextureView;if(!r)return;const i={colorAttachments:[],depthStencilAttachment:{view:r,depthClearValue:e,depthLoadOp:"clear",depthStoreOp:"store",stencilLoadOp:"load",stencilStoreOp:"store"}};t.beginRenderPass(i).end(),this._pendingCommandEncoders.push(t.finish()),this._hasPendingDraws=!0}_prepareBuffer(e,t,n){const r=n.attributes[e.attr];if(!r)return;const{src:i,dst:o,size:s,map:a}=e,u=this.device,l=this._getOrMakeCachedBuffers(t);let p=t[i];if(!p||0===p.length){if(!e.default)return;p=t[i]=e.default(t),p.isDefault=!0}if(!l[o]||!1!==t.dirtyFlags[i]){const e=a?a(p):p,n=this._normalizeBufferData(e,Float32Array),r=this._getVertexBufferFromPool(t,o,n.byteLength),s=new n.constructor(n);r.dataCopy=s,u.queue.writeBuffer(r.buffer,0,s),l[o]=r.buffer,t.dirtyFlags[i]=!1}n.enableAttrib(r,s)}_disableRemainingAttributes(e){}_enableAttrib(e){const t=e.location;this.registerEnabled.has(t)||this.registerEnabled.add(t)}_ensureGeometryBuffers(e,t,n){if(!t)return;const r=this.device.createBuffer({size:4*Math.ceil(t.length*n.BYTES_PER_ELEMENT/4),usage:GPUBufferUsage.INDEX|GPUBufferUsage.COPY_DST,mappedAtCreation:!0});new n(r.getMappedRange()).set(t),r.unmap(),e.indexBuffer=r,e.indexBufferType=n===Uint32Array?"uint32":"uint16"}_freeBuffers(e){const t=e=>{e&&e.destroy&&e.destroy()};t(e.indexBuffer);const n=n=>{for(const r of n)t(e[r.dst]),e[r.dst]=null};n(this.buffers.stroke),n(this.buffers.fill),n(this.buffers.user)}_getValidSampleCount(e){return e<=1?1:4}_shaderOptions({mode:e}){const t=this.activeFramebuffer(),n=t?this._getWebGPUColorFormat(t):this.presentationFormat,r=t&&t.antialias?t.antialiasSamples:1,i=this._getValidSampleCount(r),o=t&&t.useDepth?this._getWebGPUDepthFormat(t):this.depthFormat,s=this.drawTarget(),a=this._clipping,u=s._isClipApplied;return{topology:5===e?"triangle-strip":"triangle-list",blendMode:this.states.curBlendMode,sampleCount:i,format:n,depthFormat:o,clipping:a,clipApplied:u}}_shaderOptionsDifferent(e){if(!this.activeShaderOptions)return!0;for(const t in this.activeShaderOptions)if(this.activeShaderOptions[t]!==e[t])return!0;return!1}_initShader(e){const t=this.device;e.vertModule=t.createShaderModule({code:e.vertSrc()}),e.fragModule=t.createShaderModule({code:e.fragSrc()}),e._pipelineCache=new Map,e.getPipeline=({topology:n,blendMode:r,sampleCount:i,format:o,depthFormat:s,clipping:a,clipApplied:u})=>{const l=`${n}_${r}_${i}_${o}_${s}_${a}_${u}`;if(!e._pipelineCache.has(l)){const p=t.createRenderPipeline({layout:e._pipelineLayout,vertex:{module:e.vertModule,entryPoint:"main",buffers:this._getVertexLayout(e)},fragment:{module:e.fragModule,entryPoint:"main",targets:[{format:o,blend:this._getBlendState(r)}]},primitive:{topology:n},multisample:{count:i},depthStencil:{format:s,depthWriteEnabled:!a,depthCompare:"less-equal",stencilFront:{compare:a?"always":u?"not-equal":"always",failOp:"keep",depthFailOp:"keep",passOp:a?"replace":"keep"},stencilBack:{compare:a?"always":u?"not-equal":"always",failOp:"keep",depthFailOp:"keep",passOp:a?"replace":"keep"},stencilReadMask:255,stencilWriteMask:a?255:0}});e._pipelineCache.set(l,p)}return e._pipelineCache.get(l)}}_finalizeShader(e){e._uniformBufferGroups=[],e.buffersDirty=new Set;for(const t of e._uniformGroups){const n=Object.values(t.uniforms),r=Math.max(0,...n.map((e=>e.offsetEnd))),i=16*Math.ceil(r/16);e._uniformBufferGroups.push({group:t.group,binding:t.binding,cacheKey:1e3*t.group+t.binding,varName:t.varName,structType:t.structType,uniforms:n,size:i,bufferPool:[],nextBufferPool:[],dynamic:n.some((e=>e.name.startsWith("uModel"))),buffersInUse:new Set,currentBuffer:null})}this._shadersWithPools.push(e);const t=new Map,n=new Map,r=new Map;for(const t of e._uniformBufferGroups){const e=r.get(t.group)||[];e.push({bufferGroup:t,binding:t.binding,visibility:GPUShaderStage.VERTEX|GPUShaderStage.FRAGMENT,buffer:{type:"uniform",hasDynamicOffset:t.dynamic}}),r.set(t.group,e)}for(const[e,t]of r.entries())t.sort(((e,t)=>e.binding-t.binding)),n.set(e,t);for(const t of e.samplers){const e=t.group,r=n.get(e)||[];if(!["sampler","texture_2d<f32>"].includes(t.type))throw new Error(`Unsupported texture type: ${t.type}`);r.push({binding:t.binding,visibility:t.visibility,sampler:"sampler"===t.type?{type:"filtering"}:void 0,texture:"texture_2d<f32>"===t.type?{sampleType:"float",viewDimension:"2d"}:void 0,uniform:t}),r.sort(((e,t)=>e.binding-t.binding)),n.set(e,r)}const i=[];for(const[e,r]of n){const n=this.device.createBindGroupLayout({entries:r});t.set(e,n),i.push([e,r])}e._groupEntries=i,e._bindGroupLayouts=[...t.values()],e._cachedBindGroup={},e._lastDynamicBuffer={},e._pipelineLayout=this.device.createPipelineLayout({bindGroupLayouts:e._bindGroupLayouts})}_getBlendState(e){switch(e){case l:return{color:{operation:"add",srcFactor:"one",dstFactor:"one-minus-src-alpha"},alpha:{operation:"add",srcFactor:"one",dstFactor:"one-minus-src-alpha"}};case f:return{color:{operation:"add",srcFactor:"one",dstFactor:"one"},alpha:{operation:"add",srcFactor:"one",dstFactor:"one"}};case p:return{color:{operation:"add",srcFactor:"zero",dstFactor:"one-minus-src-alpha"},alpha:{operation:"add",srcFactor:"zero",dstFactor:"one-minus-src-alpha"}};case g:return{color:{operation:"add",srcFactor:"dst-color",dstFactor:"one-minus-src-alpha"},alpha:{operation:"add",srcFactor:"dst-alpha",dstFactor:"one-minus-src-alpha"}};case x:return{color:{operation:"add",srcFactor:"one",dstFactor:"one-minus-src-color"},alpha:{operation:"add",srcFactor:"one",dstFactor:"one-minus-src-alpha"}};case h:return{color:{operation:"add",srcFactor:"one-minus-dst-color",dstFactor:"one-minus-src-color"},alpha:{operation:"add",srcFactor:"one",dstFactor:"one"}};case v:return{color:{operation:"add",srcFactor:"one",dstFactor:"zero"},alpha:{operation:"add",srcFactor:"one",dstFactor:"zero"}};case m:return{color:{operation:"reverse-subtract",srcFactor:"one",dstFactor:"one"},alpha:{operation:"add",srcFactor:"one",dstFactor:"one-minus-src-alpha"}};case c:return{color:{operation:"min",srcFactor:"one",dstFactor:"one"},alpha:{operation:"min",srcFactor:"one",dstFactor:"one"}};case d:return{color:{operation:"max",srcFactor:"one",dstFactor:"one"},alpha:{operation:"max",srcFactor:"one",dstFactor:"one"}};default:return void console.warn(`Unsupported blend mode: ${e}`)}}_applyColorBlend(){}_getVertexLayout(e){const t=[];for(const n in e.attributes){const r=e.attributes[n];if(!r||-1===r.location)continue;const i=this.buffers[e.shaderType].find((e=>e.attr===n))||this.buffers.user.find((e=>e.attr===n));if(!i)continue;const{size:o}=i,s=this._getFormatFromSize(o);t.push({arrayStride:4*o,stepMode:"vertex",attributes:[{shaderLocation:r.location,offset:0,format:s}]})}return t}_getVertexBuffers(e){if(!e._vertexBuffers){const t=[];for(const n in e.attributes){const r=e.attributes[n];if(!r||-1===r.location)continue;const i=this.buffers[e.shaderType].find((e=>e.attr===n))||this.buffers.user.find((e=>e.attr===n));i&&t.push(i)}e._vertexBuffers=t}return e._vertexBuffers}_getFormatFromSize(e){switch(e){case 1:return"float32";case 2:return"float32x2";case 3:return"float32x3";case 4:return"float32x4";default:throw new Error(`Unsupported attribute size: ${e}`)}}_useShader(e,t){}_updateViewport(){this._origViewport={width:this.width,height:this.height},this._viewport=[0,0,this.width,this.height]}_createPixelsArray(){this.pixels=new Uint8Array(this.width*this.pixelDensity()*this.height*this.pixelDensity()*4)}viewport(){}zClipRange(){return[0,1]}defaultNearScale(){return.01}defaultFarScale(){return 100}_resetBuffersBeforeDraw(){this._finishActiveRenderPass(),this._frameState=We;const e=this.activeFramebuffer(),t=this.device.createCommandEncoder(),n=e?e.aaDepthTexture?e.aaDepthTextureView:e.depthTextureView:this.depthTextureView;if(n){const e={colorAttachments:[],depthStencilAttachment:{view:n,depthClearValue:1,depthLoadOp:"clear",depthStoreOp:"store",stencilLoadOp:"load",stencilStoreOp:"store"}};t.beginRenderPass(e).end(),this._pendingCommandEncoders.push(t.finish()),this._hasPendingDraws=!0}}_promoteToFramebuffer(){if(this._frameState===Ke)return;if(this.activeFramebuffer())return;this.flushDraw(),this._frameState=Ke;const e=this.drawingContext.getCurrentTexture();this.mainFramebuffer.width===this.width&&this.mainFramebuffer.height===this.height||this.mainFramebuffer.resize(this.width,this.height);const t=this.device.createCommandEncoder();t.copyTextureToTexture({texture:e,origin:{x:0,y:0,z:0},mipLevel:0},{texture:this.mainFramebuffer.colorTexture,origin:{x:0,y:0,z:0},mipLevel:0},{width:Math.ceil(this.width*this._pixelDensity),height:Math.ceil(this.height*this._pixelDensity),depthOrArrayLayers:1}),t.copyTextureToTexture({texture:this.depthTexture,origin:{x:0,y:0,z:0},mipLevel:0},{texture:this.mainFramebuffer.depthTexture,origin:{x:0,y:0,z:0},mipLevel:0},{width:Math.ceil(this.width*this._pixelDensity),height:Math.ceil(this.height*this._pixelDensity),depthOrArrayLayers:1}),this._pendingCommandEncoders.push(t.finish()),this._hasPendingDraws=!0;const n=this.states.uModelMatrix.copy();this.mainFramebuffer.defaultCamera.set(this.states.curCamera),this.mainFramebuffer.begin(),this.states.uModelMatrix.set(n)}_promoteToFramebufferWithoutCopy(){this.mainFramebuffer.width===this.width&&this.mainFramebuffer.height===this.height||this.mainFramebuffer.resize(this.width,this.height),this._frameState=Ke,this.flushDraw();const e=this.states.uModelMatrix.copy();this.mainFramebuffer.defaultCamera.set(this.states.curCamera),this.mainFramebuffer.begin(),this.states.uModelMatrix.set(e)}_initializeGeometryBufferPools(e){e._vertexBufferPools||(e._vertexBufferPools={},e._vertexBuffersInUse={},e._vertexBuffersToReturn={},this._geometriesWithPools.push(e))}_getVertexBufferFromPool(e,t,n){this._initializeGeometryBufferPools(e),e._vertexBufferPools[t]||(e._vertexBufferPools[t]=[]),e._vertexBuffersInUse[t]||(e._vertexBuffersInUse[t]=[]),e._vertexBuffersToReturn[t]||(e._vertexBuffersToReturn[t]=[]);const r=e._vertexBufferPools[t];if(r.length>0){const i=r.pop();if(i.buffer.size>=n)return e._vertexBuffersInUse[t].push(i),i;this._retiredBuffers.push(i.buffer)}const i={buffer:this.device.createBuffer({size:n,usage:GPUBufferUsage.VERTEX|GPUBufferUsage.COPY_DST}),size:n,dataCopy:null};return e._vertexBuffersInUse[t].push(i),i}_returnVertexBuffersToPool(){for(const e of this._geometriesWithPools)if(e._vertexBuffersToReturn)for(const[t,n]of Object.entries(e._vertexBuffersToReturn))if(n.length>0){const r=e._vertexBufferPools[t]||[];for(;n.length>0;){const e=n.pop();e.dataCopy=null,r.push(e)}e._vertexBufferPools[t]=r}}onReset(e){this._markGeometryBuffersForReturn(e)}_markGeometryBuffersForReturn(e){if(e._vertexBuffersInUse&&e._vertexBuffersToReturn)for(const[t,n]of Object.entries(e._vertexBuffersInUse))if(n.length>0){const r=e._vertexBuffersToReturn[t]||[];for(;n.length>0;){const e=n.pop();r.push(e)}e._vertexBuffersToReturn[t]=r}}_getUniformBufferFromPool(e){if(e.bufferPool.length>0){const t=e.bufferPool.pop();return e.buffersInUse.add(t),t}const t=this.device.createBuffer({size:e.size,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),n=new Float32Array(e.size/4),r={buffer:t,data:n,dataView:new DataView(n.buffer)};return e.buffersInUse.add(r),r}_getDynamicUniformBufferFromPool(e){let t;if(this.currentUniformBuffer&&this.currentUniformBuffer.offset+e.size<this.currentUniformBuffer.size)t=this.currentUniformBuffer;else if(this.uniformBufferPool.length>0)t=this.uniformBufferPool.pop(),this.activeUniformBuffers.push(t);else{const e=10240;t={dynamic:!0,lastOffset:0,offset:0,size:e,buffer:this.device.createBuffer({size:e,usage:GPUBufferUsage.MAP_WRITE|GPUBufferUsage.COPY_SRC,mappedAtCreation:!0}),uniformBuffer:this.device.createBuffer({size:e,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST})},t.data=new Float32Array(t.buffer.getMappedRange()),t.dataView=new DataView(t.data.buffer),this.activeUniformBuffers.push(t)}return this.currentUniformBuffer=t,t}_returnUniformBuffersToPool(){for(const e of this._shadersWithPools)this._returnShaderBuffersToPool(e)}_returnShaderBuffersToPool(e){if(e._uniformBufferGroups)for(const t of e._uniformBufferGroups){for(;t.nextBufferPool.length>0;)t.bufferPool.push(t.nextBufferPool.pop());for(const e of t.buffersInUse.keys())e!==t.currentBuffer&&t.nextBufferPool.push(e);t.buffersInUse.clear(),t.currentBuffer&&t.buffersInUse.add(t.currentBuffer)}}flushDraw(){if(this._finishActiveRenderPass(),this._hasPendingDraws){const e=this._pendingCommandEncoders;if(this._pendingCommandEncoders=[],this._hasPendingDraws=!1,this.activeUniformBuffers.length>0){const t=this.device.createCommandEncoder();for(const e of this.activeUniformBuffers)e.buffer.unmap(),t.copyBufferToBuffer(e.buffer,e.uniformBuffer);e.unshift(t.finish())}this.queue.submit(e);for(const e of this.activeUniformBuffers)e.offset=0,e.lastOffset=0,e.buffer.mapAsync(GPUMapMode.WRITE).then((()=>(e.data=new Float32Array(e.buffer.getMappedRange()),e.dataView=new DataView(e.data.buffer),this.uniformBufferPool.push(e),e)));if(this.activeUniformBuffers=[],this.currentUniformBuffer=void 0,this._postSubmitCallbacks.length>0){const e=this._postSubmitCallbacks;this._postSubmitCallbacks=[],this.device.queue.onSubmittedWorkDone().then((()=>{for(const t of e)t()}))}this.currentCanvasColorTexture=null,this.currentCanvasColorTextureView=null}}_ensurePixelReadCanvas(e,t){return this._pixelReadCanvas||(this._pixelReadCanvas=document.createElement("canvas"),this._pixelReadCtx=this._pixelReadCanvas.getContext("2d")),this._pixelReadCanvas.width===e&&this._pixelReadCanvas.height===t||(this._pixelReadCanvas.width=e,this._pixelReadCanvas.height=t),{canvas:this._pixelReadCanvas,ctx:this._pixelReadCtx}}resize(e,t){super.resize(e,t),this._hasPendingDraws=!0,this.flushDraw()}async finishDraw(){this.flushDraw();const e=[];if(this._frameState===Ke){for(;this.activeFramebuffers.length>0;){const t=this.activeFramebuffers.pop();e.unshift({fbo:t,diff:{...this.states}})}this.flushDraw(),this._pInst.push(),this.states.setValue("enableLighting",!1),this.states.setValue("activeImageLight",null),this._pInst.setCamera(this.finalCamera),this._pInst.shader(this._getBlitShader()),this._pInst.resetMatrix(),this._pInst.imageMode(this._pInst.CENTER),this._pInst.image(this.mainFramebuffer,0,0),this._pInst.pop(),this.flushDraw()}this._returnUniformBuffersToPool();for(const e of this._geometriesWithPools)this._markGeometryBuffersForReturn(e);this.resettingUniformBuffers=[],this._returnVertexBuffersToPool();const t=this._retiredBuffers;if(this._postSubmitCallbacks.push((()=>{for(const e of t)e&&e.destroy&&e.destroy()})),this._retiredBuffers=[],this._frameState===Ke)for(const{fbo:t,diff:n}of e){t===this.mainFramebuffer&&this._frameState===Ke||t.begin();for(const e in n)this.states.setValue(e,n[e])}}_drawBuffers(e,{mode:t=4,count:n=1}){const r=this.geometryBufferCache.getCached(e);if(!r)return;this._frameState!==We||this.activeFramebuffer()||this._promoteToFramebufferWithoutCopy(),this._beginActiveRenderPass();const i=this.activeRenderPass,o=this._curShader,s=this._shaderOptions({mode:t});(this.activeShader!==o||this._shaderOptionsDifferent(s))&&i.setPipeline(o.getPipeline(s)),this.activeShader=o,this.activeShaderOptions=s;this.drawTarget()._isClipApplied&&!this._clipping?i.setStencilReference(0):this._clipping&&i.setStencilReference(1);for(const e of o._vertexBuffers||this._getVertexBuffers(o)){const t=o.attributes[e.attr].location,n=r[e.dst];i.setVertexBuffer(t,n,0)}for(const e of o._uniformBufferGroups)if(e.dynamic){const t=this._getDynamicUniformBufferFromPool(e);o._lastDynamicBuffer[e.cacheKey]!==t&&(o._cachedBindGroup[e.group]=void 0,o._lastDynamicBuffer[e.cacheKey]=t),this._packUniformGroup(o,e.uniforms,t),t.lastOffset=t.offset,t.offset+=Math.ceil(e.size/this.uniformBufferAlignment)*this.uniformBufferAlignment,e.currentDynamicBuffer=t,e.lastOffset=t.lastOffset}else{let t;!this._hasGroupDataChanged(o,e)&&e.currentBuffer?(t=e.currentBuffer,e.buffersInUse.add(t)):(t=this._getUniformBufferFromPool(e),this._packUniformGroup(o,e.uniforms,t),this.device.queue.writeBuffer(t.buffer,0,t.data.buffer,t.data.byteOffset,t.data.byteLength),o.buffersDirty.delete(1e3*e.group+e.binding),o._cachedBindGroup[e.group]=void 0,e.currentBuffer=t)}for(const e of o.samplers){const t=1e3*e.group+e.binding;o.buffersDirty.has(t)&&(o._cachedBindGroup[e.group]=void 0,o.buffersDirty.delete(t))}for(const e of o._groupEntries){const t=e[0],n=e[1];let r=0;const s=[];let a=o._cachedBindGroup[t];for(const e of n){const t=e.bufferGroup,n=t?.currentBuffer||t?.currentDynamicBuffer;n?(t.dynamic&&(this.dynamicEntryOffsets[r++]=t.lastOffset),a||s.push({binding:e.binding,resource:t.dynamic?{buffer:n.uniformBuffer,offset:0,size:Math.ceil(t.size/this.uniformBufferAlignment)*this.uniformBufferAlignment}:{buffer:n.buffer}})):a||s.push({binding:e.binding,resource:"sampler"===e.uniform.type?(e.uniform.textureSource.texture||this._getEmptyTexture()).getSampler():(e.uniform.texture||this._getEmptyTexture()).textureHandle.view})}const u=o._bindGroupLayouts[t];a||(a=this.device.createBindGroup({layout:u,entries:s})),o._cachedBindGroup[t]=a,0===r?i.setBindGroup(t,a):i.setBindGroup(t,a,this.dynamicEntryOffsets,0,r)}if("fill"===o.shaderType)if(r.indexBuffer){const t=r.indexFormat||"uint16";i.setIndexBuffer(r.indexBuffer,t),i.drawIndexed(3*e.faces.length,n,0,0,0)}else i.draw(e.vertices.length,n,0,0);else if("text"===o.shaderType){if(!r.indexBuffer)throw new Error("Text geometry must have an index buffer");const t=r.indexFormat||"uint16";i.setIndexBuffer(r.indexBuffer,t),i.drawIndexed(3*e.faces.length,n,0,0,0)}r.lineVerticesBuffer&&"stroke"===o.shaderType&&i.draw(e.lineVertices.length/3,n,0,0),this._hasPendingDraws=!0}_packUniformGroup(e,t,n){const r=n.data,i=n.dataView,o=n.offset||0;for(const n of t){const t=e.uniforms[n.name];if(!t||t.isSampler)continue;const s=t._mappedData;if("u32"===t.baseType)if(4===t.size)i.setUint32(o+t.offset,s,!0);else for(let e=0;e<s.length;e++)i.setUint32(o+t.offset+4*e,s[e],!0);else if("i32"===t.baseType)if(4===t.size)i.setInt32(o+t.offset,s,!0);else for(let e=0;e<s.length;e++)i.setInt32(o+t.offset+4*e,s[e],!0);else if(t.packInPlace){const e=(o+t.offset)/4;r[e+0]=s[0],r[e+1]=s[1],r[e+2]=s[2],r[e+4]=s[3],r[e+5]=s[4],r[e+6]=s[5],r[e+8]=s[6],r[e+9]=s[7],r[e+10]=s[8]}else 4===t.size?r.set([s],(o+t.offset)/4):void 0!==s&&r.set(s,(o+t.offset)/4)}}_hasGroupDataChanged(e,t){return!t.currentBuffer||e.buffersDirty.has(1e3*t.group+t.binding)}_parseStruct(e,t){const n=e.match(new RegExp(`struct\\s+${t}\\s*\\{([^\\}]+)\\}`));if(!n)throw new Error(`Can't find a struct definition for ${t}`);const r=n[1],i={};let o,s=0,a=0;const u=/(?:@location\((\d+)\)\s+)?(\w+):\s*([^\n]+?),?\n/g,l=e=>{if(["f32","i32","u32","bool"].includes(e))return{align:4,size:4,items:1,baseType:e};if(/^vec[2-4](<f32>|f)$/.test(e)){const t=parseInt(e.match(/^vec([2-4])/)[1]);return{align:2===t?8:16,size:4*t,items:t,baseType:"f32"}}if(/^vec[2-4]<(i32|u32)>$/.test(e)){const t=parseInt(e.match(/^vec([2-4])/)[1]);return{align:2===t?8:16,size:4*t,items:t,baseType:e.match(/^vec[2-4]<(i32|u32)>$/)[1]}}if(/^mat[2-4](?:x[2-4])?(<f32>|f)$/.test(e)){if("x"===e[4]&&e[3]!==e[5])throw new Error("Non-square matrices not implemented yet");const t=parseInt(e[3]),n=2===t?8:16;return{align:n,size:Math.ceil(4*t/n)*n*t,pack:3===t?e=>[...e.slice(0,3),...e.slice(3,6),...e.slice(6,9)]:void 0,packInPlace:3===t,items:t*t,baseType:"f32"}}if(/^array<.+>$/.test(e)){const[,t,n]=e.match(/^array<(.+),\s*(\d+)>/),r=parseInt(n),{align:i,size:o,items:s,pack:a=e=>[...e],baseType:u}=l(t),p=Math.ceil(o/i)*i;return{align:i,size:p*r,items:s*r,pack:e=>{const t=[];for(let n=0;n<e.length;n+=s){const r=a(e.slice(n,s));t.push(...r);for(let e=0;e<p/4-r.length;e++)t.push(0)}return t},baseType:u}}throw new Error(`Unknown type in WGSL struct: ${e}`)};for(;null!==(o=u.exec(r));){const[e,t,n,r]=o,{size:u,align:p,pack:f,packInPlace:c,baseType:d}=l(r);a=Math.ceil(a/p)*p;const m=a+u;i[n]={name:n,location:t?parseInt(t):void 0,index:s,type:r,size:u,offset:a,offsetEnd:m,pack:f,packInPlace:c,baseType:d},s++,a=m}return i}_mapUniformData(e,t){return e.pack?e.pack(t):t}_getShaderAttributes(e){const t=/fn main\(.+:\s*([^\s\)]+)/.exec(e._vertSrc);if(!t)throw new Error("Can't find `fn main` in vertex shader source");const n=t[1];return this._parseStruct(e.vertSrc(),n)}getUniformMetadata(e){const t=[],n=/@group\((\d+)\)\s+@binding\((\d+)\)\s+var<uniform>\s+(\w+)\s*:\s*(\w+);/g;let r;for(;null!==(r=n.exec(e.vertSrc()));){const[n,i,o,s,a]=r,u=parseInt(o),l=this._parseStruct(e.vertSrc(),a);t.push({group:parseInt(i),binding:u,varName:s,structType:a,uniforms:l})}if(0===t.length)throw new Error("Expected at least one uniform struct bound to @group(0)");const i={};for(const e of t)for(const[t,n]of Object.entries(e.uniforms))i[t]={...n,group:e.group,binding:e.binding,varName:e.varName};e._uniformGroups=t;const o={},s=/@group\((\d+)\)\s*@binding\((\d+)\)\s*var\s+(\w+)\s*:\s*(texture_2d<f32>|sampler);/g,a={};for(const e of t)a[e.group+","+e.binding]=!0;for(const[t,n]of[[e.vertSrc(),GPUShaderStage.VERTEX],[e.fragSrc(),GPUShaderStage.FRAGMENT]]){let e;for(;null!==(e=s.exec(t));){const[t,r,i,s,u]=e,l=parseInt(r),p=parseInt(i);if(a[l+","+p])continue;const f=`${l},${p}`;o[f]={visibility:(o[f]?.visibility||0)|n,group:l,binding:p,name:s,type:u,isSampler:!0,noData:"sampler"===u}}for(const e of Object.values(o))if(e.type.startsWith("texture")){const t=e.name+"_sampler",n=Object.values(o).find((e=>e.name===t));if(!n)throw new Error(`Every shader texture needs an accompanying sampler. Could not find sampler ${t} for texture ${e.name}`);n.textureSource=e}}return[...Object.values(i).sort(((e,t)=>e.index-t.index)),...Object.values(o)]}getNextBindingIndex({vert:e,frag:t},n=0){const r=/@group\((\d+)\)\s*@binding\((\d+)\)\s*var(?:<uniform>)?\s+(\w+)\s*:\s*(texture_2d<f32>|sampler|uniform|\w+)/g;let i=-1;for(const[o,s]of[[e,GPUShaderStage.VERTEX],[t,GPUShaderStage.FRAGMENT]]){let e;for(;null!==(e=r.exec(o));){const[t,r,o]=e;parseInt(r)===n&&(i=Math.max(i,parseInt(o)))}}return i+1}updateUniformValue(e,t,n){t.isSampler?t.texture=n instanceof s?n:this.getTexture(n):t._mappedData=this._mapUniformData(t,t._cachedData),e.buffersDirty.add(1e3*t.group+t.binding)}_updateTexture(e,t){t.update()}bindTexture(e){}unbindTexture(e){}_unbindFramebufferTexture(e){}createTexture({width:e,height:t,format:n="rgba8unorm",usage:r}){const i=this.device.createTexture({size:[e,t],format:n,usage:r||GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT});return{gpuTexture:i,view:i.createView()}}uploadTextureFromSource({gpuTexture:e},t){this.queue.copyExternalImageToTexture({source:t},{texture:e},[t.width,t.height]),this._hasPendingDraws=!0,this.flushDraw()}uploadTextureFromData({gpuTexture:e},t,n,r){this.queue.writeTexture({texture:e},t,{bytesPerRow:4*n,rowsPerImage:r},{width:n,height:r,depthOrArrayLayers:1}),this._hasPendingDraws=!0,this.flushDraw()}setTextureParams(e){}getSampler(e){const t=`${e.minFilter}_${e.magFilter}_${e.wrapS}_${e.wrapT}`;if(this.samplers.has(t))return this.samplers.get(t);const n={[y]:"nearest",[T]:"linear",[C]:"clamp-to-edge",[b]:"repeat",[_]:"mirror-repeat"},r=this.device.createSampler({magFilter:n[e.magFilter],minFilter:n[e.minFilter],addressModeU:n[e.wrapS],addressModeV:n[e.wrapT]});return this.samplers.set(t,r),r}bindTextureToShader(e,t,n,r){}deleteTexture({gpuTexture:e}){this._postSubmitCallbacks.push((()=>e.destroy()))}_getLightShader(){return this._defaultLightShader||(this._defaultLightShader=new o(this,le,pe,{vertex:{"void beforeVertex":"() {}","Vertex getObjectInputs":"(inputs: Vertex) { return inputs; }","Vertex getWorldInputs":"(inputs: Vertex) { return inputs; }","Vertex getCameraInputs":"(inputs: Vertex) { return inputs; }","void afterVertex":"() {}"},fragment:{"void beforeFragment":"() {}","Inputs getPixelInputs":"(inputs: Inputs) { return inputs; }","vec4f combineColors":"(components: ColorComponents) {\n var rgb = vec3<f32>(0.0);\n rgb += components.diffuse * components.baseColor;\n rgb += components.ambient * components.ambientColor;\n rgb += components.specular * components.specularColor;\n rgb += components.emissive;\n return vec4<f32>(rgb, components.opacity);\n }","vec4f getFinalColor":"(color: vec4<f32>) { return color; }","void afterFragment":"() {}"}})),this._defaultLightShader}_getColorShader(){return this._defaultColorShader||(this._defaultColorShader=new o(this,re,ie,{vertex:{"void beforeVertex":"() {}","Vertex getObjectInputs":"(inputs: Vertex) { return inputs; }","Vertex getWorldInputs":"(inputs: Vertex) { return inputs; }","Vertex getCameraInputs":"(inputs: Vertex) { return inputs; }","void afterVertex":"() {}"},fragment:{"void beforeFragment":"() {}","vec4<f32> getFinalColor":"(color: vec4<f32>) { return color; }","void afterFragment":"() {}"}})),this._defaultColorShader}_getLineShader(){return this._defaultLineShader||(this._defaultLineShader=new o(this,r+se,r+ae,{vertex:{"void beforeVertex":"() {}","StrokeVertex getObjectInputs":"(inputs: StrokeVertex) { return inputs; }","StrokeVertex getWorldInputs":"(inputs: StrokeVertex) { return inputs; }","StrokeVertex getCameraInputs":"(inputs: StrokeVertex) { return inputs; }","void afterVertex":"() {}"},fragment:{"void beforeFragment":"() {}","Inputs getPixelInputs":"(inputs: Inputs) { return inputs; }","vec4<f32> getFinalColor":"(color: vec4<f32>) { return color; }","bool shouldDiscard":"(outside: bool) { return outside; };","void afterFragment":"() {}"}})),this._defaultLineShader}_getFontShader(){return this._defaultFontShader||(this._defaultFontShader=new o(this,ce,de)),this._defaultFontShader}_getBlitShader(){return this._defaultBlitShader||(this._defaultBlitShader=new o(this,he,ge)),this._defaultBlitShader}_adjustDimensions(e,t){return{adjustedWidth:e,adjustedHeight:t}}_applyClip(){const e=this.device.createCommandEncoder(),t=this.activeFramebuffer(),n=t?t.aaDepthTexture?t.aaDepthTextureView:t.depthTextureView:this.depthTextureView;if(!n)return;const r={colorAttachments:[],depthStencilAttachment:{view:n,stencilLoadOp:"clear",stencilStoreOp:"store",stencilClearValue:0,depthReadOnly:!0,stencilReadOnly:!1}};e.beginRenderPass(r).end(),this._pendingCommandEncoders.push(e.finish()),this._hasPendingDraws=!0}_unapplyClip(){}_clearClipBuffer(){this._finishActiveRenderPass();const e=this.device.createCommandEncoder(),t=this.activeFramebuffer(),n=t?t.aaDepthTexture?t.aaDepthTextureView:t.depthTextureView:this.depthTextureView;if(!n)return;const r={colorAttachments:[],depthStencilAttachment:{view:n,stencilLoadOp:"clear",stencilStoreOp:"store",stencilClearValue:1,depthReadOnly:!0,stencilReadOnly:!1}};e.beginRenderPass(r).end(),this._pendingCommandEncoders.push(e.finish()),this._hasPendingDraws=!0}_applyStencilTestIfClipping(){}uniformNameFromHookKey(e){return e.slice(0,e.indexOf(":"))}populateHooks(e,t,n){if(!t.includes("fn main"))return t;t=t.replace(/\/\/ @p5 (ifdef|ifndef) (\w+)\s+(\w+)\n((?:(?!\/\/ @p5)(?:.|\n))*)\/\/ @p5 endif/g,((t,n,r,i,o)=>{const s="ifdef"===n;return(!!e.hooks.modified.vertex[`${r} ${i}`]||!!e.hooks.modified.fragment[`${r} ${i}`])===s?o:""}));let[r,i,o]=t.split(/((?:@(?:vertex|fragment)\s*)?fn main[^{]+\{)/);"fragment"!==n&&(i.match(/\@builtin\s*\(\s*instance_index\s*\)/)||(i=i.replace(/\)\s*(->|\{)/,", @builtin(instance_index) instanceID: u32) $1")));let s="";for(const t in e.hooks.uniforms)t.endsWith(": sampler2D")||(s+=` ${t},\n`);if(s){const t=`\n// Hook Uniforms (from .modify())\nstruct HookUniforms {\n${s}}\n\n@group(0) @binding(${this.getNextBindingIndex({vert:"vertex"===n?r+(e.hooks.vertex?.declarations??"")+e.hooks.declarations:e._vertSrc,frag:"fragment"===n?r+(e.hooks.fragment?.declarations??"")+e.hooks.declarations:e._fragSrc},0)}) var<uniform> hooks: HookUniforms;\n`;r=r.replace(/(@group\(0\)\s+@binding)/,`${t}\n$1`)}if(e.hooks.varyingVariables&&e.hooks.varyingVariables.length>0){let t=this._getNextAvailableLocation(r,n),i="";for(const n of e.hooks.varyingVariables){const e=`@location(${t++}) ${n},`;i+=e+"\n"}"vertex"===n?r=r.replace(/struct\s+VertexOutput\s+\{([^}]*)\}/,((e,t)=>`struct VertexOutput {${t}\n${i}}`)):"fragment"===n&&(r=r.replace(/struct\s+FragmentInput\s+\{([^}]*)\}/,((e,t)=>`struct FragmentInput {${t}\n${i}}`)))}if(e.hooks.varyingVariables&&e.hooks.varyingVariables.length>0){let t="";for(const n of e.hooks.varyingVariables){const[e,r]=n.split(":").map((e=>e.trim()));t+=`var<private> ${e}: ${r};\n`}if(r+=t,"vertex"===n){let t="";for(const n of e.hooks.varyingVariables){const[e]=n.split(":").map((e=>e.trim()));t+=` OUTPUT_VAR.${e} = ${e};\n`}const n=o.match(/return\s+(\w+)\s*;/);if(n){const e=n[1];t=t.replace(/OUTPUT_VAR/g,e),o=o.replace(/(return\s+\w+\s*;)/g,`${t} $1`)}}else if("fragment"===n){let t="";for(const n of e.hooks.varyingVariables){const[e]=n.split(":").map((e=>e.trim()));t+=` ${e} = INPUT_VAR.${e};\n`}const n=i.match(/fn main\s*\((\w+):\s*\w+\)/);if(n){const e=n[1];t=t.replace(/INPUT_VAR/g,e),o=t+o}}}let a="",u="";e.hooks.declarations&&(a+=e.hooks.declarations+"\n"),e.hooks[n].declarations&&(a+=e.hooks[n].declarations+"\n");for(const t in e.hooks.helpers){const[n,r]=t.split(" "),[i,o,s]=/^(\([^\)]*\))((?:.|\n)*)$/.exec(e.hooks.helpers[t]);a+="void"===n?`fn ${r}${o}${s}\n`:`fn ${r}${o} -> ${n}${s}\n`}for(const t in e.hooks[n]){if("declarations"===t)continue;const[r,i]=t.split(" ");u+=`const AUGMENTED_HOOK_${i} = ${e.hooks.modified[n][t]?"true":"false"};\n`;let[o,s,l]=/^(\([^\)]*\))((?:.|\n)*)$/.exec(e.hooks[n][t]);if("fragment"!==n){let e=!!s.match(/^\(\s*\S+.*\)$/);s=s.slice(0,-1)+(e?", ":"")+"instanceID: u32)"}a+="void"===r?`fn HOOK_${i}${s}${l}\n`:`fn HOOK_${i}${s} -> ${r}${l}\n`}if("fragment"!==n){const e=e=>{let t,n=e,r=0;do{if(t=/HOOK_\w+\(/.exec(n.slice(r)),t){r+=t.index+t[0].length-1;let e=0,i=!1;for(;r<n.length&&("("===n[r]?e++:")"===n[r]?e--:n[r].match(/\S/)&&(i=!0),r++,0!==e););const o=(i?", ":"")+"instanceID";n=n.slice(0,r-1)+o+n.slice(r-1),r+=o.length}}while(t);return n};r=e(r),o=e(o)}return r+"\n"+u+a+i+o}_getNextAvailableLocation(e,t){let n=-1;const r="vertex"===t?"VertexOutput":"FragmentInput",i=e.match(new RegExp(`struct\\s+${r}\\s*\\{([^}]*)\\}`,"s"));if(i){const e=i[1].matchAll(/@location\((\d+)\)/g);for(const t of e){const e=parseInt(t[1]);e>n&&(n=e)}}return n+1}getShaderHookTypes(e,t){const n={f32:K.float1,"vec2<f32>":K.float2,"vec3<f32>":K.float3,"vec4<f32>":K.float4,vec2f:K.float2,vec3f:K.float3,vec4f:K.float4,i32:K.int1,"vec2<i32>":K.int2,"vec3<i32>":K.int3,"vec4<i32>":K.int4,bool:K.bool1,"vec2<bool>":K.bool2,"vec3<bool>":K.bool3,"vec4<bool>":K.bool4,"mat2x2<f32>":K.mat2,"mat3x3<f32>":K.mat3,"mat4x4<f32>":K.mat4,"texture_2d<f32>":K.sampler2D};let r=e._vertSrc,i=e.hooks.vertex[t];if(i||(i=e.hooks.fragment[t],r=e._fragSrc),!i)throw new Error(`Can't find hook ${t}!`);const o=t.split(/\s+/g),s=o.pop(),a=o.pop(),u=[...o],l=/\(([^\)]*)\)/.exec(i);if(!l)throw new Error(`Couldn't find function parameters in hook body:\n${i}`);const p=e=>{const t=new RegExp(`struct\\s+${e}\\s*{([^}]*)}`).exec(r);if(!t)return;const i=[];for(const e of t[1].split(",")){const t=e.trim();if(!t)continue;const r=/(?:@location\([^)]*\)\s*)?(\w+)\s*:\s*([^,\s]+)/.exec(t);if(!r)continue;const o=r[1];let s=r[2];const a=n[s]||null,u=p(s);i.push({name:o,type:{typeName:s,qualifiers:[],properties:u,dataType:a}})}return i},f=l[1].split(",").map((e=>{const t=e.trim();if(!t)return null;const r=t.split(":").map((e=>e.trim()));if(2!==r.length)return null;const i=r[0];let o=r[1];o.includes("texture_2d")&&(o="texture_2d<f32>");const s=n[o]||null;return{name:i,type:{typeName:o,qualifiers:[],properties:p(o),dataType:s}}})).filter(Boolean),c=n[a]||null;return{name:s,returnType:{typeName:a,qualifiers:u,properties:p(a),dataType:c},parameters:f}}_ensurePixelReadBuffer(e){if(!this.pixelReadBuffer||this.pixelReadBufferSize<e){this.pixelReadBuffer&&(this.flushDraw(),this.pixelReadBuffer.destroy());const t=Math.max(e,2*this.pixelReadBufferSize);this.pixelReadBuffer=this.device.createBuffer({size:t,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ}),this.pixelReadBufferSize=t}return this.pixelReadBuffer}_alignBytesPerRow(e){return 256*Math.ceil(e/256)}defaultFramebufferAlpha(){return!0}defaultFramebufferAntialias(){return!0}supportsFramebufferAntialias(){return!0}createFramebufferResources(e){}validateFramebufferFormats(e){[O,S,F].includes(e.format)||(console.warn("Unknown Framebuffer format. Please use UNSIGNED_BYTE, FLOAT, or HALF_FLOAT. Defaulting to UNSIGNED_BYTE."),e.format=O),e.useDepth&&![w,S].includes(e.depthFormat)&&(console.warn("Unknown Framebuffer depth format. Please use UNSIGNED_INT or FLOAT. Defaulting to FLOAT."),e.depthFormat=S)}recreateFramebufferTextures(e){if(this.flushDraw(),e.colorTexture&&e.colorTexture.destroy){const t=e.colorTexture;this._postSubmitCallbacks.push((()=>t.destroy())),e.colorTextureView=null}if(e.aaColorTexture&&e.aaColorTexture.destroy){const t=e.aaColorTexture;this._postSubmitCallbacks.push((()=>t.destroy())),e.aaColorTextureView=null}if(e.depthTexture&&e.depthTexture.destroy){const t=e.depthTexture;this._postSubmitCallbacks.push((()=>t.destroy())),e.depthTextureView=null}if(e.aaDepthTexture&&e.aaDepthTexture.destroy){const t=e.aaDepthTexture;this._postSubmitCallbacks.push((()=>t.destroy())),e.aaDepthTextureView=null}const t={size:{width:e.width*e.density,height:e.height*e.density,depthOrArrayLayers:1},format:this._getWebGPUColorFormat(e)},n={...t,usage:GPUTextureUsage.RENDER_ATTACHMENT|GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_SRC|(e._useCanvasFormat?GPUTextureUsage.COPY_DST:0),sampleCount:1};if(e.colorTexture=this.device.createTexture(n),e.colorTextureView=e.colorTexture.createView(),e.antialias){const n={...t,usage:GPUTextureUsage.RENDER_ATTACHMENT,sampleCount:this._getValidSampleCount(e.antialiasSamples)};e.aaColorTexture=this.device.createTexture(n),e.aaColorTextureView=e.aaColorTexture.createView()}if(e.useDepth){const t={size:{width:e.width*e.density,height:e.height*e.density,depthOrArrayLayers:1},format:this._getWebGPUDepthFormat(e)},n={...t,usage:GPUTextureUsage.RENDER_ATTACHMENT|GPUTextureUsage.TEXTURE_BINDING|(e._useCanvasFormat?GPUTextureUsage.COPY_DST:0),sampleCount:1};if(e.depthTexture=this.device.createTexture(n),e.depthTextureView=e.depthTexture.createView(),e.antialias){const n={...t,usage:GPUTextureUsage.RENDER_ATTACHMENT,sampleCount:this._getValidSampleCount(e.antialiasSamples)};e.aaDepthTexture=this.device.createTexture(n),e.aaDepthTextureView=e.aaDepthTexture.createView()}}this._clearFramebufferTextures(e)}_clearFramebufferTextures(e){this._finishActiveRenderPass();const t=this.device.createCommandEncoder(),n={colorAttachments:[{view:e.aaColorTexture?e.aaColorTextureView:e.colorTextureView,loadOp:"clear",storeOp:"store",clearValue:{r:0,g:0,b:0,a:0},resolveTarget:e.aaColorTexture?e.colorTextureView:void 0}],depthStencilAttachment:e.aaDepthTexture||e.depthTexture?{view:e.aaDepthTexture?e.aaDepthTextureView:e.depthTextureView,depthLoadOp:"clear",depthStoreOp:"store",depthClearValue:1,stencilLoadOp:"clear",stencilStoreOp:"store",depthReadOnly:!1,stencilReadOnly:!1}:void 0};t.beginRenderPass(n).end(),this._pendingCommandEncoders.push(t.finish()),this._hasPendingDraws=!0}_getFramebufferColorTextureView(e){return e.colorTexture?e.colorTextureView:null}createFramebufferTextureHandle(e){const t=e;let n=this;return{get view(){return n._getFramebufferColorTextureView(t.framebuffer)},get gpuTexture(){return t.framebuffer.colorTexture}}}_getWebGPUColorFormat(e){return e.format===S?(e.channels,"rgba32float"):e.format===F?(e.channels,"rgba16float"):e._useCanvasFormat?this.presentationFormat:(e.channels,"rgba8unorm")}_getWebGPUDepthFormat(e){return e._useCanvasFormat?this.depthFormat:e.useStencil?e.depthFormat===S?"depth32float-stencil8":"depth24plus-stencil8":e.depthFormat===S?"depth32float":"depth24plus"}_deleteFramebufferTexture(e){this.flushDraw();const t=e.rawTexture();if(t.texture&&t.texture.destroy){const e=t.texture;this._postSubmitCallbacks.push((()=>e.destroy()))}this.textures.delete(e)}deleteFramebufferTextures(e){this._deleteFramebufferTexture(e.color),e.depth&&this._deleteFramebufferTexture(e.depth)}deleteFramebufferResources(e){if(this.flushDraw(),e.colorTexture&&e.colorTexture.destroy){const t=e.colorTexture;this._postSubmitCallbacks.push((()=>t.destroy()))}if(e.depthTexture&&e.depthTexture.destroy){const t=e.depthTexture;this._postSubmitCallbacks.push((()=>t.destroy()))}if(e.aaDepthTexture&&e.aaDepthTexture.destroy){const t=e.aaDepthTexture;this._postSubmitCallbacks.push((()=>t.destroy()))}}getFramebufferToBind(e){}updateFramebufferTexture(e,t){}bindFramebuffer(e){}framebufferYScale(){return 1}async readFramebufferPixels(e){this.flushDraw();const t=e.width*e.density,n=e.height*e.density,r=4*t,i=this._alignBytesPerRow(r),o=i*n,s=this.device.createBuffer({size:o,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ}),a=this.device.createCommandEncoder();a.copyTextureToBuffer({texture:e.colorTexture,origin:{x:0,y:0,z:0},mipLevel:0,aspect:"all"},{buffer:s,bytesPerRow:i,rowsPerImage:n},{width:t,height:n,depthOrArrayLayers:1}),this.device.queue.submit([a.finish()]),await s.mapAsync(GPUMapMode.READ,0,o);const u=s.getMappedRange(0,o);let l;if(i===r)l=new Uint8Array(u.slice(0,t*n*4)),s.unmap();else{l=new Uint8Array(t*n*4);const e=new Uint8Array(u);for(let t=0;t<n;t++){const n=t*i,o=t*r;l.set(e.subarray(n,n+r),o)}s.unmap()}return this._ensurePixelsAreRGBA(e,l),l}async readFramebufferPixel(e,t,n){this.flushDraw();const r=this._alignBytesPerRow(4),i=r,o=this._ensurePixelReadBuffer(i),s=this.device.createCommandEncoder();s.copyTextureToBuffer({texture:e.colorTexture,origin:{x:t,y:n,z:0}},{buffer:o,bytesPerRow:r},{width:1,height:1,depthOrArrayLayers:1}),this.device.queue.submit([s.finish()]),await o.mapAsync(GPUMapMode.READ,0,i);const a=o.getMappedRange(0,i),u=new Uint8Array(a),l=[u[0],u[1],u[2],u[3]];return this._ensurePixelsAreRGBA(e,l),o.unmap(),l}async readFramebufferRegion(e,t,n,r,i){this.flushDraw();const o=r*e.density,s=i*e.density,a=4*o,l=this._alignBytesPerRow(a),p=l*s,f=this._ensurePixelReadBuffer(p),c=this.device.createCommandEncoder();c.copyTextureToBuffer({texture:e.colorTexture,mipLevel:0,origin:{x:t*e.density,y:n*e.density,z:0}},{buffer:f,bytesPerRow:l},{width:o,height:s,depthOrArrayLayers:1}),this.device.queue.submit([c.finish()]),await f.mapAsync(GPUMapMode.READ,0,p);const d=f.getMappedRange(0,p);let m;if(l===a)m=new Uint8Array(d.slice(0,o*s*4));else{m=new Uint8Array(o*s*4);const e=new Uint8Array(d);for(let t=0;t<s;t++){const n=t*l,r=t*a;m.set(e.subarray(n,n+a),r)}}this._ensurePixelsAreRGBA(e,m);const h=new u(o,s);return h.imageData=h.canvas.getContext("2d").createImageData(o,s),h.imageData.data.set(m),h.pixels=h.imageData.data,h.updatePixels(),1!==e.density&&h.pixelDensity(e.density),f.unmap(),h}updateFramebufferPixels(e){const t=e.width*e.density,n=e.height*e.density,r=t*n*4;if(!e.pixels||e.pixels.length!==r)throw new Error("The pixels array has not been set correctly. Please call loadPixels() before updatePixels().");this.device.queue.writeTexture({texture:e.colorTexture},e.pixels,{bytesPerRow:4*t,rowsPerImage:n},{width:t,height:n,depthOrArrayLayers:1})}_ensurePixelsAreRGBA(e,t){e._useCanvasFormat&&"bgra8unorm"===this.presentationFormat&&this._convertBGRtoRGB(t)}_convertBGRtoRGB(e){for(let t=0;t<e.length;t+=4){const n=e[t];e[t]=e[t+2],e[t+2]=n}}async loadPixels(){this._promoteToFramebuffer(),await this.mainFramebuffer.loadPixels(),this.pixels=this.mainFramebuffer.pixels.slice()}async get(e,t,n,r){return this._promoteToFramebuffer(),this.mainFramebuffer.get(e,t,n,r)}filter(...e){return this.activeFramebuffer()||this._promoteToFramebuffer(),super.filter(...e)}getNoiseShaderSnippet(){return"fn mod289Vec3(x: vec3<f32>) -> vec3<f32> {\n return x - floor(x * (1.0 / 289.0)) * 289.0;\n}\n\nfn mod289Vec4(x: vec4<f32>) -> vec4<f32> {\n return x - floor(x * (1.0 / 289.0)) * 289.0;\n}\n\nfn permute(x: vec4<f32>) -> vec4<f32> {\n return mod289Vec4(((x*34.0)+10.0)*x);\n}\n\nfn taylorInvSqrt(r: vec4<f32>) -> vec4<f32> {\n return vec4<f32>(1.79284291400159) - vec4<f32>(0.85373472095314) * r;\n}\n\nfn baseNoise(v: vec3<f32>) -> f32 {\n let C = vec2<f32>(1.0/6.0, 1.0/3.0);\n let D = vec4<f32>(0.0, 0.5, 1.0, 2.0);\n\n // First corner\n var i = floor(v + dot(v, C.yyy));\n let x0 = v - i + dot(i, C.xxx);\n\n // Other corners\n let g = step(x0.yzx, x0.xyz);\n let l = vec3<f32>(1.0) - g;\n let i1 = min(g.xyz, l.zxy);\n let i2 = max(g.xyz, l.zxy);\n\n // x0 = x0 - 0.0 + 0.0 * C.xxx;\n // x1 = x0 - i1 + 1.0 * C.xxx;\n // x2 = x0 - i2 + 2.0 * C.xxx;\n // x3 = x0 - 1.0 + 3.0 * C.xxx;\n let x1 = x0 - i1 + C.xxx;\n let x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y\n let x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y\n\n // Permutations\n i = mod289Vec3(i);\n let p = permute( permute( permute(\n i.z + vec4<f32>(0.0, i1.z, i2.z, 1.0 ))\n + i.y + vec4<f32>(0.0, i1.y, i2.y, 1.0 ))\n + i.x + vec4<f32>(0.0, i1.x, i2.x, 1.0 ));\n\n // Gradients: 7x7 points over a square, mapped onto an octahedron.\n // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)\n let n_ = 0.142857142857; // 1.0/7.0\n let ns = n_ * D.wyz - D.xzx;\n\n let j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)\n\n let x_ = floor(j * ns.z);\n let y_ = floor(j - 7.0 * x_ ); // mod(j,N)\n\n let x = x_ *ns.x + ns.yyyy;\n let y = y_ *ns.x + ns.yyyy;\n let h = vec4<f32>(1.0) - abs(x) - abs(y);\n\n let b0 = vec4<f32>( x.xy, y.xy );\n let b1 = vec4<f32>( x.zw, y.zw );\n\n //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;\n //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;\n let s0 = floor(b0)*2.0 + vec4<f32>(1.0);\n let s1 = floor(b1)*2.0 + vec4<f32>(1.0);\n let sh = -step(h, vec4<f32>(0.0));\n\n let a0 = b0.xzyw + s0.xzyw*sh.xxyy;\n let a1 = b1.xzyw + s1.xzyw*sh.zzww;\n\n let p0 = vec3<f32>(a0.xy, h.x);\n let p1 = vec3<f32>(a0.zw, h.y);\n let p2 = vec3<f32>(a1.xy, h.z);\n let p3 = vec3<f32>(a1.zw, h.w);\n\n //Normalise gradients\n let norm = taylorInvSqrt(vec4<f32>(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));\n let p0_norm = p0 * norm.x;\n let p1_norm = p1 * norm.y;\n let p2_norm = p2 * norm.z;\n let p3_norm = p3 * norm.w;\n\n // Mix final noise value\n var m = max(vec4<f32>(0.5) - vec4<f32>(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), vec4<f32>(0.0));\n m = m * m;\n return 105.0 * dot( m*m, vec4<f32>( dot(p0_norm,x0), dot(p1_norm,x1),\n dot(p2_norm,x2), dot(p3_norm,x3) ) );\n}\n\nfn noise(st: vec3<f32>, octaves: i32, ampFalloff: f32) -> f32 {\n var result = 0.0;\n var amplitude = 1.0;\n var frequency = 1.0;\n\n for (var i = 0; i < 8; i++) {\n if (i >= octaves) { break; }\n result += amplitude * baseNoise(st * frequency);\n frequency *= 2.0;\n amplitude *= ampFalloff;\n }\n return (result + 1.0) * 0.5;\n}"}baseFilterShader(){return this._baseFilterShader||(this._baseFilterShader=new o(this,Be,ke,{vertex:{},fragment:{"vec4<f32> getColor":"(inputs: FilterInputs, canvasContent: texture_2d<f32>, canvasContent_sampler: sampler) -> vec4<f32> {\n return textureSample(tex, tex_sampler, inputs.texCoord);\n }"},hookAliases:{getColor:["filterColor"]}})),this._baseFilterShader}_createImageLightShader(e){if("diffused"===e)return this._pInst.createShader($e,je);if("specular"===e)return this._pInst.createShader($e,He);throw new Error(`Unknown imageLight shader type: ${e}`)}_createMipmapTexture(e){return new a(this,e,{})}_prepareMipmapData(e,t){const n={size:{width:e,height:e,depthOrArrayLayers:1},mipLevelCount:t,format:"rgba8unorm",usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT};return{gpuTexture:this.device.createTexture(n),size:e,mipLevels:t,format:"rgba8unorm"}}_accumulateMipLevel(e,t,n,r,i){this.flushDraw();const o=this.device.createCommandEncoder(),s=e.color.rawTexture().texture;o.copyTextureToTexture({texture:s,origin:{x:0,y:0,z:0}},{texture:t.gpuTexture,mipLevel:n,origin:{x:0,y:0,z:0}},{width:r,height:i,depthOrArrayLayers:1}),this.device.queue.submit([o.finish()])}_finalizeMipmapTexture(e){return new a(this,e,{})}createMipmapTextureHandle({gpuTexture:e,format:t,dataType:n,width:r,height:i}){return{texture:e,view:e.createView(),glFormat:t||"rgba8unorm",glDataType:n||"uint8"}}}e.RendererWebGPU=L,e.renderers[t]=e.RendererWebGPU,n.setAttributes=async function(e,t){return this._renderer._setAttributes(e,t)}}"undefined"!=typeof p5&&Ye(p5,p5.prototype)}();
|
|
1
|
+
!function(){"use strict";const e=Math.PI,t="webgpu",n=e/2,r=e,i=e/4,o=2*e,s=2*e,a=e/180,u=180/e,l="source-over",p="destination-out",f="lighter",c="darken",d="lighten",m="subtract",h="exclusion",g="multiply",x="screen",v="copy",T="linear",y="nearest",b="repeat",C="clamp",_="mirror",O="unsigned-byte",w="unsigned-int",S="float",F="half-float",A=Symbol("include"),I=Symbol("exclude"),L=Symbol("join");var P=Object.freeze({__proto__:null,ADD:f,ALT:"Alt",ARROW:"default",AUTO:"auto",AXES:"axes",BACKSPACE:"Backspace",BASELINE:"alphabetic",BEVEL:"bevel",BEZIER:"bezier",BLEND:l,BLUR:"blur",BOLD:"bold",BOLDITALIC:"bold italic",BOTTOM:"bottom",BURN:"color-burn",CENTER:"center",CHAR:"CHAR",CHORD:"chord",CLAMP:C,CLOSE:"close",CONTAIN:"contain",CONTROL:"Control",CORNER:"corner",CORNERS:"corners",COVER:"cover",CROSS:"crosshair",CURVE:"curve",DARKEST:c,DEG_TO_RAD:a,DELETE:"Delete",DIFFERENCE:"difference",DILATE:"dilate",DODGE:"color-dodge",DOWN_ARROW:"ArrowDown",EMPTY_PATH:7,ENTER:"Enter",ERODE:"erode",ESCAPE:"Escape",EXCLUDE:I,EXCLUSION:h,FALLBACK:"fallback",FILL:"fill",FLAT:"flat",FLOAT:S,FULL:"full",GRAY:"gray",GRID:"grid",HALF_FLOAT:F,HALF_PI:n,HAND:"pointer",HARD_LIGHT:"hard-light",IMAGE:"image",IMMEDIATE:"immediate",INCLUDE:A,INVERT:"invert",ITALIC:"italic",JOIN:L,LABEL:"label",LANDSCAPE:"landscape",LEFT:"left",LEFT_ARROW:"ArrowLeft",LIGHTEST:d,LINEAR:T,LINEAR_MIPMAP:"linear_mipmap",LINES:1,LINE_LOOP:2,LINE_STRIP:3,MIRROR:_,MITER:"miter",MOVE:"move",MULTIPLY:g,NEAREST:y,NORMAL:"normal",OPAQUE:"opaque",OPEN:"open",OPTION:"Alt",OVERLAY:"overlay",P2D:"p2d",P2DHDR:"p2d-hdr",PATH:8,PI:r,PIE:"pie",POINTS:0,PORTRAIT:"portrait",POSTERIZE:"posterize",PROJECT:"square",QUADRATIC:"quadratic",QUADS:"quads",QUAD_STRIP:"quad_strip",QUARTER_PI:i,RADIUS:"radius",RAD_TO_DEG:u,REMOVE:p,REPEAT:b,REPLACE:v,RETURN:"Enter",RIGHT:"right",RIGHT_ARROW:"ArrowRight",ROUND:"round",SCREEN:x,SHIFT:"Shift",SIMPLE:"simple",SMOOTH:"smooth",SOFT_LIGHT:"soft-light",SQUARE:"butt",STROKE:"stroke",SUBTRACT:m,TAB:"Tab",TAU:o,TESS:"tess",TEXT:"text",TEXTURE:"texture",THRESHOLD:"threshold",TOP:"top",TRIANGLES:4,TRIANGLE_FAN:6,TRIANGLE_STRIP:5,TWO_PI:s,UNSIGNED_BYTE:O,UNSIGNED_INT:w,UP_ARROW:"ArrowUp",VERSION:"2.2.2",WAIT:"wait",WEBGL:"webgl",WEBGL2:"webgl2",WEBGPU:t,WORD:"WORD",_CTX_MIDDLE:"middle",_DEFAULT_FILL:"#FFFFFF",_DEFAULT_LEADMULT:1.25,_DEFAULT_STROKE:"#000000",_DEFAULT_TEXT_FILL:"#000000"});const E={OPERATION:"operation",LITERAL:"literal",VARIABLE:"variable",CONSTANT:"constant",STRUCT:"struct",PHI:"phi",STATEMENT:"statement",ASSIGNMENT:"assignment"},D=Object.fromEntries(Object.entries(E).map((([e,t])=>[t,e]))),R={[E.OPERATION]:["opCode","dependsOn","dimension","baseType"],[E.LITERAL]:["value","dimension","baseType"],[E.VARIABLE]:["identifier","dimension","baseType"],[E.CONSTANT]:["value","dimension","baseType"],[E.STRUCT]:[""],[E.PHI]:["dependsOn","phiBlocks","dimension","baseType"],[E.STATEMENT]:["statementType"],[E.ASSIGNMENT]:["dependsOn"]},V="discard",N="break",M="early_return",U="expression",B="empty",k="float",z="int",G="bool",$="mat",j="defer",H="sampler2D",W="sampler",q={[k]:3,[z]:2,[G]:1,[$]:0,[j]:-1,[H]:-10,[W]:-11},K={float1:{fnName:"float",baseType:k,dimension:1,priority:3},float2:{fnName:"vec2",baseType:k,dimension:2,priority:3},float3:{fnName:"vec3",baseType:k,dimension:3,priority:3},float4:{fnName:"vec4",baseType:k,dimension:4,priority:3},int1:{fnName:"int",baseType:z,dimension:1,priority:2},int2:{fnName:"ivec2",baseType:z,dimension:2,priority:2},int3:{fnName:"ivec3",baseType:z,dimension:3,priority:2},int4:{fnName:"ivec4",baseType:z,dimension:4,priority:2},bool1:{fnName:"bool",baseType:G,dimension:1,priority:1},bool2:{fnName:"bvec2",baseType:G,dimension:2,priority:1},bool3:{fnName:"bvec3",baseType:G,dimension:3,priority:1},bool4:{fnName:"bvec4",baseType:G,dimension:4,priority:1},mat2:{fnName:"mat2x2",baseType:$,dimension:2,priority:0},mat3:{fnName:"mat3x3",baseType:$,dimension:3,priority:0},mat4:{fnName:"mat4x4",baseType:$,dimension:4,priority:0},defer:{fnName:null,baseType:j,dimension:null,priority:-1},sampler2D:{fnName:"sampler2D",baseType:H,dimension:1,priority:-10},sampler:{fnName:"sampler",baseType:W,dimension:1,priority:-11}};const Y={FLOAT:{baseType:k,dimension:null,priority:3},INT:{baseType:z,dimension:null,priority:2},BOOL:{baseType:G,dimension:null,priority:1}};Object.fromEntries(Object.values(K).filter((e=>null!==e.fnName)).map((e=>[e.fnName,e])));const X={ADD:0,SUBTRACT:1,MULTIPLY:2,DIVIDE:3,MODULO:4,EQUAL:5,NOT_EQUAL:6,GREATER_THAN:7,GREATER_EQUAL:8,LESS_THAN:9,LESS_EQUAL:10,LOGICAL_AND:11,LOGICAL_OR:12,MEMBER_ACCESS:13},Q={LOGICAL_NOT:100,NEGATE:101,PLUS:102,SWIZZLE:103},J={FUNCTION_CALL:200,CONSTRUCTOR:201},Z=[{arity:"unary",name:"not",symbol:"!",opCode:Q.LOGICAL_NOT},{arity:"unary",name:"neg",symbol:"-",opCode:Q.NEGATE},{arity:"unary",name:"plus",symbol:"+",opCode:Q.PLUS},{arity:"binary",name:"add",symbol:"+",opCode:X.ADD},{arity:"binary",name:"sub",symbol:"-",opCode:X.SUBTRACT},{arity:"binary",name:"mult",symbol:"*",opCode:X.MULTIPLY},{arity:"binary",name:"div",symbol:"/",opCode:X.DIVIDE},{arity:"binary",name:"mod",symbol:"%",opCode:X.MODULO},{arity:"binary",name:"equalTo",symbol:"==",opCode:X.EQUAL},{arity:"binary",name:"notEqual",symbol:"!=",opCode:X.NOT_EQUAL},{arity:"binary",name:"greaterThan",symbol:">",opCode:X.GREATER_THAN},{arity:"binary",name:"greaterEqual",symbol:">=",opCode:X.GREATER_EQUAL},{arity:"binary",name:"lessThan",symbol:"<",opCode:X.LESS_THAN},{arity:"binary",name:"lessEqual",symbol:"<=",opCode:X.LESS_EQUAL},{arity:"binary",name:"and",symbol:"&&",opCode:X.LOGICAL_AND},{arity:"binary",name:"or",symbol:"||",opCode:X.LOGICAL_OR}],ee={};for(const{symbol:e,opCode:t,name:n,arity:r}of Z)ee[t]=e;const te={GLOBAL:"global",FUNCTION:"function",BRANCH:"branch",IF_COND:"if_cond",IF_BODY:"if_body",ELSE_COND:"else_cond",SCOPE_START:"scope_start",SCOPE_END:"scope_end",FOR:"for",MERGE:"merge",DEFAULT:"default"};Object.fromEntries(Object.entries(te).map((([e,t])=>[t,e])));const ne="\n// Group 0: Material Properties\nstruct MaterialUniforms {\n uUseVertexColor: u32,\n}\n\n// Group 1: Model Transform\nstruct ModelUniforms {\n// @p5 ifdef Vertex getWorldInputs\n uModelMatrix: mat4x4<f32>,\n uModelNormalMatrix: mat3x3<f32>,\n// @p5 endif\n// @p5 ifndef Vertex getWorldInputs\n uModelViewMatrix: mat4x4<f32>,\n uNormalMatrix: mat3x3<f32>,\n// @p5 endif\n uMaterialColor: vec4<f32>,\n}\n\n// Group 2: Camera and Projection\nstruct CameraUniforms {\n uProjectionMatrix: mat4x4<f32>,\n// @p5 ifdef Vertex getWorldInputs\n uViewMatrix: mat4x4<f32>,\n// @p5 endif\n uCameraNormalMatrix: mat3x3<f32>,\n}\n",re=`\nstruct VertexInput {\n @location(0) aPosition: vec3<f32>,\n @location(1) aNormal: vec3<f32>,\n @location(2) aTexCoord: vec2<f32>,\n @location(3) aVertexColor: vec4<f32>,\n};\n\nstruct VertexOutput {\n @builtin(position) Position: vec4<f32>,\n @location(0) vVertexNormal: vec3<f32>,\n @location(1) vVertTexCoord: vec2<f32>,\n @location(2) vColor: vec4<f32>,\n};\n\n${ne}\n@group(0) @binding(0) var<uniform> material: MaterialUniforms;\n@group(1) @binding(0) var<uniform> model: ModelUniforms;\n@group(2) @binding(0) var<uniform> camera: CameraUniforms;\n\nstruct Vertex {\n position: vec3<f32>,\n normal: vec3<f32>,\n texCoord: vec2<f32>,\n color: vec4<f32>,\n}\n\n@vertex\nfn main(input: VertexInput) -> VertexOutput {\n HOOK_beforeVertex();\n var output: VertexOutput;\n\n let useVertexColor = (material.uUseVertexColor != 0 && input.aVertexColor.x >= 0.0);\n var inputs = Vertex(\n input.aPosition,\n input.aNormal,\n input.aTexCoord,\n select(model.uMaterialColor, input.aVertexColor, useVertexColor)\n );\n\n// @p5 ifdef Vertex getObjectInputs\n inputs = HOOK_getObjectInputs(inputs);\n// @p5 endif\n\n// @p5 ifdef Vertex getWorldInputs\n inputs.position = (model.uModelMatrix * vec4<f32>(inputs.position, 1.0)).xyz;\n inputs.normal = model.uModelNormalMatrix * inputs.normal;\n inputs = HOOK_getWorldInputs(inputs);\n// @p5 endif\n\n// @p5 ifdef Vertex getWorldInputs\n // Already multiplied by the model matrix, just apply view\n inputs.position = (camera.uViewMatrix * vec4<f32>(inputs.position, 1.0)).xyz;\n inputs.normal = camera.uCameraNormalMatrix * inputs.normal;\n// @p5 endif\n// @p5 ifndef Vertex getWorldInputs\n // Apply both at once\n inputs.position = (model.uModelViewMatrix * vec4<f32>(inputs.position, 1.0)).xyz;\n inputs.normal = model.uNormalMatrix * inputs.normal;\n// @p5 endif\n\n// @p5 ifdef Vertex getCameraInputs\n inputs = HOOK_getCameraInputs(inputs);\n// @p5 endif\n\n output.vVertTexCoord = inputs.texCoord;\n output.vVertexNormal = normalize(inputs.normal);\n output.vColor = inputs.color;\n\n output.Position = camera.uProjectionMatrix * vec4<f32>(inputs.position, 1.0);\n\n HOOK_afterVertex();\n return output;\n}\n`,ie=`\nstruct FragmentInput {\n @location(0) vVertexNormal: vec3<f32>,\n @location(1) vVertTexCoord: vec2<f32>,\n @location(2) vColor: vec4<f32>,\n};\n\n${ne}\n@group(0) @binding(0) var<uniform> material: MaterialUniforms;\n@group(1) @binding(0) var<uniform> model: ModelUniforms;\n@group(2) @binding(0) var<uniform> camera: CameraUniforms;\n\n\n@fragment\nfn main(input: FragmentInput) -> @location(0) vec4<f32> {\n HOOK_beforeFragment();\n var outColor = HOOK_getFinalColor(input.vColor);\n outColor = vec4<f32>(outColor.rgb * outColor.a, outColor.a);\n HOOK_afterFragment();\n return outColor;\n}\n`,oe="\n// Group 0: Stroke Properties\nstruct StrokeUniforms {\n uStrokeWeight: f32,\n uUseLineColor: f32,\n uSimpleLines: f32,\n uStrokeCap: u32,\n uStrokeJoin: u32,\n}\n\n// Group 1: Model Transform\nstruct ModelUniforms {\n// @p5 ifdef StrokeVertex getWorldInputs\n uModelMatrix: mat4x4<f32>,\n uViewMatrix: mat4x4<f32>,\n// @p5 endif\n// @p5 ifndef StrokeVertex getWorldInputs\n uModelViewMatrix: mat4x4<f32>,\n// @p5 endif\n uMaterialColor: vec4<f32>,\n}\n\n// Group 2: Camera and Projection\nstruct CameraUniforms {\n uProjectionMatrix: mat4x4<f32>,\n uViewport: vec4<f32>,\n uPerspective: u32,\n}\n",se=`\nstruct StrokeVertexInput {\n @location(0) aPosition: vec3<f32>,\n @location(1) aSide: f32,\n @location(2) aTangentIn: vec3<f32>,\n @location(3) aTangentOut: vec3<f32>,\n @location(4) aVertexColor: vec4<f32>,\n};\n\nstruct StrokeVertexOutput {\n @builtin(position) Position: vec4<f32>,\n @location(0) vColor: vec4<f32>,\n @location(1) vTangent: vec2<f32>,\n @location(2) vCenter: vec2<f32>,\n @location(3) vPosition: vec2<f32>,\n @location(4) vMaxDist: f32,\n @location(5) vCap: f32,\n @location(6) vJoin: f32,\n @location(7) vStrokeWeight: f32,\n};\n\n${oe}\n@group(0) @binding(0) var<uniform> stroke: StrokeUniforms;\n@group(1) @binding(0) var<uniform> model: ModelUniforms;\n@group(2) @binding(0) var<uniform> camera: CameraUniforms;\n\nstruct StrokeVertex {\n position: vec3<f32>,\n tangentIn: vec3<f32>,\n tangentOut: vec3<f32>,\n color: vec4<f32>,\n weight: f32,\n}\n\nfn lineIntersection(aPoint: vec2f, aDir: vec2f, bPoint: vec2f, bDir: vec2f) -> vec2f {\n // Rotate and translate so a starts at the origin and goes out to the right\n var bMutPoint = bPoint;\n bMutPoint -= aPoint;\n var rotatedBFrom = vec2<f32>(\n bMutPoint.x*aDir.x + bMutPoint.y*aDir.y,\n bMutPoint.y*aDir.x - bMutPoint.x*aDir.y\n );\n var bTo = bMutPoint + bDir;\n var rotatedBTo = vec2<f32>(\n bTo.x*aDir.x + bTo.y*aDir.y,\n bTo.y*aDir.x - bTo.x*aDir.y\n );\n var intersectionDistance =\n rotatedBTo.x + (rotatedBFrom.x - rotatedBTo.x) * rotatedBTo.y /\n (rotatedBTo.y - rotatedBFrom.y);\n return aPoint + aDir * intersectionDistance;\n}\n\n@vertex\nfn main(input: StrokeVertexInput) -> StrokeVertexOutput {\n HOOK_beforeVertex();\n var output: StrokeVertexOutput;\n let simpleLines = (stroke.uSimpleLines != 0.);\n if (!simpleLines) {\n if (all(input.aTangentIn == vec3<f32>()) != all(input.aTangentOut == vec3<f32>())) {\n output.vCap = 1.;\n } else {\n output.vCap = 0.;\n }\n let conditionA = any(input.aTangentIn != vec3<f32>());\n let conditionB = any(input.aTangentOut != vec3<f32>());\n let conditionC = any(input.aTangentIn != input.aTangentOut);\n if (conditionA && conditionB && conditionC) {\n output.vJoin = 1.;\n } else {\n output.vJoin = 0.;\n }\n }\n var lineColor: vec4<f32>;\n if (stroke.uUseLineColor != 0.) {\n lineColor = input.aVertexColor;\n } else {\n lineColor = model.uMaterialColor;\n }\n var inputs = StrokeVertex(\n input.aPosition.xyz,\n input.aTangentIn,\n input.aTangentOut,\n lineColor,\n stroke.uStrokeWeight\n );\n\n// @p5 ifdef StrokeVertex getObjectInputs\n inputs = HOOK_getObjectInputs(inputs);\n// @p5 endif\n\n// @p5 ifdef StrokeVertex getWorldInputs\n inputs.position = (model.uModelMatrix * vec4<f32>(inputs.position, 1.)).xyz;\n inputs.tangentIn = (model.uModelMatrix * vec4<f32>(input.aTangentIn, 1.)).xyz;\n inputs.tangentOut = (model.uModelMatrix * vec4<f32>(input.aTangentOut, 1.)).xyz;\n inputs = HOOK_getWorldInputs(inputs);\n// @p5 endif\n\n// @p5 ifdef StrokeVertex getWorldInputs\n // Already multiplied by the model matrix, just apply view\n inputs.position = (model.uViewMatrix * vec4<f32>(inputs.position, 1.)).xyz;\n inputs.tangentIn = (model.uViewMatrix * vec4<f32>(input.aTangentIn, 0.)).xyz;\n inputs.tangentOut = (model.uViewMatrix * vec4<f32>(input.aTangentOut, 0.)).xyz;\n// @p5 endif\n// @p5 ifndef StrokeVertex getWorldInputs\n // Apply both at once\n inputs.position = (model.uModelViewMatrix * vec4<f32>(inputs.position, 1.)).xyz;\n inputs.tangentIn = (model.uModelViewMatrix * vec4<f32>(input.aTangentIn, 0.)).xyz;\n inputs.tangentOut = (model.uModelViewMatrix * vec4<f32>(input.aTangentOut, 0.)).xyz;\n// @p5 endif\n// @p5 ifdef StrokeVertex getCameraInputs\n inputs = HOOK_getCameraInputs(inputs);\n// @p5 endif\n\n var posp = vec4<f32>(inputs.position, 1.);\n var posqIn = vec4<f32>(inputs.position + inputs.tangentIn, 1.);\n var posqOut = vec4<f32>(inputs.position + inputs.tangentOut, 1.);\n output.vStrokeWeight = inputs.weight;\n\n var facingCamera = pow(\n // The word space tangent's z value is 0 if it's facing the camera\n abs(normalize(posqIn-posp).z),\n\n // Using pow() here to ramp 'facingCamera' up from 0 to 1 really quickly\n // so most lines get scaled and don't get clipped\n 0.25\n );\n\n // Moving vertices slightly toward the camera\n // to avoid depth-fighting with the fill triangles.\n // A mix of scaling and offsetting is used based on distance\n // Discussion here:\n // https://github.com/processing/p5.js/issues/7200\n\n // using a scale <1 moves the lines towards nearby camera\n // in order to prevent popping effects due to half of\n // the line disappearing behind the geometry faces.\n var zDistance = -posp.z;\n var distanceFactor = smoothstep(0., 800., zDistance);\n\n // Discussed here:\n // http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=252848\n var scale = mix(1., 0.995, facingCamera);\n var dynamicScale = mix(scale, 1.0, distanceFactor); // Closer = more scale, farther = less\n\n posp = vec4<f32>(posp.xyz * dynamicScale, posp.w);\n posqIn = vec4<f32>(posqIn.xyz * dynamicScale, posqIn.w);\n posqOut= vec4<f32>(posqOut.xyz * dynamicScale, posqOut.w);\n\n // Moving vertices slightly toward camera when far away\n // https://github.com/processing/p5.js/issues/6956\n var zOffset = mix(0., -1., facingCamera);\n var dynamicZAdjustment = mix(0., zOffset, distanceFactor); // Closer = less zAdjustment, farther = more\n\n posp.z -= dynamicZAdjustment;\n posqIn.z -= dynamicZAdjustment;\n posqOut.z -= dynamicZAdjustment;\n\n var p = camera.uProjectionMatrix * posp;\n var qIn = camera.uProjectionMatrix * posqIn;\n var qOut = camera.uProjectionMatrix * posqOut;\n\n var tangentIn = normalize((qIn.xy * p.w - p.xy * qIn.w) * camera.uViewport.zw);\n var tangentOut = normalize((qOut.xy * p.w - p.xy * qOut.w) * camera.uViewport.zw);\n\n var curPerspScale = vec2<f32>();\n if (camera.uPerspective == 1) {\n // Perspective ---\n // convert from world to clip by multiplying with projection scaling factor\n // to get the right thickness (see https://github.com/processing/processing/issues/5182)\n\n // The y value of the projection matrix may be flipped if rendering to a Framebuffer.\n // Multiplying again by its sign here negates the flip to get just the scale.\n curPerspScale = (camera.uProjectionMatrix * vec4(1., sign(camera.uProjectionMatrix[1][1]), 0., 0.)).xy;\n } else {\n // No Perspective ---\n // multiply by W (to cancel out division by W later in the pipeline) and\n // convert from screen to clip (derived from clip to screen above)\n curPerspScale = p.w / (0.5 * camera.uViewport.zw);\n }\n\n var offset = vec2<f32>();\n if (output.vJoin == 1. && !simpleLines) {\n output.vTangent = normalize(tangentIn + tangentOut);\n var normalIn = vec2<f32>(-tangentIn.y, tangentIn.x);\n var normalOut = vec2<f32>(-tangentOut.y, tangentOut.x);\n var side = sign(input.aSide);\n var sideEnum = abs(input.aSide);\n\n // We generate vertices for joins on either side of the centerline, but\n // the "elbow" side is the only one needing a join. By not setting the\n // offset for the other side, all its vertices will end up in the same\n // spot and not render, effectively discarding it.\n if (sign(dot(tangentOut, vec2<f32>(-tangentIn.y, tangentIn.x))) != side) {\n // Side enums:\n // 1: the side going into the join\n // 2: the middle of the join\n // 3: the side going out of the join\n if (sideEnum == 2.) {\n // Calculate the position + tangent on either side of the join, and\n // find where the lines intersect to find the elbow of the join\n var c = (posp.xy / posp.w + vec2<f32>(1.)) * 0.5 * camera.uViewport.zw;\n\n var intersection = lineIntersection(\n c + (side * normalIn * inputs.weight / 2.),\n tangentIn,\n c + (side * normalOut * inputs.weight / 2.),\n tangentOut\n );\n offset = intersection - c;\n\n\n // When lines are thick and the angle of the join approaches 180, the\n // elbow might be really far from the center. We'll apply a limit to\n // the magnitude to avoid lines going across the whole screen when this\n // happens.\n var mag = length(offset);\n var maxMag = 3. * inputs.weight;\n if (mag > maxMag) {\n offset *= maxMag / mag;\n }\n } else if (sideEnum == 1.) {\n offset = side * normalIn * inputs.weight / 2.;\n } else if (sideEnum == 3.) {\n offset = side * normalOut * inputs.weight / 2.;\n }\n }\n if (stroke.uStrokeJoin == 2) {\n var avgNormal = vec2<f32>(-output.vTangent.y, output.vTangent.x);\n output.vMaxDist = abs(dot(avgNormal, normalIn * inputs.weight / 2.));\n } else {\n output.vMaxDist = inputs.weight / 2.;\n }\n } else {\n var tangent: vec2<f32>;\n if (all(input.aTangentIn == vec3<f32>())) {\n tangent = tangentOut;\n } else {\n tangent = tangentIn;\n }\n output.vTangent = tangent;\n var normal = vec2<f32>(-tangent.y, tangent.x);\n\n var normalOffset = sign(input.aSide);\n // Caps will have side values of -2 or 2 on the edge of the cap that\n // extends out from the line\n var tangentOffset = abs(input.aSide) - 1.;\n offset = (normal * normalOffset + tangent * tangentOffset) *\n inputs.weight * 0.5;\n output.vMaxDist = inputs.weight / 2.;\n }\n output.vCenter = p.xy;\n output.vPosition = output.vCenter + offset;\n output.vColor = inputs.color;\n\n output.Position = vec4<f32>(\n p.xy + offset.xy * curPerspScale,\n p.zw\n );\n HOOK_afterVertex();\n return output;\n}`,ae=`\nstruct StrokeFragmentInput {\n @location(0) vColor: vec4<f32>,\n @location(1) vTangent: vec2<f32>,\n @location(2) vCenter: vec2<f32>,\n @location(3) vPosition: vec2<f32>,\n @location(4) vMaxDist: f32,\n @location(5) vCap: f32,\n @location(6) vJoin: f32,\n @location(7) vStrokeWeight: f32,\n}\n\n${oe}\n@group(0) @binding(0) var<uniform> stroke: StrokeUniforms;\n@group(1) @binding(0) var<uniform> model: ModelUniforms;\n@group(2) @binding(0) var<uniform> camera: CameraUniforms;\n\n\nfn distSquared(a: vec2<f32>, b: vec2<f32>) -> f32 {\n return dot(b - a, b - a);\n}\n\nstruct Inputs {\n color: vec4<f32>,\n tangent: vec2<f32>,\n center: vec2<f32>,\n position: vec2<f32>,\n strokeWeight: f32,\n}\n\n@fragment\nfn main(input: StrokeFragmentInput) -> @location(0) vec4<f32> {\n HOOK_beforeFragment();\n\n var inputs: Inputs;\n inputs.color = input.vColor;\n inputs.tangent = input.vTangent;\n inputs.center = input.vCenter;\n inputs.position = input.vPosition;\n inputs.strokeWeight = input.vStrokeWeight;\n inputs = HOOK_getPixelInputs(inputs);\n\n if (input.vCap > 0.) {\n if (\n stroke.uStrokeCap == STROKE_CAP_ROUND &&\n HOOK_shouldDiscard(distSquared(inputs.position, inputs.center) > inputs.strokeWeight * inputs.strokeWeight * 0.25)\n ) {\n discard;\n } else if (\n stroke.uStrokeCap == STROKE_CAP_SQUARE &&\n HOOK_shouldDiscard(dot(inputs.position - inputs.center, inputs.tangent) > 0.)\n ) {\n discard;\n } else if (HOOK_shouldDiscard(false)) {\n discard;\n }\n } else if (input.vJoin > 0.) {\n if (\n stroke.uStrokeJoin == STROKE_JOIN_ROUND &&\n HOOK_shouldDiscard(distSquared(inputs.position, inputs.center) > inputs.strokeWeight * inputs.strokeWeight * 0.25)\n ) {\n discard;\n } else if (stroke.uStrokeJoin == STROKE_JOIN_BEVEL) {\n let normal = vec2<f32>(-inputs.tangent.y, -inputs.tangent.x);\n if (HOOK_shouldDiscard(abs(dot(inputs.position - inputs.center, normal)) > input.vMaxDist)) {\n discard;\n }\n } else if (HOOK_shouldDiscard(false)) {\n discard;\n }\n }\n var col = HOOK_getFinalColor(inputs.color);\n col = vec4<f32>(col.rgb, 1.0) * col.a;\n HOOK_afterFragment();\n return vec4<f32>(col);\n}\n`,ue="\n// Group 0: Material Properties\nstruct MaterialUniforms {\n uUseVertexColor: u32,\n uHasSetAmbient: u32,\n uAmbientColor: vec3<f32>,\n uSpecularMatColor: vec4<f32>,\n uAmbientMatColor: vec4<f32>,\n uEmissiveMatColor: vec4<f32>,\n uTint: vec4<f32>,\n isTexture: u32,\n uSpecular: u32,\n uShininess: f32,\n uMetallic: f32,\n}\n\n// Group 0: Lighting\nstruct LightingUniforms {\n uDirectionalLightCount: i32,\n uLightingDirection: array<vec3<f32>, 5>,\n uDirectionalDiffuseColors: array<vec3<f32>, 5>,\n uDirectionalSpecularColors: array<vec3<f32>, 5>,\n uPointLightCount: i32,\n uPointLightLocation: array<vec3<f32>, 5>,\n uPointLightDiffuseColors: array<vec3<f32>, 5>,\n uPointLightSpecularColors: array<vec3<f32>, 5>,\n uSpotLightCount: i32,\n uSpotLightAngle: vec4<f32>,\n uSpotLightConc: vec4<f32>,\n uSpotLightDiffuseColors: array<vec3<f32>, 4>,\n uSpotLightSpecularColors: array<vec3<f32>, 4>,\n uSpotLightLocation: array<vec3<f32>, 4>,\n uSpotLightDirection: array<vec3<f32>, 4>,\n uConstantAttenuation: f32,\n uLinearAttenuation: f32,\n uQuadraticAttenuation: f32,\n uUseImageLight: u32,\n uUseLighting: u32,\n}\n\n// Group 1: Model Transform\nstruct ModelUniforms {\n// @p5 ifdef Vertex getWorldInputs\n uModelMatrix: mat4x4<f32>,\n uModelNormalMatrix: mat3x3<f32>,\n// @p5 endif\n// @p5 ifndef Vertex getWorldInputs\n uModelViewMatrix: mat4x4<f32>,\n uNormalMatrix: mat3x3<f32>,\n// @p5 endif\n uMaterialColor: vec4<f32>,\n}\n\n// Group 2: Camera and Projection\nstruct CameraUniforms {\n uViewMatrix: mat4x4<f32>,\n uProjectionMatrix: mat4x4<f32>,\n uCameraNormalMatrix: mat3x3<f32>,\n}\n",le=`\nstruct VertexInput {\n @location(0) aPosition: vec3<f32>,\n @location(1) aNormal: vec3<f32>,\n @location(2) aTexCoord: vec2<f32>,\n @location(3) aVertexColor: vec4<f32>,\n};\n\nstruct VertexOutput {\n @builtin(position) Position: vec4<f32>,\n @location(0) vNormal: vec3<f32>,\n @location(1) vTexCoord: vec2<f32>,\n @location(2) vViewPosition: vec3<f32>,\n @location(4) vColor: vec4<f32>,\n};\n\n${ue}\n@group(0) @binding(0) var<uniform> material: MaterialUniforms;\n@group(0) @binding(1) var<uniform> lighting: LightingUniforms;\n@group(1) @binding(0) var<uniform> model: ModelUniforms;\n@group(2) @binding(0) var<uniform> camera: CameraUniforms;\n\nstruct Vertex {\n position: vec3<f32>,\n normal: vec3<f32>,\n texCoord: vec2<f32>,\n color: vec4<f32>,\n}\n\n@vertex\nfn main(input: VertexInput) -> VertexOutput {\n HOOK_beforeVertex();\n var output: VertexOutput;\n\n let useVertexColor = (material.uUseVertexColor != 0 && input.aVertexColor.x >= 0.0);\n var inputs = Vertex(\n input.aPosition,\n input.aNormal,\n input.aTexCoord,\n select(model.uMaterialColor, input.aVertexColor, useVertexColor)\n );\n\n// @p5 ifdef Vertex getObjectInputs\n inputs = HOOK_getObjectInputs(inputs);\n// @p5 endif\n\n// @p5 ifdef Vertex getWorldInputs\n inputs.position = (model.uModelMatrix * vec4<f32>(inputs.position, 1.0)).xyz;\n inputs.normal = model.uModelNormalMatrix * inputs.normal;\n inputs = HOOK_getWorldInputs(inputs);\n// @p5 endif\n\n// @p5 ifdef Vertex getWorldInputs\n // Already multiplied by the model matrix, just apply view\n inputs.position = (camera.uViewMatrix * vec4<f32>(inputs.position, 1.0)).xyz;\n inputs.normal = camera.uCameraNormalMatrix * inputs.normal;\n// @p5 endif\n// @p5 ifndef Vertex getWorldInputs\n // Apply both at once\n inputs.position = (model.uModelViewMatrix * vec4<f32>(inputs.position, 1.0)).xyz;\n inputs.normal = model.uNormalMatrix * inputs.normal;\n// @p5 endif\n\n// @p5 ifdef Vertex getCameraInputs\n inputs = HOOK_getCameraInputs(inputs);\n// @p5 endif\n\n output.vViewPosition = inputs.position;\n output.vTexCoord = inputs.texCoord;\n output.vNormal = normalize(inputs.normal);\n output.vColor = inputs.color;\n\n output.Position = camera.uProjectionMatrix * vec4<f32>(inputs.position, 1.0);\n\n HOOK_afterVertex();\n return output;\n}\n`,pe=`\nstruct FragmentInput {\n @location(0) vNormal: vec3<f32>,\n @location(1) vTexCoord: vec2<f32>,\n @location(2) vViewPosition: vec3<f32>,\n @location(4) vColor: vec4<f32>,\n};\n\n${ue}\n@group(0) @binding(0) var<uniform> material: MaterialUniforms;\n@group(0) @binding(1) var<uniform> lighting: LightingUniforms;\n@group(0) @binding(2) var uSampler: texture_2d<f32>;\n@group(0) @binding(3) var uSampler_sampler: sampler;\n@group(0) @binding(4) var environmentMapDiffused: texture_2d<f32>;\n@group(0) @binding(5) var environmentMapDiffused_sampler: sampler;\n@group(0) @binding(6) var environmentMapSpecular: texture_2d<f32>;\n@group(0) @binding(7) var environmentMapSpecular_sampler: sampler;\n@group(1) @binding(0) var<uniform> model: ModelUniforms;\n@group(2) @binding(0) var<uniform> camera: CameraUniforms;\n\nstruct ColorComponents {\n baseColor: vec3<f32>,\n opacity: f32,\n ambientColor: vec3<f32>,\n specularColor: vec3<f32>,\n diffuse: vec3<f32>,\n ambient: vec3<f32>,\n specular: vec3<f32>,\n emissive: vec3<f32>,\n}\n\nstruct Inputs {\n normal: vec3<f32>,\n texCoord: vec2<f32>,\n ambientLight: vec3<f32>,\n ambientMaterial: vec3<f32>,\n specularMaterial: vec3<f32>,\n emissiveMaterial: vec3<f32>,\n color: vec4<f32>,\n shininess: f32,\n metalness: f32,\n}\n\n\nstruct LightResult {\n diffuse: vec3<f32>,\n specular: vec3<f32>,\n}\nstruct LightIntensityResult {\n diffuse: f32,\n specular: f32,\n}\n\nconst specularFactor = 2.0;\nconst diffuseFactor = 0.73;\nconst PI = 3.14159265359;\n\nfn mapTextureToNormal(v: vec3<f32>) -> vec2<f32> {\n // x = r sin(phi) cos(theta)\n // y = r cos(phi)\n // z = r sin(phi) sin(theta)\n let phi = acos(v.y);\n // if phi is 0, then there are no x, z components\n var theta = 0.0;\n // else\n theta = acos(v.x / sin(phi));\n let sinTheta = v.z / sin(phi);\n if (sinTheta < 0.0) {\n // Turn it into -theta, but in the 0-2PI range\n theta = 2.0 * PI - theta;\n }\n theta = theta / (2.0 * PI);\n let phiNorm = phi / PI;\n\n let angles = vec2<f32>(fract(theta + 0.25), 1.0 - phiNorm);\n return angles;\n}\n\nfn calculateImageDiffuse(vNormal: vec3<f32>, vViewPosition: vec3<f32>, metallic: f32) -> vec3<f32> {\n // make 2 seperate builds\n let worldCameraPosition = vec3<f32>(0.0, 0.0, 0.0); // hardcoded world camera position\n let worldNormal = normalize(vNormal * camera.uCameraNormalMatrix);\n let newTexCoord = mapTextureToNormal(worldNormal);\n let texture = textureSample(environmentMapDiffused, environmentMapDiffused_sampler, newTexCoord);\n // this is to make the darker sections more dark\n // png and jpg usually flatten the brightness so it is to reverse that\n return mix(smoothstep(vec3<f32>(0.0), vec3<f32>(1.0), texture.xyz), vec3<f32>(0.0), metallic);\n}\n\nfn calculateImageSpecular(vNormal: vec3<f32>, vViewPosition: vec3<f32>, shininess: f32, metallic: f32) -> vec3<f32> {\n let worldCameraPosition = vec3<f32>(0.0, 0.0, 0.0);\n let worldNormal = normalize(vNormal);\n let lightDirection = normalize(vViewPosition - worldCameraPosition);\n let R = reflect(lightDirection, worldNormal) * camera.uCameraNormalMatrix;\n let newTexCoord = mapTextureToNormal(R);\n\n // In p5js the range of shininess is >= 1,\n // Therefore roughness range will be ([0,1]*8)*20 or [0, 160]\n // The factor of 8 is because currently the getSpecularTexture\n // only calculated 8 different levels of roughness\n // The factor of 20 is just to spread up this range so that,\n // [1, max] of shininess is converted to [0,160] of roughness\n let roughness = 20.0 / shininess;\n let outColor = textureSampleLevel(environmentMapSpecular, environmentMapSpecular_sampler, newTexCoord, roughness * 8.0 - 1.);\n\n // this is to make the darker sections more dark\n // png and jpg usually flatten the brightness so it is to reverse that\n return mix(\n pow(outColor.xyz, vec3<f32>(10.0)),\n pow(outColor.xyz, vec3<f32>(1.2)),\n metallic\n );\n}\n\nfn phongSpecular(\n lightDirection: vec3<f32>,\n viewDirection: vec3<f32>,\n surfaceNormal: vec3<f32>,\n shininess: f32\n) -> f32 {\n let R = reflect(lightDirection, surfaceNormal);\n return pow(max(0.0, dot(R, viewDirection)), shininess);\n}\n\nfn lambertDiffuse(lightDirection: vec3<f32>, surfaceNormal: vec3<f32>) -> f32 {\n return max(0.0, dot(-lightDirection, surfaceNormal));\n}\n\nfn singleLight(\n viewDirection: vec3<f32>,\n normal: vec3<f32>,\n lightVector: vec3<f32>,\n shininess: f32,\n metallic: f32\n) -> LightIntensityResult {\n let lightDir = normalize(lightVector);\n let specularIntensity = mix(1.0, 0.4, metallic);\n let diffuseIntensity = mix(1.0, 0.1, metallic);\n let diffuse = lambertDiffuse(lightDir, normal) * diffuseIntensity;\n let specular = select(\n 0.,\n phongSpecular(lightDir, viewDirection, normal, shininess) * specularIntensity,\n material.uSpecular == 1\n );\n return LightIntensityResult(diffuse, specular);\n}\n\nfn totalLight(\n modelPosition: vec3<f32>,\n normal: vec3<f32>,\n shininess: f32,\n metallic: f32\n) -> LightResult {\n var totalSpecular = vec3<f32>(0.0, 0.0, 0.0);\n var totalDiffuse = vec3<f32>(0.0, 0.0, 0.0);\n\n if (lighting.uUseLighting == 0) {\n return LightResult(vec3<f32>(1.0, 1.0, 1.0), totalSpecular);\n }\n\n let viewDirection = normalize(-modelPosition);\n\n for (var j = 0; j < 5; j++) {\n if (j < lighting.uDirectionalLightCount) {\n let lightVector = (camera.uViewMatrix * vec4<f32>(\n lighting.uLightingDirection[j],\n 0.0\n )).xyz;\n let lightColor = lighting.uDirectionalDiffuseColors[j];\n let specularColor = lighting.uDirectionalSpecularColors[j];\n let result = singleLight(viewDirection, normal, lightVector, shininess, metallic);\n totalDiffuse += result.diffuse * lightColor;\n totalSpecular += result.specular * specularColor;\n }\n\n if (j < lighting.uPointLightCount) {\n let lightPosition = (camera.uViewMatrix * vec4<f32>(\n lighting.uPointLightLocation[j],\n 1.0\n )).xyz;\n let lightVector = modelPosition - lightPosition;\n let lightDistance = length(lightVector);\n let lightFalloff = 1.0 / (\n lighting.uConstantAttenuation +\n lightDistance * lighting.uLinearAttenuation +\n lightDistance * lightDistance * lighting.uQuadraticAttenuation\n );\n let lightColor = lighting.uPointLightDiffuseColors[j] * lightFalloff;\n let specularColor = lighting.uPointLightSpecularColors[j] * lightFalloff;\n let result = singleLight(viewDirection, normal, lightVector, shininess, metallic);\n totalDiffuse += result.diffuse * lightColor;\n totalSpecular += result.specular * specularColor;\n }\n\n if (j < lighting.uSpotLightCount) {\n let lightPosition = (camera.uViewMatrix * vec4<f32>(\n lighting.uSpotLightLocation[j],\n 1.0\n )).xyz;\n let lightVector = modelPosition - lightPosition;\n let lightDistance = length(lightVector);\n var lightFalloff = 1.0 / (\n lighting.uConstantAttenuation +\n lightDistance * lighting.uLinearAttenuation +\n lightDistance * lightDistance * lighting.uQuadraticAttenuation\n );\n let lightDirection = (camera.uViewMatrix * vec4<f32>(\n lighting.uSpotLightDirection[j],\n 0.0\n )).xyz;\n let spotDot = dot(normalize(lightVector), normalize(lightDirection));\n let spotFalloff = select(\n 0.0,\n pow(spotDot, lighting.uSpotLightConc[j]),\n spotDot < lighting.uSpotLightAngle[j]\n );\n lightFalloff *= spotFalloff;\n let lightColor = lighting.uSpotLightDiffuseColors[j];\n let specularColor = lighting.uSpotLightSpecularColors[j];\n let result = singleLight(viewDirection, normal, lightVector, shininess, metallic);\n totalDiffuse += result.diffuse * lightColor;\n totalSpecular += result.specular * specularColor;\n }\n }\n\n // Image light contribution\n if (lighting.uUseImageLight != 0) {\n totalDiffuse += calculateImageDiffuse(normal, modelPosition, metallic);\n totalSpecular += calculateImageSpecular(normal, modelPosition, shininess, metallic);\n }\n\n return LightResult(\n totalDiffuse * diffuseFactor,\n totalSpecular * specularFactor\n );\n}\n\n@fragment\nfn main(input: FragmentInput) -> @location(0) vec4<f32> {\n HOOK_beforeFragment();\n\n let color = select(\n input.vColor,\n textureSample(uSampler, uSampler_sampler, input.vTexCoord) * (material.uTint/255.0),\n material.isTexture == 1\n ); // TODO: check isTexture and apply tint\n var inputs = Inputs(\n normalize(input.vNormal),\n input.vTexCoord,\n material.uAmbientColor,\n select(color.rgb, material.uAmbientMatColor.rgb, material.uHasSetAmbient == 1),\n material.uSpecularMatColor.rgb,\n material.uEmissiveMatColor.rgb,\n color,\n material.uShininess,\n material.uMetallic\n );\n inputs = HOOK_getPixelInputs(inputs);\n\n let light = totalLight(\n input.vViewPosition,\n inputs.normal,\n inputs.shininess,\n inputs.metalness\n );\n\n let baseColor = inputs.color;\n let components = ColorComponents(\n baseColor.rgb,\n baseColor.a,\n inputs.ambientMaterial,\n inputs.specularMaterial,\n light.diffuse,\n inputs.ambientLight,\n light.specular,\n inputs.emissiveMaterial\n );\n\n var outColor = HOOK_getFinalColor(\n HOOK_combineColors(components)\n );\n outColor = vec4<f32>(outColor.rgb * outColor.a, outColor.a);\n HOOK_afterFragment();\n return outColor;\n}\n`,fe="\n// Group 0: Font Properties\nstruct FontUniforms {\n uStrokeImageSize: vec2<i32>,\n uCellsImageSize: vec2<i32>,\n uGridImageSize: vec2<i32>,\n uGridOffset: vec2<i32>,\n uGridSize: vec2<i32>,\n uGlyphRect: vec4<f32>,\n uGlyphOffset: f32,\n uMaterialColor: vec4<f32>,\n}\n\n// Group 1: Model Transform\nstruct ModelUniforms {\n uModelViewMatrix: mat4x4<f32>,\n}\n\n// Group 2: Camera and Projection\nstruct CameraUniforms {\n uProjectionMatrix: mat4x4<f32>,\n}\n",ce=`\nstruct VertexInput {\n @location(0) aPosition: vec3<f32>,\n @location(1) aTexCoord: vec2<f32>,\n};\n\nstruct VertexOutput {\n @builtin(position) Position: vec4<f32>,\n @location(0) vTexCoord: vec2<f32>,\n};\n\n${fe}\n@group(0) @binding(0) var<uniform> font: FontUniforms;\n@group(1) @binding(0) var<uniform> model: ModelUniforms;\n@group(2) @binding(0) var<uniform> camera: CameraUniforms;\n\n@vertex\nfn main(input: VertexInput) -> VertexOutput {\n var output: VertexOutput;\n var positionVec4 = vec4<f32>(input.aPosition, 1.0);\n\n // scale by the size of the glyph's rectangle\n positionVec4.x = positionVec4.x * (font.uGlyphRect.z - font.uGlyphRect.x);\n positionVec4.y = positionVec4.y * (font.uGlyphRect.w - font.uGlyphRect.y);\n\n // Expand glyph bounding boxes by 1px on each side to give a bit of room\n // for antialiasing\n let newOrigin = (model.uModelViewMatrix * vec4<f32>(0.0, 0.0, 0.0, 1.0)).xyz;\n let newDX = (model.uModelViewMatrix * vec4<f32>(1.0, 0.0, 0.0, 1.0)).xyz;\n let newDY = (model.uModelViewMatrix * vec4<f32>(0.0, 1.0, 0.0, 1.0)).xyz;\n let pixelScale = vec2<f32>(\n 1.0 / length(newOrigin - newDX),\n 1.0 / length(newOrigin - newDY)\n );\n let offset = pixelScale * normalize(input.aTexCoord - vec2<f32>(0.5, 0.5));\n let textureOffset = offset * (1.0 / vec2<f32>(\n font.uGlyphRect.z - font.uGlyphRect.x,\n font.uGlyphRect.w - font.uGlyphRect.y\n ));\n\n // move to the corner of the glyph\n positionVec4.x = positionVec4.x + font.uGlyphRect.x;\n positionVec4.y = positionVec4.y + font.uGlyphRect.y;\n\n // move to the letter's line offset\n positionVec4.x = positionVec4.x + font.uGlyphOffset;\n\n positionVec4.x = positionVec4.x + offset.x;\n positionVec4.y = positionVec4.y + offset.y;\n\n output.Position = camera.uProjectionMatrix * model.uModelViewMatrix * positionVec4;\n output.vTexCoord = input.aTexCoord + textureOffset;\n\n return output;\n}\n`,de=`\nstruct FragmentInput {\n @location(0) vTexCoord: vec2<f32>,\n};\n\n${fe}\n@group(0) @binding(0) var<uniform> font: FontUniforms;\n@group(0) @binding(1) var uSamplerStrokes: texture_2d<f32>;\n@group(0) @binding(2) var uSamplerStrokes_sampler: sampler;\n@group(0) @binding(3) var uSamplerRowStrokes: texture_2d<f32>;\n@group(0) @binding(4) var uSamplerRowStrokes_sampler: sampler;\n@group(0) @binding(5) var uSamplerRows: texture_2d<f32>;\n@group(0) @binding(6) var uSamplerRows_sampler: sampler;\n@group(0) @binding(7) var uSamplerColStrokes: texture_2d<f32>;\n@group(0) @binding(8) var uSamplerColStrokes_sampler: sampler;\n@group(0) @binding(9) var uSamplerCols: texture_2d<f32>;\n@group(0) @binding(10) var uSamplerCols_sampler: sampler;\n@group(1) @binding(0) var<uniform> model: ModelUniforms;\n@group(2) @binding(0) var<uniform> camera: CameraUniforms;\n\n// some helper functions\nfn ROUND_f32(v: f32) -> i32 { return i32(floor(v + 0.5)); }\nfn ROUND_vec2(v: vec2<f32>) -> vec2<i32> { return vec2<i32>(floor(v + 0.5)); }\nfn saturate_f32(v: f32) -> f32 { return clamp(v, 0.0, 1.0); }\nfn saturate_vec2(v: vec2<f32>) -> vec2<f32> { return clamp(v, vec2<f32>(0.0), vec2<f32>(1.0)); }\n\nfn mul_f32_i32(v1: f32, v2: i32) -> i32 {\n return i32(floor(v1 * f32(v2)));\n}\n\nfn mul_vec2_ivec2(v1: vec2<f32>, v2: vec2<i32>) -> vec2<i32> {\n return vec2<i32>(floor(v1 * vec2<f32>(v2) + 0.5));\n}\n\n// unpack a 16-bit integer from a float vec2\nfn getInt16(v: vec2<f32>) -> i32 {\n let iv = ROUND_vec2(v * 255.0);\n return iv.x * 128 + iv.y;\n}\n\nconst minDistance: f32 = 1.0/8192.0;\nconst hardness: f32 = 1.05; // amount of antialias\n\n// the maximum number of curves in a glyph\nconst N: i32 = 250;\n\n// retrieves an indexed pixel from a texture\nfn getTexel(texture: texture_2d<f32>, samp: sampler, pos: i32, size: vec2<i32>) -> vec4<f32> {\n let width = size.x;\n let x = pos % width;\n let y = pos / width;\n\n return textureLoad(texture, vec2<i32>(x, y), 0);\n}\n\nfn calculateCrossings(p0: vec2<f32>, p1: vec2<f32>, p2: vec2<f32>, vTexCoord: vec2<f32>, pixelScale: vec2<f32>) -> array<vec2<f32>, 2> {\n // get the coefficients of the quadratic in t\n var a = p0 - p1 * 2.0 + p2;\n var b = p0 - p1;\n a = vec2<f32>(\n select(a.x, sign(a.x) * 1e-6, abs(a.x) < 1e-6),\n select(a.y, sign(a.y) * 1e-6, abs(a.y) < 1e-6)\n );\n b = vec2<f32>(\n select(b.x, sign(b.x) * 1e-6, abs(b.x) < 1e-6),\n select(b.y, sign(b.y) * 1e-6, abs(b.y) < 1e-6)\n );\n let c = p0 - vTexCoord;\n\n // found out which values of 't' it crosses the axes\n let surd = sqrt(max(vec2<f32>(0.0), b * b - a * c));\n let t1 = ((b - surd) / a).yx;\n let t2 = ((b + surd) / a).yx;\n\n // approximate straight lines to avoid rounding errors\n var t1_fixed = t1;\n var t2_fixed = t2;\n if (abs(a.y) < 0.001) {\n t1_fixed.x = c.y / (2.0 * b.y);\n t2_fixed.x = c.y / (2.0 * b.y);\n }\n\n if (abs(a.x) < 0.001) {\n t1_fixed.y = c.x / (2.0 * b.x);\n t2_fixed.y = c.x / (2.0 * b.x);\n }\n\n // plug into quadratic formula to find the coordinates of the crossings\n let C1 = ((a * t1_fixed - b * 2.0) * t1_fixed + c) * pixelScale;\n let C2 = ((a * t2_fixed - b * 2.0) * t2_fixed + c) * pixelScale;\n\n return array<vec2<f32>, 2>(C1, C2);\n}\n\nfn coverageX(p0: vec2<f32>, p1: vec2<f32>, p2: vec2<f32>, vTexCoord: vec2<f32>, pixelScale: vec2<f32>, coverage: ptr<function, vec2<f32>>, weight: ptr<function, vec2<f32>>) {\n let crossings = calculateCrossings(p0, p1, p2, vTexCoord, pixelScale);\n let C1 = crossings[0];\n let C2 = crossings[1];\n\n // determine on which side of the x-axis the points lie\n let y0 = p0.y > vTexCoord.y;\n let y1 = p1.y > vTexCoord.y;\n let y2 = p2.y > vTexCoord.y;\n\n // could we be under the curve (after t1)?\n if ((y1 && !y2) || (!y1 && y0)) {\n // add the coverage for t1\n (*coverage).x = (*coverage).x + saturate_f32(C1.x + 0.5);\n // calculate the anti-aliasing for t1\n (*weight).x = min((*weight).x, abs(C1.x));\n }\n\n // are we outside the curve (after t2)?\n if ((y1 && !y0) || (!y1 && y2)) {\n // subtract the coverage for t2\n (*coverage).x = (*coverage).x - saturate_f32(C2.x + 0.5);\n // calculate the anti-aliasing for t2\n (*weight).x = min((*weight).x, abs(C2.x));\n }\n}\n\n// this is essentially the same as coverageX, but with the axes swapped\nfn coverageY(p0: vec2<f32>, p1: vec2<f32>, p2: vec2<f32>, vTexCoord: vec2<f32>, pixelScale: vec2<f32>, coverage: ptr<function, vec2<f32>>, weight: ptr<function, vec2<f32>>) {\n let crossings = calculateCrossings(p0, p1, p2, vTexCoord, pixelScale);\n let C1 = crossings[0];\n let C2 = crossings[1];\n\n let x0 = p0.x > vTexCoord.x;\n let x1 = p1.x > vTexCoord.x;\n let x2 = p2.x > vTexCoord.x;\n\n if ((x1 && !x2) || (!x1 && x0)) {\n (*coverage).y = (*coverage).y - saturate_f32(C1.y + 0.5);\n weight.y = min(weight.y, abs(C1.y));\n }\n\n if ((x1 && !x0) || (!x1 && x2)) {\n (*coverage).y = (*coverage).y + saturate_f32(C2.y + 0.5);\n (*weight).y = min((*weight).y, abs(C2.y));\n }\n}\n\n@fragment\nfn main(input: FragmentInput) -> @location(0) vec4<f32> {\n // var pixelScale: vec2<f32>;\n var coverage: vec2<f32> = vec2<f32>(0.0);\n var weight: vec2<f32> = vec2<f32>(0.5);\n let pixelScale = hardness / fwidth(input.vTexCoord);\n\n // which grid cell is this pixel in?\n let gridCoord = vec2<i32>(floor(input.vTexCoord * vec2<f32>(font.uGridSize)));\n\n // intersect curves in this row\n {\n // the index into the row info bitmap\n let rowIndex = gridCoord.y + font.uGridOffset.y;\n // fetch the info texel\n let rowInfo = getTexel(uSamplerRows, uSamplerRows_sampler, rowIndex, font.uGridImageSize);\n // unpack the rowInfo\n let rowStrokeIndex = getInt16(rowInfo.xy);\n let rowStrokeCount = getInt16(rowInfo.zw);\n\n for (var iRowStroke = 0; iRowStroke < N; iRowStroke = iRowStroke + 1) {\n if (iRowStroke >= rowStrokeCount) {\n break;\n }\n\n // each stroke is made up of 3 points: the start and control point\n // and the start of the next curve.\n // fetch the indices of this pair of strokes:\n let strokeIndices = getTexel(uSamplerRowStrokes, uSamplerRowStrokes_sampler, rowStrokeIndex + iRowStroke, font.uCellsImageSize);\n\n // unpack the stroke index\n let strokePos = getInt16(strokeIndices.xy);\n\n // fetch the two strokes\n let stroke0 = getTexel(uSamplerStrokes, uSamplerStrokes_sampler, strokePos + 0, font.uStrokeImageSize);\n let stroke1 = getTexel(uSamplerStrokes, uSamplerStrokes_sampler, strokePos + 1, font.uStrokeImageSize);\n\n // calculate the coverage\n coverageX(stroke0.xy, stroke0.zw, stroke1.xy, input.vTexCoord, pixelScale, &coverage, &weight);\n }\n }\n\n // intersect curves in this column\n {\n let colIndex = gridCoord.x + font.uGridOffset.x;\n let colInfo = getTexel(uSamplerCols, uSamplerCols_sampler, colIndex, font.uGridImageSize);\n let colStrokeIndex = getInt16(colInfo.xy);\n let colStrokeCount = getInt16(colInfo.zw);\n\n for (var iColStroke = 0; iColStroke < N; iColStroke = iColStroke + 1) {\n if (iColStroke >= colStrokeCount) {\n break;\n }\n\n let strokeIndices = getTexel(uSamplerColStrokes, uSamplerColStrokes_sampler, colStrokeIndex + iColStroke, font.uCellsImageSize);\n\n let strokePos = getInt16(strokeIndices.xy);\n let stroke0 = getTexel(uSamplerStrokes, uSamplerStrokes_sampler, strokePos + 0, font.uStrokeImageSize);\n let stroke1 = getTexel(uSamplerStrokes, uSamplerStrokes_sampler, strokePos + 1, font.uStrokeImageSize);\n coverageY(stroke0.xy, stroke0.zw, stroke1.xy, input.vTexCoord, pixelScale, &coverage, &weight);\n }\n }\n\n weight = saturate_vec2(vec2<f32>(1.0) - weight * 2.0);\n let distance = max(weight.x + weight.y, minDistance); // manhattan approx.\n let antialias = abs(dot(coverage, weight) / distance);\n let cover = min(abs(coverage.x), abs(coverage.y));\n var outColor = vec4<f32>(font.uMaterialColor.rgb, 1.0) * font.uMaterialColor.a;\n outColor = outColor * saturate_f32(max(antialias, cover));\n return outColor;\n}\n`,me="\nstruct Uniforms {\n uModelViewMatrix: mat4x4<f32>,\n uProjectionMatrix: mat4x4<f32>,\n};\n",he=`\nstruct VertexInput {\n @location(0) aPosition: vec3<f32>,\n @location(1) aNormal: vec3<f32>,\n @location(2) aTexCoord: vec2<f32>,\n @location(3) aVertexColor: vec4<f32>,\n};\n\nstruct VertexOutput {\n @builtin(position) Position: vec4<f32>,\n @location(0) vTexCoord: vec2<f32>,\n};\n\n${me}\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\n\n@vertex\nfn main(input: VertexInput) -> VertexOutput {\n var output: VertexOutput;\n output.vTexCoord = input.aTexCoord;\n let positionVec4 = vec4<f32>(input.aPosition, 1.0);\n output.Position = uniforms.uProjectionMatrix * uniforms.uModelViewMatrix * positionVec4;\n return output;\n}\n`,ge=`\nstruct FragmentInput {\n @location(0) vTexCoord: vec2<f32>,\n};\n\n${me}\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\n@group(0) @binding(1) var uSampler: texture_2d<f32>;\n@group(0) @binding(2) var uSampler_sampler: sampler;\n\n@fragment\nfn main(input: FragmentInput) -> @location(0) vec4<f32> {\n return textureSample(uSampler, uSampler_sampler, input.vTexCoord);\n}\n`;function xe(e){throw new Error(`[p5.strands internal error]: ${e}`)}function ve(e,t){throw new Error(`[p5.strands ${e}]: ${t}`)}function Te(e,t){const n=function(e,t){const n=e.nextID++;e.nodeTypes[n]=t.nodeType,e.opCodes[n]=t.opCode,e.values[n]=t.value,e.identifiers[n]=t.identifier,e.dependsOn[n]=t.dependsOn.slice(),e.usedBy[n]=t.usedBy,e.phiBlocks[n]=t.phiBlocks.slice(),e.baseTypes[n]=t.baseType,e.dimensions[n]=t.dimension,e.statementTypes[n]=t.statementType,e.swizzles[n]=t.swizzle;for(const r of t.dependsOn)Array.isArray(e.usedBy[r])||(e.usedBy[r]=[]),e.usedBy[r].push(n);return n}(e,t);return n}function ye(e={}){const t={nodeType:e.nodeType??null,baseType:e.baseType??null,dimension:e.dimension??null,opCode:e.opCode??null,value:e.value??null,identifier:e.identifier??null,statementType:e.statementType??null,swizzle:e.swizzle??null,dependsOn:Array.isArray(e.dependsOn)?e.dependsOn:[],usedBy:Array.isArray(e.usedBy)?e.usedBy:[],phiBlocks:Array.isArray(e.phiBlocks)?e.phiBlocks:[]};return function(e){const t=e.nodeType,n=R[t];2===n.length&&xe(`Required fields for node type '${D[t]}' not defined. Please add them to the utils.js file in p5.strands!`);const r=[];for(const t of n)null===e[t]&&r.push(t);if(e.dependsOn?.some((e=>void 0===e)))throw new Error("Undefined dependency!");r.length>0&&xe(`Missing fields ${r.join(", ")} for a node type '${D[t]}'.`)}(t),t}function be(e,t){return{id:t,nodeType:e.nodeTypes[t],opCode:e.opCodes[t],value:e.values[t],identifier:e.identifiers[t],dependsOn:e.dependsOn[t],usedBy:e.usedBy[t],phiBlocks:e.phiBlocks[t],dimension:e.dimensions[t],baseType:e.baseTypes[t],statementType:e.statementTypes[t],swizzle:e.swizzles[t]}}function Ce(e,t){return{baseType:e.baseTypes[t],dimension:e.dimensions[t],priority:q[e.baseTypes[t]]}}function _e(e,t,n){void 0===n&&xe("undefined nodeID in `recordInBasicBlock()`"),void 0===t&&xe("undefined blockID in `recordInBasicBlock()"),e.blockInstructions[t]=e.blockInstructions[t]||[],e.blockInstructions[t].push(n)}class Oe{constructor(e,t,n){this.id=e,this.strandsContext=n,this.dimension=t,this.structProperties=null,this.isStrandsNode=!0;const r=be(this.strandsContext.dag,this.id);r&&r.identifier&&(this._originalIdentifier=r.identifier),r&&(this._originalBaseType=r.baseType,this._originalDimension=r.dimension)}withStructProperties(e){return this.structProperties=e,this}copy(){return we(this.id,this.dimension,this.strandsContext)}typeInfo(){return{baseType:this._originalBaseType||k,dimension:this.dimension}}bridge(e){const{dag:t,cfg:n}=this.strandsContext,r=be(t,this.id),i=r?.baseType??k;let o;if(e?.isStrandsNode)o=e.id;else{o=Pe(this.strandsContext,{baseType:i,dimension:this.dimension},e).id}if(this._originalIdentifier){const{id:e}=Ae(this.strandsContext,{baseType:this._originalBaseType,dimension:this._originalDimension},this._originalIdentifier),r=Te(t,ye({nodeType:E.ASSIGNMENT,dependsOn:[e,o],phiBlocks:[]}));_e(n,n.currentBlock,r),this.strandsContext.globalAssignments.push(r);const i=Te(t,ye({nodeType:E.VARIABLE,baseType:this._originalBaseType,dimension:this._originalDimension,identifier:this._originalIdentifier}));this.id=i}else this.id=o;return this}bridgeSwizzle(e,t){const{dag:n,cfg:r}=this.strandsContext,i=be(n,this.id),o=i?.baseType??k;let s;if(t?.isStrandsNode)s=t.id;else{s=Pe(this.strandsContext,{baseType:o,dimension:this.dimension},t).id}if(this._originalIdentifier){const{id:t}=Ae(this.strandsContext,{baseType:this._originalBaseType,dimension:this._originalDimension},this._originalIdentifier),i=Te(n,ye({nodeType:E.OPERATION,opCode:Q.SWIZZLE,baseType:this._originalBaseType,dimension:e.length,swizzle:e,dependsOn:[t]})),o=Te(n,ye({nodeType:E.ASSIGNMENT,dependsOn:[i,s],phiBlocks:[]}));_e(r,r.currentBlock,o),this.strandsContext.globalAssignments.push(o);const a=Te(n,ye({nodeType:E.VARIABLE,baseType:this._originalBaseType,dimension:this._originalDimension,identifier:this._originalIdentifier}));this.id=a}else this.id=s;return this}getValue(){if(this._originalIdentifier){const{id:e,dimension:t}=Ae(this.strandsContext,{baseType:this._originalBaseType,dimension:this._originalDimension},this._originalIdentifier);return we(e,t,this.strandsContext)}return this}}function we(e,t,n,r){return new Proxy(new Oe(e,t,n),function(e,t,n){const r=[["x","y","z","w"],["r","g","b","a"],["s","t","p","q"]].map((e=>e.slice(0,t))),i={get(e,t,i){if(t in e)return Reflect.get(...arguments);for(const i of r)if([...t.toString()].every((e=>i.includes(e)))){const o=[...t].map((e=>{const t=i.indexOf(e);return r[0][t]})).join(""),s=De(n,e,o);return we(s.id,s.dimension,n)}},set(e,t,i,o){for(const o of r){const r=[...t];if(!(r.every((e=>o.includes(e)))&&new Set(r).size===r.length&&e.dimension>=r.length))continue;const s=e.dimension,a=new Array(s);for(let t=0;t<s;t++){const{id:r,dimension:i}=De(n,e,"xyzw"[t]);a[t]=we(r,i,n)}let u=[];if(i?.isStrandsNode)if(1===i.dimension)u=Array(r.length).fill(i);else if(i.dimension===r.length)for(let e=0;e<r.length;e++){const{id:t,dimension:r}=De(n,i,"xyzw"[e]);u.push(we(t,r,n))}else ve("type error",`Swizzle assignment: RHS vector does not match LHS vector (need ${r.length}, got ${i.dimension}).`);else if(Array.isArray(i)){const e=i.flat(1/0);1===e.length?u=Array(r.length).fill(e[0]):e.length===r.length?u=e:ve("type error",`Swizzle assignment: RHS length ${e.length} does not match ${r.length}.`)}else"number"==typeof i?u=Array(r.length).fill(i):ve("type error",`Unsupported RHS for swizzle assignment: ${i}`);for(let e=0;e<r.length;e++){a[o.indexOf(r[e])]=u[e]}const l=be(n.dag,e.id),p=l?.baseType??k,{id:f}=Pe(n,{baseType:p,dimension:s},a);return e.id=f,!0}return Reflect.set(...arguments)}};return i}(0,t,n))}const Se={...{acos:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],acosh:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],asin:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],asinh:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],atan:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0},{params:[Y.FLOAT,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],atanh:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],cos:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],cosh:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],degrees:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],radians:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],sin:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],sinh:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],tan:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],tanh:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],abs:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0},{params:[Y.FLOAT],returnType:Y.INT,isp5Function:!0}],ceil:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],clamp:[{params:[Y.FLOAT,Y.FLOAT,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1},{params:[Y.FLOAT,K.float1,K.float1],returnType:Y.FLOAT,isp5Function:!1},{params:[Y.INT,Y.INT,Y.INT],returnType:Y.INT,isp5Function:!1},{params:[Y.INT,K.int1,K.int1],returnType:Y.INT,isp5Function:!1}],dFdx:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],dFdy:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],exp:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],exp2:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],floor:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],fma:[{params:[Y.FLOAT,Y.FLOAT,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],fract:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],fwidth:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],inversesqrt:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],log:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],log2:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],max:[{params:[Y.FLOAT,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0},{params:[Y.FLOAT,K.float1],returnType:Y.FLOAT,isp5Function:!0},{params:[Y.INT,Y.INT],returnType:Y.INT,isp5Function:!0},{params:[Y.INT,K.int1],returnType:Y.INT,isp5Function:!0}],min:[{params:[Y.FLOAT,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0},{params:[Y.FLOAT,K.float1],returnType:Y.FLOAT,isp5Function:!0},{params:[Y.INT,Y.INT],returnType:Y.INT,isp5Function:!0},{params:[Y.INT,K.int1],returnType:Y.INT,isp5Function:!0}],mix:[{params:[Y.FLOAT,Y.FLOAT,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1},{params:[Y.FLOAT,Y.FLOAT,K.float1],returnType:Y.FLOAT,isp5Function:!1},{params:[Y.FLOAT,Y.FLOAT,Y.BOOL],returnType:Y.FLOAT,isp5Function:!1}],mod:[{params:[Y.FLOAT,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0},{params:[Y.FLOAT,K.float1],returnType:Y.FLOAT,isp5Function:!0}],pow:[{params:[Y.FLOAT,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],round:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],roundEven:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],sign:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1},{params:[Y.INT],returnType:Y.INT,isp5Function:!1}],smoothstep:[{params:[Y.FLOAT,Y.FLOAT,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1},{params:[K.float1,K.float1,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],sqrt:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],step:[{params:[Y.FLOAT,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],trunc:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],cross:[{params:[K.float3,K.float3],returnType:K.float3,isp5Function:!0}],distance:[{params:[Y.FLOAT,Y.FLOAT],returnType:K.float1,isp5Function:!0}],dot:[{params:[Y.FLOAT,Y.FLOAT],returnType:K.float1,isp5Function:!0}],equal:[{params:[Y.FLOAT,Y.FLOAT],returnType:Y.BOOL,isp5Function:!1},{params:[Y.INT,Y.INT],returnType:Y.BOOL,isp5Function:!1},{params:[Y.BOOL,Y.BOOL],returnType:Y.BOOL,isp5Function:!1}],faceforward:[{params:[Y.FLOAT,Y.FLOAT,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],length:[{params:[Y.FLOAT],returnType:K.float1,isp5Function:!1}],normalize:[{params:[Y.FLOAT],returnType:Y.FLOAT,isp5Function:!0}],notEqual:[{params:[Y.FLOAT,Y.FLOAT],returnType:Y.BOOL,isp5Function:!1},{params:[Y.INT,Y.INT],returnType:Y.BOOL,isp5Function:!1},{params:[Y.BOOL,Y.BOOL],returnType:Y.BOOL,isp5Function:!1}],reflect:[{params:[Y.FLOAT,Y.FLOAT],returnType:Y.FLOAT,isp5Function:!1}],refract:[{params:[Y.FLOAT,Y.FLOAT,K.float1],returnType:Y.FLOAT,isp5Function:!1}]}};function Fe(e,t,n){const{cfg:r,dag:i}=e;let{dimension:o,baseType:s}=t;1!==o&&xe("Created a scalar literal node with dimension > 1.");const a=Te(i,ye({nodeType:E.LITERAL,dimension:o,baseType:s,value:n}));return _e(r,r.currentBlock,a),{id:a,dimension:o}}function Ae(e,t,n){const{cfg:r,dag:i}=e,{dimension:o,baseType:s}=t,a=Te(i,ye({nodeType:E.VARIABLE,dimension:o,baseType:s,identifier:n}));return _e(r,r.currentBlock,a),{id:a,dimension:o}}function Ie(e,t,n){const r=Array.isArray(n)?n:[n],i=[];let{dimension:o,baseType:s}=t;const a=e.dag;let u=0,l=null;for(const t of r.flat(1/0))if(t&&t.isStrandsNode){const e=be(a,t.id);if(l=t.id,s=e.baseType,e.opCode===J.CONSTRUCTOR)for(const t of e.dependsOn)i.push(t);else i.push(t.id);u+=e.dimension}else if("number"!=typeof t)ve("type error",`You've tried to construct a scalar or vector type with a non-numeric value: ${t}`);else{const{id:n,dimension:r}=Fe(e,{dimension:1,baseType:s},t);i.push(n),u+=r}null===o?o=u:o>u&&1===u?u=o:1!==u&&u!==o&&ve("type error",`You've tried to construct a ${s+o} with ${u} components`);return{originalNodeID:l,mappedDependencies:i,inferredTypeInfo:{dimension:o,baseType:s,priority:q[s]}}}function Le(e,t,n){const r=ye({nodeType:E.OPERATION,opCode:J.CONSTRUCTOR,dimension:t.dimension,baseType:t.baseType,dependsOn:n});return Te(e.dag,r)}function Pe(e,t,n){const r=e.cfg;n=(Array.isArray(n)?n:[n]).flat(1/0).map((t=>t.isStrandsNode&&t.typeInfo().baseType===z&&1===t.typeInfo().dimension?function(e,t){const{id:n,dimension:r}=Ee(e,e.backend.getTypeName("float",t.typeInfo().dimension),[t],{overloads:[{params:[t.typeInfo()],returnType:{...t.typeInfo(),baseType:k}}]});return we(n,r,e)}(e,t):t));const{mappedDependencies:i,inferredTypeInfo:o}=Ie(e,t,n),s={baseType:t.baseType,dimension:o.dimension},a=Le(e,s,i);return t.baseType!==j&&_e(r,r.currentBlock,a),{id:a,dimension:s.dimension,components:i}}function Ee(e,t,n,{overloads:r}={}){const{cfg:i,dag:o}=e,s=r||Se[t],a=n.map((t=>Ie(e,K.defer,t))),u=s.filter((e=>e.params.length===a.length));if(0===u.length){const e=new Set,n=[];s.forEach((t=>e.add(t.params.length))),e.forEach((e=>n.push(`${e}`)));const r=n.join(", or ");ve("parameter validation error",`Function '${t}' has ${s.length} variants which expect ${r} arguments, but ${a.length} arguments were provided.`)}const l=e=>null===e.dimension;let p=null,f=0,c=null,d=null;for(const e of u){let t=!0,n=0;for(let r=0;r<a.length;r++){const i=a[r].inferredTypeInfo,o=e.params[r];let s=o.dimension;l(o)?(null!==d&&1!==d||(d=i.dimension),d===i.dimension||1===i.dimension&&d>=1||(t=!1),s=d):i.dimension>s&&(t=!1),i.baseType===o.baseType?n+=2:o.priority>i.priority&&(n+=1)}t&&(!p||n>f)&&(p=e,f=n,c={...e.returnType},l(c)&&(c.dimension=d))}null===p&&ve("parameter validation",`No matching overload for ${t} was found!`);let m=[];for(let t=0;t<p.params.length;t++){const n=a[t],r={...p.params[t]};if(l(r)&&(r.dimension=d),n.originalNodeID&&(h=n.inferredTypeInfo,g=r,h.dimension===g.dimension&&h.baseType===g.baseType))m.push(n.originalNodeID);else{const t=Le(e,r,n.mappedDependencies);_e(i,i.currentBlock,t),m.push(t)}}var h,g;const x=Te(o,ye({nodeType:E.OPERATION,opCode:J.FUNCTION_CALL,identifier:t,dependsOn:m,baseType:c.baseType,dimension:c.dimension}));return _e(i,i.currentBlock,x),{id:x,dimension:c.dimension}}function De(e,t,n){const{dag:r,cfg:i}=e,o=r.baseTypes[t.id],s=Te(r,ye({nodeType:E.OPERATION,baseType:o,dimension:n.length,opCode:Q.SWIZZLE,dependsOn:[t.id],swizzle:n}));return _e(i,i.currentBlock,s),{id:s,dimension:n.length}}function Re(e,t){if(e.nodeTypes[t]!==E.OPERATION)return!1;if(e.baseTypes[t]===H)return!1;return(e.usedBy[t]||[]).length>1}const Ve={float1:"f32",float2:"vec2<f32>",float3:"vec3<f32>",float4:"vec4<f32>",int1:"i32",int2:"vec2<i32>",int3:"vec3<i32>",int4:"vec4<i32>",bool1:"bool",bool2:"vec2<bool>",bool3:"vec3<bool>",bool4:"vec4<bool>",mat2:"mat2x2<f32>",mat3:"mat3x3<f32>",mat4:"mat4x4<f32>"},Ne={[te.DEFAULT]:(e,t,n)=>{const{dag:r,cfg:i}=t,o=i.blockInstructions[e]||[];for(const e of o){const t=r.nodeTypes[e];if(Re(r,e)){const t=Me.generateDeclaration(n,r,e);n.write(t)}t===E.STATEMENT&&Me.generateStatement(n,r,e),t===E.ASSIGNMENT&&(Me.generateAssignment(n,r,e),n.visitedNodes.add(e))}},[te.BRANCH](e,t,n){const{dag:r,cfg:i}=t,o=i.blockInstructions[e]||[];for(const e of o){const t=be(r,e);if(t.nodeType===E.PHI){const i=t.dependsOn||[];if(i.length>0){const t=i[0],r=n.tempNames[t];if(r){n.tempNames[e]=r;continue}}const o="T"+n.nextTempID++;n.tempNames[e]=o;const s=Ce(r,e),a=Me.getTypeName(s.baseType,s.dimension);let u;if(1===s.dimension)u="float"===s.baseType?"0.0":"0";else{const e="float"===s.baseType?"0.0":"0";u=`${a}(${Array(s.dimension).fill(e).join(", ")})`}n.write(`var ${o}: ${a} = ${u};`)}}this[te.DEFAULT](e,t,n)},[te.IF_COND](e,t,n){const{dag:r,cfg:i}=t,o=i.blockConditions[e],s=Me.generateExpression(n,r,o);n.write(`if (${s})`),this[te.DEFAULT](e,t,n)},[te.ELSE_COND](e,t,n){n.write("else"),this[te.DEFAULT](e,t,n)},[te.IF_BODY](e,t,n){this[te.DEFAULT](e,t,n),this.assignPhiNodeValues(e,t,n)},[te.SCOPE_START](e,t,n){n.write("{"),n.indent++},[te.SCOPE_END](e,t,n){n.indent--,n.write("}")},[te.MERGE](e,t,n){this[te.DEFAULT](e,t,n)},[te.FUNCTION](e,t,n){this[te.DEFAULT](e,t,n)},[te.FOR](e,t,n){const{dag:r,cfg:i}=t,o=i.blockInstructions[e]||[];n.write("for (");const s=n.suppressSemicolon;for(let e=0;e<o.length;e++){const t=o[e],i=be(r,t),s=e===o.length-1;if(n.suppressSemicolon=s,Re(r,t)){const e=Me.generateDeclaration(n,r,t);n.write(e)}i.nodeType===E.STATEMENT&&Me.generateStatement(n,r,t),i.nodeType===E.ASSIGNMENT&&(Me.generateAssignment(n,r,t),n.visitedNodes.add(t))}n.suppressSemicolon=s,n.write(")")},assignPhiNodeValues(e,t,n){const{dag:r,cfg:i}=t,o=i.outgoingEdges[e]||[];for(const t of o){const o=i.blockInstructions[t]||[];for(const t of o){const i=be(r,t);if(i.nodeType===E.PHI){const o=i.phiBlocks?.indexOf(e);if(-1!==o&&o<i.dependsOn.length){const e=i.dependsOn[o],s=n.tempNames[t];if(s&&null!==e){const t=Me.generateExpression(n,r,e);n.write(`${s} = ${t};`)}}}}}}},Me={hookEntry(e){const t=`(${e.parameters.map((e=>`${e.type.properties?`_p5_strands_raw_${e.name}`:e.name}: ${e.type.typeName}`)).join(", ")}) {`,n=e.parameters.filter((e=>e.type.properties)).map((e=>` var ${e.name} = _p5_strands_raw_${e.name};`)).join("\n");return n?t+"\n"+n:t},addTextureBindingsToDeclarations(e){if(!e.renderer||!e.baseShader)return;let t=e.renderer.getNextBindingIndex({vert:e.baseShader.vertSrc(),frag:e.baseShader.fragSrc()});for(const{name:n,typeInfo:r}of e.uniforms)if("sampler2D"===r.baseType){const r=`@group(0) @binding(${t}) var ${n}: texture_2d<f32>;`,i=`@group(0) @binding(${t+1}) var ${n}_sampler: sampler;`;e.vertexDeclarations.add(r),e.vertexDeclarations.add(i),e.fragmentDeclarations.add(r),e.fragmentDeclarations.add(i),t+=2}},getTypeName(e,t){const n=Ve[e+t];return n||e},generateHookUniformKey(e,t){return"sampler2D"===t.baseType?`${e}: sampler2D`:`${e}: ${this.getTypeName(t.baseType,t.dimension)}`},generateVaryingVariable(e,t){return`${e}: ${this.getTypeName(t.baseType,t.dimension)}`},generateLocalDeclaration(e,t){return`var<private> ${e}: ${this.getTypeName(t.baseType,t.dimension)};`},generateStatement(e,t,n){const r=be(t,n),i=e.suppressSemicolon?"":";";if(r.statementType===V)e.write(`discard${i}`);else if(r.statementType===N)e.write(`break${i}`);else if(r.statementType===U){const n=r.dependsOn[0],o=this.generateExpression(e,t,n);e.write(`${o}${i}`)}else if(r.statementType===B)e.write(i);else if(r.statementType===M){const n=r.dependsOn[0],o=this.generateExpression(e,t,n);e.write(`return ${o}${i}`)}},generateAssignment(e,t,n){const r=be(t,n),i=r.dependsOn[0],o=r.dependsOn[1],s=be(t,i),a=e.suppressSemicolon?"":";";if(s.opCode===Q.SWIZZLE){const n=s.dependsOn[0],r=be(t,n),i=this.generateExpression(e,t,n),u=s.swizzle,l=r.dimension,p=this.generateExpression(e,t,o),f=[];for(let e=0;e<l;e++)f[e]={target:"self",index:e};const c=e=>"xyzw".includes(e)?"xyzw".indexOf(e):"rgba".includes(e)?"rgba".indexOf(e):-1;for(let e=0;e<u.length;e++){const t=c(u[e]);t>=0&&t<l&&(f[t]={target:"rhs",index:e})}const d=this.getTypeName(r.baseType,l),m=f.map((({target:e,index:t})=>`${"self"===e?i:p}.${"xyzw"[t]}`));e.write(`${i} = ${d}(${m.join(", ")})${a}`)}else{const n=this.generateExpression(e,t,i),r=this.generateExpression(e,t,o);n&&r&&n!==r&&e.write(`${n} = ${r}${a}`)}},generateDeclaration(e,t,n){const r=this.generateExpression(e,t,n),i="T"+e.nextTempID++;e.tempNames[n]=i;const o=Ce(t,n);return`var ${i}: ${this.getTypeName(o.baseType,o.dimension)} = ${r};`},generateReturnStatement(e,t,n,r){const i=e.dag,o=be(i,n);if((s=r)&&s.properties){const e=r;for(let n=0;n<e.properties.length;n++){const r=e.properties[n],s=this.generateExpression(t,i,o.dependsOn[n]);r.name!==s&&t.write(`${o.identifier}.${r.name} = ${s};`)}}var s;t.write(`return ${this.generateExpression(t,i,n)};`)},generateExpression(e,t,n){const r=be(t,n);if(e.tempNames?.[n])return e.tempNames[n];switch(r.nodeType){case E.LITERAL:return r.baseType===k?r.value.toFixed(4):r.value;case E.VARIABLE:if(e.shaderContext&&e.strandsContext?.sharedVariables?.has(r.identifier)){const t=e.strandsContext.sharedVariables.get(r.identifier);"vertex"===e.shaderContext?t.usedInVertex=!0:"fragment"===e.shaderContext&&(t.usedInFragment=!0)}const i=e.strandsContext?.uniforms?.find((e=>e.name===r.identifier));return i&&"sampler2D"!==i.typeInfo.baseType?`hooks.${r.identifier}`:r.identifier;case E.OPERATION:const o=r.usedBy.length>0;if(r.opCode===J.CONSTRUCTOR){if(r.baseType===H)return this.generateExpression(e,t,r.dependsOn[0]);return`${this.getTypeName(r.baseType,r.dimension)}(${r.dependsOn.map((n=>this.generateExpression(e,t,n))).join(", ")})`}if(r.opCode===J.FUNCTION_CALL){if("mod"===r.identifier&&2===r.dependsOn.length){const[n,i]=r.dependsOn,o=this.generateExpression(e,t,n),s=this.generateExpression(e,t,i);return r.usedBy.length>0?`(${o} % ${s})`:`${o} % ${s}`}if("atan"===r.identifier&&2===r.dependsOn.length){return`atan2(${r.dependsOn.map((n=>this.generateExpression(e,t,n))).join(", ")})`}const n=r.dependsOn.map((n=>this.generateExpression(e,t,n)));return`${r.identifier}(${n.join(", ")})`}if(r.opCode===X.MEMBER_ACCESS){const[n,i]=r.dependsOn;return`${this.generateExpression(e,t,n)}.${this.generateExpression(e,t,i)}`}if(r.opCode===Q.SWIZZLE){const n=r.dependsOn[0];return`${this.generateExpression(e,t,n)}.${r.swizzle}`}if(2===r.dependsOn.length){const[n,i]=r.dependsOn,s=this.generateExpression(e,t,n),a=this.generateExpression(e,t,i);if(r.opCode===X.MODULO)return`(${s} % ${a})`;const u=ee[r.opCode];return o?`(${s} ${u} ${a})`:`${s} ${u} ${a}`}if(r.opCode===Q.LOGICAL_NOT||r.opCode===Q.NEGATE||r.opCode===Q.PLUS){const[n]=r.dependsOn,i=this.generateExpression(e,t,n);return`${ee[r.opCode]}${i}`}case E.PHI:if(r.identifier)return r.identifier;if(e.tempNames?.[n])return e.tempNames[n];{const n=r.dependsOn.filter((e=>null!==e));if(n.length>0)return this.generateExpression(e,t,n[0]);throw new Error("No valid inputs for node")}case E.ASSIGNMENT:xe("ASSIGNMENT nodes should not be used as expressions");default:xe(`${D[r.nodeType]} code generation not implemented yet`)}},generateBlock(e,t,n){const r=t.cfg.blockTypes[e];(Ne[r]||Ne[te.DEFAULT]).call(Ne,e,t,n)},createGetTextureCall(e,t){const n=t[0],r=t[1],{dag:i}=e,o=be(i,n.id).identifier+"_sampler",s=Ae(e,{baseType:W,dimension:1},o),a=[n,we(s.id,s.dimension,e),r],{id:u,dimension:l}=Ee(e,"textureSample",a,{overloads:[{params:[K.sampler2D,K.sampler,K.float2],returnType:K.float4}]});return{id:u,dimension:l}},instanceIdReference:()=>"instanceID"};const Ue="\n// Group 0: Filter Properties\nstruct FilterUniforms {\n canvasSize: vec2<f32>,\n texelSize: vec2<f32>,\n}\n\n// Group 1: Model Transform\nstruct ModelUniforms {\n uModelViewMatrix: mat4x4<f32>,\n}\n\n// Group 2: Camera and Projection\nstruct CameraUniforms {\n uProjectionMatrix: mat4x4<f32>,\n}\n\n@group(0) @binding(0) var<uniform> filterParams: FilterUniforms;\n@group(0) @binding(1) var tex0: texture_2d<f32>;\n@group(0) @binding(2) var tex0_sampler: sampler;\n@group(1) @binding(0) var<uniform> model: ModelUniforms;\n@group(2) @binding(0) var<uniform> camera: CameraUniforms;\n",Be=Ue+"\nstruct VertexInput {\n @location(0) aPosition: vec3<f32>,\n @location(1) aTexCoord: vec2<f32>,\n}\n\nstruct VertexOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) vTexCoord: vec2<f32>,\n}\n\n@vertex\nfn main(input: VertexInput) -> VertexOutput {\n var output: VertexOutput;\n\n // transferring texcoords for the frag shader\n output.vTexCoord = input.aTexCoord;\n\n // copy position with a fourth coordinate for projection (1.0 is normal)\n let positionVec4 = vec4<f32>(input.aPosition, 1.0);\n\n // project to 3D space\n output.position = camera.uProjectionMatrix * model.uModelViewMatrix * positionVec4;\n\n return output;\n}\n",ke=Ue+"\nstruct FilterInputs {\n texCoord: vec2<f32>,\n canvasSize: vec2<f32>,\n texelSize: vec2<f32>,\n}\n\nstruct FragmentInput {\n @location(0) vTexCoord: vec2<f32>,\n}\n\nstruct FragmentOutput {\n @location(0) color: vec4<f32>,\n}\n\n@fragment\nfn main(input: FragmentInput) -> FragmentOutput {\n var output: FragmentOutput;\n var inputs: FilterInputs;\n inputs.texCoord = input.vTexCoord;\n inputs.canvasSize = filterParams.canvasSize;\n inputs.texelSize = filterParams.texelSize;\n\n var outColor = HOOK_getColor(inputs, tex0, tex0_sampler);\n outColor = vec4<f32>(outColor.rgb * outColor.a, outColor.a);\n output.color = outColor;\n\n return output;\n}\n",ze="\nstruct Uniforms {\n uModelViewMatrix: mat4x4<f32>,\n uProjectionMatrix: mat4x4<f32>,\n uNormalMatrix: mat3x3<f32>,\n roughness: f32,\n};\n",Ge="\nconst PI = 3.14159265359;\n\nfn nTOE(v: vec3<f32>) -> vec2<f32> {\n // x = r sin(phi) cos(theta)\n // y = r cos(phi)\n // z = r sin(phi) sin(theta)\n let phi = acos(v.y);\n // if phi is 0, then there are no x, z components\n var theta = 0.0;\n // else\n theta = acos(v.x / sin(phi));\n let sinTheta = v.z / sin(phi);\n if (sinTheta < 0.0) {\n // Turn it into -theta, but in the 0-2PI range\n theta = 2.0 * PI - theta;\n }\n theta = theta / (2.0 * PI);\n let phiNorm = phi / PI;\n\n return vec2<f32>(phiNorm, theta);\n}\n\nfn random(p: vec2<f32>) -> f32 {\n let p3 = fract(vec3<f32>(p.x, p.y, p.x) * 0.1031);\n let dotP3 = dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n",$e=`\nstruct VertexInput {\n @location(0) aPosition: vec3<f32>,\n @location(1) aNormal: vec3<f32>,\n @location(2) aTexCoord: vec2<f32>,\n}\n\nstruct VertexOutput {\n @builtin(position) Position: vec4<f32>,\n @location(0) localPos: vec3<f32>,\n @location(1) vWorldNormal: vec3<f32>,\n @location(2) vWorldPosition: vec3<f32>,\n @location(3) vTexCoord: vec2<f32>,\n}\n\n${ze}\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\n\n@vertex\nfn main(input: VertexInput) -> VertexOutput {\n var output: VertexOutput;\n\n // Multiply the position by the matrix\n let viewModelPosition = uniforms.uModelViewMatrix * vec4<f32>(input.aPosition, 1.0);\n output.Position = uniforms.uProjectionMatrix * viewModelPosition;\n\n // Orient the normals and pass to the fragment shader\n output.vWorldNormal = uniforms.uNormalMatrix * input.aNormal;\n\n // Send the view position to the fragment shader\n output.vWorldPosition = viewModelPosition.xyz;\n\n output.localPos = output.vWorldPosition;\n output.vTexCoord = input.aTexCoord;\n\n return output;\n}\n`,je=`\nstruct FragmentInput {\n @location(0) localPos: vec3<f32>,\n @location(3) vTexCoord: vec2<f32>,\n}\n\n${ze}\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\n@group(0) @binding(1) var environmentMap: texture_2d<f32>;\n@group(0) @binding(2) var environmentMap_sampler: sampler;\n\n${Ge}\n\n@fragment\nfn main(input: FragmentInput) -> @location(0) vec4<f32> {\n // The sample direction equals the hemisphere's orientation\n let phi = input.vTexCoord.x * 2.0 * PI;\n let theta = input.vTexCoord.y * PI;\n let x = sin(theta) * cos(phi);\n let y = sin(theta) * sin(phi);\n let z = cos(theta);\n let normal = vec3<f32>(x, y, z);\n\n // Discretely sampling the hemisphere given the integral's\n // spherical coordinates translates to the following fragment code:\n var irradiance = vec3<f32>(0.0);\n let up = vec3<f32>(0.0, 1.0, 0.0);\n let right = normalize(cross(up, normal));\n let upNorm = normalize(cross(normal, right));\n\n // We specify a fixed sampleDelta delta value to traverse\n // the hemisphere; decreasing or increasing the sample delta\n // will increase or decrease the accuracy respectively.\n let sampleDelta = 0.100;\n var nrSamples = 0.0;\n let randomOffset = random(input.vTexCoord) * sampleDelta;\n\n for (var rawPhi = 0.0; rawPhi < 2.0 * PI; rawPhi += sampleDelta) {\n let phiSample = rawPhi + randomOffset;\n for (var rawTheta = 0.0; rawTheta < 0.5 * PI; rawTheta += sampleDelta) {\n let thetaSample = rawTheta + randomOffset;\n // spherical to cartesian (in tangent space) // tangent space to world // add each sample result to irradiance\n let xSample = sin(thetaSample) * cos(phiSample);\n let ySample = sin(thetaSample) * sin(phiSample);\n let zSample = cos(thetaSample);\n let tangentSample = vec3<f32>(xSample, ySample, zSample);\n\n let sampleVec = tangentSample.x * right + tangentSample.y * upNorm + tangentSample.z * normal;\n let envSample = textureSample(environmentMap, environmentMap_sampler, nTOE(sampleVec));\n irradiance += envSample.xyz * cos(thetaSample) * sin(thetaSample);\n nrSamples += 1.0;\n }\n }\n // divide by the total number of samples taken, giving us the average sampled irradiance.\n irradiance = PI * irradiance * (1.0 / nrSamples);\n\n return vec4<f32>(irradiance, 1.0);\n}\n`,He=`\nstruct FragmentInput {\n @location(0) localPos: vec3<f32>,\n @location(3) vTexCoord: vec2<f32>,\n}\n\n${ze}\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\n@group(0) @binding(1) var environmentMap: texture_2d<f32>;\n@group(0) @binding(2) var environmentMap_sampler: sampler;\n\n${Ge}\n\nfn VanDerCorput(nIn: i32, base: i32) -> f32 {\n // Use the bit manipulation version for WebGPU (equivalent to WEBGL2 version)\n var n = u32(nIn);\n n = (n << 16u) | (n >> 16u);\n n = ((n & 0x55555555u) << 1u) | ((n & 0xAAAAAAAAu) >> 1u);\n n = ((n & 0x33333333u) << 2u) | ((n & 0xCCCCCCCCu) >> 2u);\n n = ((n & 0x0F0F0F0Fu) << 4u) | ((n & 0xF0F0F0F0u) >> 4u);\n n = ((n & 0x00FF00FFu) << 8u) | ((n & 0xFF00FF00u) >> 8u);\n return f32(n) * 2.3283064365386963e-10; // / 0x100000000\n}\n\nfn HammersleyNoBitOps(i: i32, N: i32) -> vec2<f32> {\n return vec2<f32>(f32(i) / f32(N), VanDerCorput(i, 2));\n}\n\nfn ImportanceSampleGGX(Xi: vec2<f32>, N: vec3<f32>, roughness: f32) -> vec3<f32> {\n let a = roughness * roughness;\n\n let phi = 2.0 * PI * Xi.x;\n let cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a * a - 1.0) * Xi.y));\n let sinTheta = sqrt(1.0 - cosTheta * cosTheta);\n\n // from spherical coordinates to cartesian coordinates\n var H: vec3<f32>;\n H.x = cos(phi) * sinTheta;\n H.y = sin(phi) * sinTheta;\n H.z = cosTheta;\n\n // from tangent-space vector to world-space sample vector\n let up = select(vec3<f32>(0.0, 0.0, 1.0), vec3<f32>(1.0, 0.0, 0.0), abs(N.z) < 0.999);\n let tangent = normalize(cross(up, N));\n let bitangent = cross(N, tangent);\n\n let sampleVec = tangent * H.x + bitangent * H.y + N * H.z;\n return normalize(sampleVec);\n}\n\n@fragment\nfn main(input: FragmentInput) -> @location(0) vec4<f32> {\n let SAMPLE_COUNT = 400i; // 4096\n let lowRoughnessLimit = i32(pow(2.0, (uniforms.roughness + 0.1) * 20.0));\n var totalWeight = 0.0;\n var prefilteredColor = vec3<f32>(0.0);\n let phi = input.vTexCoord.x * 2.0 * PI;\n let theta = input.vTexCoord.y * PI;\n let x = sin(theta) * cos(phi);\n let y = sin(theta) * sin(phi);\n let z = cos(theta);\n let N = vec3<f32>(x, y, z);\n let V = N;\n\n for (var i = 0i; i < SAMPLE_COUNT; i++) {\n // break at smaller sample numbers for low roughness levels\n if (i == lowRoughnessLimit) {\n break;\n }\n let Xi = HammersleyNoBitOps(i, SAMPLE_COUNT);\n let H = ImportanceSampleGGX(Xi, N, uniforms.roughness);\n let L = normalize(2.0 * dot(V, H) * H - V);\n\n let NdotL = max(dot(N, L), 0.0);\n // Always sample the texture to maintain uniform control flow\n let envSample = textureSample(environmentMap, environmentMap_sampler, nTOE(L));\n // Only add to accumulators if NdotL > 0\n if (NdotL > 0.0) {\n prefilteredColor += envSample.xyz * NdotL;\n totalWeight += NdotL;\n }\n }\n prefilteredColor = prefilteredColor / totalWeight;\n\n return vec4<f32>(prefilteredColor, 1.0);\n}\n`,We=0,qe=1,Ke=2;function Ye(e,n){const{lineDefs:r}=function(e){const t={},n={};let r="";const i=function(n,i){r+=e(`STROKE_CAP_${n}`,`${i}`,"u32"),t[P[n]]=i},o=function(t,i){r+=e(`STROKE_JOIN_${t}`,`${i}`,"u32"),n[P[t]]=i};return i("ROUND",0),i("PROJECT",1),i("SQUARE",2),o("ROUND",0),o("MITER",1),o("BEVEL",2),{STROKE_CAP_ENUM:t,STROKE_JOIN_ENUM:n,lineDefs:r}}(((e,t,n)=>`const ${e}: ${n} = ${t};\n`)),{Renderer3D:i,Shader:o,Texture:s,MipmapTexture:a,Image:u,Camera:A,RGBA:I}=e;class L extends i{constructor(e,t,n,r,i){super(e,t,n,r,i),this.activeRenderPass=null,this.activeRenderPassEncoder=null,this.activeShaderOptions=null,this.activeShader=null,this.samplers=new Map,this.uniformBufferAlignment=256,this.activeUniformBuffers=[],this.currentUniformBuffer=void 0,this.uniformBufferPool=[],this.resettingUniformBuffers=[],this.dynamicEntryOffsets=new Uint32Array(64),this.currentCanvasColorTexture=null,this.currentCanvasColorTextureView=null,this.pixelReadBuffer=null,this.pixelReadBufferSize=0,this.strandsBackend=Me,this._shadersWithPools=[],this._geometriesWithPools=[],this._hasPendingDraws=!1,this._pendingCommandEncoders=[],this._postSubmitCallbacks=[],this._retiredBuffers=[],this._pixelReadCanvas=null,this._pixelReadCtx=null,this.mainFramebuffer=null,this._frameState=We,this.finalCamera=new A(this),this.finalCamera._computeCameraDefaultSettings(),this.finalCamera._setDefaultCamera(),this.depthFormat="depth24plus-stencil8",this.depthTexture=null,this.depthTextureView=null}async setupContext(){this._setAttributeDefaults(this._pInst),await this._initContext()}_setAttributeDefaults(e){const t={forceFallbackAdapter:!1,powerPreference:"high-performance"};null===e._webgpuAttributes?e._webgpuAttributes=t:e._webgpuAttributes=Object.assign(t,e._webgpuAttributes)}async _initContext(){if(this.adapter=await(navigator.gpu?.requestAdapter(this._webgpuAttributes)),this.device=await(this.adapter?.requestDevice({requiredFeatures:["depth32float-stencil8"]})),!this.device)throw new Error("Your browser does not support WebGPU.");this.queue=this.device.queue,this.drawingContext=this.canvas.getContext("webgpu"),this.presentationFormat=navigator.gpu.getPreferredCanvasFormat(),this.drawingContext.configure({device:this.device,format:this.presentationFormat,usage:GPUTextureUsage.RENDER_ATTACHMENT|GPUTextureUsage.COPY_SRC,alphaMode:"premultiplied"}),this.mainFramebuffer=this.createFramebuffer({_useCanvasFormat:!0}),this._updateSize(),this._update(),this.flushDraw()}async _setAttributes(t,n){if(void 0===this._pInst._webgpuAttributes)return void console.log("You are trying to use setAttributes on a p5.Graphics object that does not use a WebGPU renderer.");let r=!0;void 0!==n?(null===this._pInst._webgpuAttributes&&(this._pInst._webgpuAttributes={}),this._pInst._webgpuAttributes[t]!==n&&(this._webgpuAttributes[t]=n,r=!1)):t instanceof Object&&this._pInst._webgpuAttributes!==t&&(this._pInst._webgpuAttributes=t,r=!1),this.isP3D&&!r&&(!this._pInst._setupDone&&this.geometryBufferCache.numCached()>0?e._friendlyError("Sorry, Could not set the attributes, you need to call setAttributes() before calling the other drawing methods in setup()"):(await this._resetContext(null,null,L),this.states.curCamera&&(this.states.curCamera._renderer=this._renderer)))}_updateSize(){if(this.device&&this.depthFormat){if(this.depthTexture&&this.depthTexture.destroy){this.flushDraw();const e=this.depthTexture;this._postSubmitCallbacks.push((()=>e.destroy())),this.depthTextureView=null}this.depthTexture=this.device.createTexture({size:{width:Math.ceil(this.width*this._pixelDensity),height:Math.ceil(this.height*this._pixelDensity),depthOrArrayLayers:1},format:this.depthFormat,usage:GPUTextureUsage.RENDER_ATTACHMENT|GPUTextureUsage.COPY_SRC}),this.depthTextureView=this.depthTexture.createView(),this.clear()}}_getCanvasColorTextureView(){const e=this.drawingContext.getCurrentTexture();return this.currentCanvasColorTexture!==e&&(this.currentCanvasColorTexture=e,this.currentCanvasColorTextureView=e.createView()),this.currentCanvasColorTextureView}_beginActiveRenderPass(){if(this.activeRenderPass)return;const e=this.activeFramebuffer(),t={view:e?e.aaColorTexture?e.aaColorTextureView:e.colorTextureView:this._getCanvasColorTextureView(),loadOp:"load",storeOp:"store",resolveTarget:e&&e.aaColorTexture?e.colorTextureView:void 0},n=e?e.aaDepthTexture?e.aaDepthTextureView:e.depthTextureView:this.depthTextureView,r={colorAttachments:[t],depthStencilAttachment:n?{view:n,depthLoadOp:"load",depthStoreOp:"store",depthClearValue:1,stencilLoadOp:"load",stencilStoreOp:"store",depthReadOnly:!1,stencilReadOnly:!1}:void 0},i=this.device.createCommandEncoder(),o=i.beginRenderPass(r);this.activeRenderPassEncoder=i,this.activeRenderPass=o}_finishActiveRenderPass(){if(!this.activeRenderPass)return;const e=this.activeRenderPassEncoder;this.activeRenderPass.end(),this._pendingCommandEncoders.push(e.finish()),this.activeRenderPassEncoder=null,this.activeRenderPass=null,this.activeShader=null,this.activeShaderOptions=null}clear(...e){if(!this.device||!this.drawingContext)return;const t=e[0]||0,n=e[1]||0,r=e[2]||0,i=e[3]||0;this._frameState!==We||this.activeFramebuffer()||(this._frameState=qe),this._finishActiveRenderPass();const o=this.device.createCommandEncoder(),s=this.activeFramebuffer(),a={view:s?s.aaColorTexture?s.aaColorTextureView:s.colorTextureView:this._getCanvasColorTextureView(),clearValue:{r:t*i,g:n*i,b:r*i,a:i},loadOp:"clear",storeOp:"store",resolveTarget:s&&s.aaColorTexture?s.colorTextureView:void 0},u=s?s.aaDepthTexture?s.aaDepthTextureView:s.depthTextureView:this.depthTextureView,l=u?{view:u,depthClearValue:1,depthLoadOp:"clear",depthStoreOp:"store",stencilLoadOp:"load",stencilStoreOp:"store"}:void 0,p={colorAttachments:[a],...l?{depthStencilAttachment:l}:{}};o.beginRenderPass(p).end(),this._pendingCommandEncoders.push(o.finish()),this._hasPendingDraws=!0}clearDepth(e=1){if(!this.device||!this.depthTextureView)return;this._finishActiveRenderPass();const t=this.device.createCommandEncoder(),n=this.activeFramebuffer(),r=n?n.aaDepthTexture?n.aaDepthTextureView:n.depthTextureView:this.depthTextureView;if(!r)return;const i={colorAttachments:[],depthStencilAttachment:{view:r,depthClearValue:e,depthLoadOp:"clear",depthStoreOp:"store",stencilLoadOp:"load",stencilStoreOp:"store"}};t.beginRenderPass(i).end(),this._pendingCommandEncoders.push(t.finish()),this._hasPendingDraws=!0}_prepareBuffer(e,t,n){const r=n.attributes[e.attr];if(!r)return;const{src:i,dst:o,size:s,map:a}=e,u=this.device,l=this._getOrMakeCachedBuffers(t);let p=t[i];if(!p||0===p.length){if(!e.default)return;p=t[i]=e.default(t),p.isDefault=!0}if(!l[o]||!1!==t.dirtyFlags[i]){const e=a?a(p):p,n=this._normalizeBufferData(e,Float32Array),r=this._getVertexBufferFromPool(t,o,n.byteLength),s=new n.constructor(n);r.dataCopy=s,u.queue.writeBuffer(r.buffer,0,s),l[o]=r.buffer,t.dirtyFlags[i]=!1}n.enableAttrib(r,s)}_disableRemainingAttributes(e){}_enableAttrib(e){const t=e.location;this.registerEnabled.has(t)||this.registerEnabled.add(t)}_ensureGeometryBuffers(e,t,n){if(!t)return;const r=this.device.createBuffer({size:4*Math.ceil(t.length*n.BYTES_PER_ELEMENT/4),usage:GPUBufferUsage.INDEX|GPUBufferUsage.COPY_DST,mappedAtCreation:!0});new n(r.getMappedRange()).set(t),r.unmap(),e.indexBuffer=r,e.indexBufferType=n===Uint32Array?"uint32":"uint16"}_freeBuffers(e){const t=e=>{e&&e.destroy&&e.destroy()};t(e.indexBuffer);const n=n=>{for(const r of n)t(e[r.dst]),e[r.dst]=null};n(this.buffers.stroke),n(this.buffers.fill),n(this.buffers.user)}_getValidSampleCount(e){return e<=1?1:4}_shaderOptions({mode:e}){const t=this.activeFramebuffer(),n=t?this._getWebGPUColorFormat(t):this.presentationFormat,r=t&&t.antialias?t.antialiasSamples:1,i=this._getValidSampleCount(r),o=t&&t.useDepth?this._getWebGPUDepthFormat(t):this.depthFormat,s=this.drawTarget(),a=this._clipping,u=s._isClipApplied;return{topology:5===e?"triangle-strip":"triangle-list",blendMode:this.states.curBlendMode,sampleCount:i,format:n,depthFormat:o,clipping:a,clipApplied:u}}_shaderOptionsDifferent(e){if(!this.activeShaderOptions)return!0;for(const t in this.activeShaderOptions)if(this.activeShaderOptions[t]!==e[t])return!0;return!1}_initShader(e){const t=this.device;e.vertModule=t.createShaderModule({code:e.vertSrc()}),e.fragModule=t.createShaderModule({code:e.fragSrc()}),e._pipelineCache=new Map,e.getPipeline=({topology:n,blendMode:r,sampleCount:i,format:o,depthFormat:s,clipping:a,clipApplied:u})=>{const l=`${n}_${r}_${i}_${o}_${s}_${a}_${u}`;if(!e._pipelineCache.has(l)){const p=t.createRenderPipeline({layout:e._pipelineLayout,vertex:{module:e.vertModule,entryPoint:"main",buffers:this._getVertexLayout(e)},fragment:{module:e.fragModule,entryPoint:"main",targets:[{format:o,blend:this._getBlendState(r)}]},primitive:{topology:n},multisample:{count:i},depthStencil:{format:s,depthWriteEnabled:!a,depthCompare:"less-equal",stencilFront:{compare:a?"always":u?"not-equal":"always",failOp:"keep",depthFailOp:"keep",passOp:a?"replace":"keep"},stencilBack:{compare:a?"always":u?"not-equal":"always",failOp:"keep",depthFailOp:"keep",passOp:a?"replace":"keep"},stencilReadMask:255,stencilWriteMask:a?255:0}});e._pipelineCache.set(l,p)}return e._pipelineCache.get(l)}}_finalizeShader(e){e._uniformBufferGroups=[],e.buffersDirty=new Set;for(const t of e._uniformGroups){const n=Object.values(t.uniforms),r=Math.max(0,...n.map((e=>e.offsetEnd))),i=16*Math.ceil(r/16);e._uniformBufferGroups.push({group:t.group,binding:t.binding,cacheKey:1e3*t.group+t.binding,varName:t.varName,structType:t.structType,uniforms:n,size:i,bufferPool:[],nextBufferPool:[],dynamic:n.some((e=>e.name.startsWith("uModel"))),buffersInUse:new Set,currentBuffer:null})}this._shadersWithPools.push(e);const t=new Map,n=new Map,r=new Map;for(const t of e._uniformBufferGroups){const e=r.get(t.group)||[];e.push({bufferGroup:t,binding:t.binding,visibility:GPUShaderStage.VERTEX|GPUShaderStage.FRAGMENT,buffer:{type:"uniform",hasDynamicOffset:t.dynamic}}),r.set(t.group,e)}for(const[e,t]of r.entries())t.sort(((e,t)=>e.binding-t.binding)),n.set(e,t);for(const t of e.samplers){const e=t.group,r=n.get(e)||[];if(!["sampler","texture_2d<f32>"].includes(t.type))throw new Error(`Unsupported texture type: ${t.type}`);r.push({binding:t.binding,visibility:t.visibility,sampler:"sampler"===t.type?{type:"filtering"}:void 0,texture:"texture_2d<f32>"===t.type?{sampleType:"float",viewDimension:"2d"}:void 0,uniform:t}),r.sort(((e,t)=>e.binding-t.binding)),n.set(e,r)}const i=[];for(const[e,r]of n){const n=this.device.createBindGroupLayout({entries:r});t.set(e,n),i.push([e,r])}e._groupEntries=i,e._bindGroupLayouts=[...t.values()],e._cachedBindGroup={},e._lastDynamicBuffer={},e._pipelineLayout=this.device.createPipelineLayout({bindGroupLayouts:e._bindGroupLayouts})}_getBlendState(e){switch(e){case l:return{color:{operation:"add",srcFactor:"one",dstFactor:"one-minus-src-alpha"},alpha:{operation:"add",srcFactor:"one",dstFactor:"one-minus-src-alpha"}};case f:return{color:{operation:"add",srcFactor:"one",dstFactor:"one"},alpha:{operation:"add",srcFactor:"one",dstFactor:"one"}};case p:return{color:{operation:"add",srcFactor:"zero",dstFactor:"one-minus-src-alpha"},alpha:{operation:"add",srcFactor:"zero",dstFactor:"one-minus-src-alpha"}};case g:return{color:{operation:"add",srcFactor:"dst-color",dstFactor:"one-minus-src-alpha"},alpha:{operation:"add",srcFactor:"dst-alpha",dstFactor:"one-minus-src-alpha"}};case x:return{color:{operation:"add",srcFactor:"one",dstFactor:"one-minus-src-color"},alpha:{operation:"add",srcFactor:"one",dstFactor:"one-minus-src-alpha"}};case h:return{color:{operation:"add",srcFactor:"one-minus-dst-color",dstFactor:"one-minus-src-color"},alpha:{operation:"add",srcFactor:"one",dstFactor:"one"}};case v:return{color:{operation:"add",srcFactor:"one",dstFactor:"zero"},alpha:{operation:"add",srcFactor:"one",dstFactor:"zero"}};case m:return{color:{operation:"reverse-subtract",srcFactor:"one",dstFactor:"one"},alpha:{operation:"add",srcFactor:"one",dstFactor:"one-minus-src-alpha"}};case c:return{color:{operation:"min",srcFactor:"one",dstFactor:"one"},alpha:{operation:"min",srcFactor:"one",dstFactor:"one"}};case d:return{color:{operation:"max",srcFactor:"one",dstFactor:"one"},alpha:{operation:"max",srcFactor:"one",dstFactor:"one"}};default:return void console.warn(`Unsupported blend mode: ${e}`)}}_applyColorBlend(){}_getVertexLayout(e){const t=[];for(const n in e.attributes){const r=e.attributes[n];if(!r||-1===r.location)continue;const i=this.buffers[e.shaderType].find((e=>e.attr===n))||this.buffers.user.find((e=>e.attr===n));if(!i)continue;const{size:o}=i,s=this._getFormatFromSize(o);t.push({arrayStride:4*o,stepMode:"vertex",attributes:[{shaderLocation:r.location,offset:0,format:s}]})}return t}_getVertexBuffers(e){if(!e._vertexBuffers){const t=[];for(const n in e.attributes){const r=e.attributes[n];if(!r||-1===r.location)continue;const i=this.buffers[e.shaderType].find((e=>e.attr===n))||this.buffers.user.find((e=>e.attr===n));i&&t.push(i)}e._vertexBuffers=t}return e._vertexBuffers}_getFormatFromSize(e){switch(e){case 1:return"float32";case 2:return"float32x2";case 3:return"float32x3";case 4:return"float32x4";default:throw new Error(`Unsupported attribute size: ${e}`)}}_useShader(e,t){}_updateViewport(){this._origViewport={width:this.width,height:this.height},this._viewport=[0,0,this.width,this.height]}_createPixelsArray(){this.pixels=new Uint8Array(this.width*this.pixelDensity()*this.height*this.pixelDensity()*4)}viewport(){}zClipRange(){return[0,1]}defaultNearScale(){return.01}defaultFarScale(){return 100}_resetBuffersBeforeDraw(){this._finishActiveRenderPass(),this._frameState=We;const e=this.activeFramebuffer(),t=this.device.createCommandEncoder(),n=e?e.aaDepthTexture?e.aaDepthTextureView:e.depthTextureView:this.depthTextureView;if(n){const e={colorAttachments:[],depthStencilAttachment:{view:n,depthClearValue:1,depthLoadOp:"clear",depthStoreOp:"store",stencilLoadOp:"load",stencilStoreOp:"store"}};t.beginRenderPass(e).end(),this._pendingCommandEncoders.push(t.finish()),this._hasPendingDraws=!0}}_promoteToFramebuffer(){if(this._frameState===Ke)return;if(this.activeFramebuffer())return;this.flushDraw(),this._frameState=Ke;const e=this.drawingContext.getCurrentTexture();this.mainFramebuffer.width===this.width&&this.mainFramebuffer.height===this.height||this.mainFramebuffer.resize(this.width,this.height);const t=this.device.createCommandEncoder();t.copyTextureToTexture({texture:e,origin:{x:0,y:0,z:0},mipLevel:0},{texture:this.mainFramebuffer.colorTexture,origin:{x:0,y:0,z:0},mipLevel:0},{width:Math.ceil(this.width*this._pixelDensity),height:Math.ceil(this.height*this._pixelDensity),depthOrArrayLayers:1}),t.copyTextureToTexture({texture:this.depthTexture,origin:{x:0,y:0,z:0},mipLevel:0},{texture:this.mainFramebuffer.depthTexture,origin:{x:0,y:0,z:0},mipLevel:0},{width:Math.ceil(this.width*this._pixelDensity),height:Math.ceil(this.height*this._pixelDensity),depthOrArrayLayers:1}),this._pendingCommandEncoders.push(t.finish()),this._hasPendingDraws=!0;const n=this.states.uModelMatrix.copy();this.mainFramebuffer.defaultCamera.set(this.states.curCamera),this.mainFramebuffer.begin(),this.states.uModelMatrix.set(n)}_promoteToFramebufferWithoutCopy(){this.mainFramebuffer.width===this.width&&this.mainFramebuffer.height===this.height||this.mainFramebuffer.resize(this.width,this.height),this._frameState=Ke,this.flushDraw();const e=this.states.uModelMatrix.copy();this.mainFramebuffer.defaultCamera.set(this.states.curCamera),this.mainFramebuffer.begin(),this.states.uModelMatrix.set(e)}_initializeGeometryBufferPools(e){e._vertexBufferPools||(e._vertexBufferPools={},e._vertexBuffersInUse={},e._vertexBuffersToReturn={},this._geometriesWithPools.push(e))}_getVertexBufferFromPool(e,t,n){this._initializeGeometryBufferPools(e),e._vertexBufferPools[t]||(e._vertexBufferPools[t]=[]),e._vertexBuffersInUse[t]||(e._vertexBuffersInUse[t]=[]),e._vertexBuffersToReturn[t]||(e._vertexBuffersToReturn[t]=[]);const r=e._vertexBufferPools[t];if(r.length>0){const i=r.pop();if(i.buffer.size>=n)return e._vertexBuffersInUse[t].push(i),i;this._retiredBuffers.push(i.buffer)}const i={buffer:this.device.createBuffer({size:n,usage:GPUBufferUsage.VERTEX|GPUBufferUsage.COPY_DST}),size:n,dataCopy:null};return e._vertexBuffersInUse[t].push(i),i}_returnVertexBuffersToPool(){for(const e of this._geometriesWithPools)if(e._vertexBuffersToReturn)for(const[t,n]of Object.entries(e._vertexBuffersToReturn))if(n.length>0){const r=e._vertexBufferPools[t]||[];for(;n.length>0;){const e=n.pop();e.dataCopy=null,r.push(e)}e._vertexBufferPools[t]=r}}onReset(e){this._markGeometryBuffersForReturn(e)}_markGeometryBuffersForReturn(e){if(e._vertexBuffersInUse&&e._vertexBuffersToReturn)for(const[t,n]of Object.entries(e._vertexBuffersInUse))if(n.length>0){const r=e._vertexBuffersToReturn[t]||[];for(;n.length>0;){const e=n.pop();r.push(e)}e._vertexBuffersToReturn[t]=r}}_getUniformBufferFromPool(e){if(e.bufferPool.length>0){const t=e.bufferPool.pop();return e.buffersInUse.add(t),t}const t=this.device.createBuffer({size:e.size,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),n=new Float32Array(e.size/4),r={buffer:t,data:n,dataView:new DataView(n.buffer)};return e.buffersInUse.add(r),r}_getDynamicUniformBufferFromPool(e){let t;if(this.currentUniformBuffer&&this.currentUniformBuffer.offset+e.size<this.currentUniformBuffer.size)t=this.currentUniformBuffer;else if(this.uniformBufferPool.length>0)t=this.uniformBufferPool.pop(),this.activeUniformBuffers.push(t);else{const e=10240;t={dynamic:!0,lastOffset:0,offset:0,size:e,buffer:this.device.createBuffer({size:e,usage:GPUBufferUsage.MAP_WRITE|GPUBufferUsage.COPY_SRC,mappedAtCreation:!0}),uniformBuffer:this.device.createBuffer({size:e,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST})},t.data=new Float32Array(t.buffer.getMappedRange()),t.dataView=new DataView(t.data.buffer),this.activeUniformBuffers.push(t)}return this.currentUniformBuffer=t,t}_returnUniformBuffersToPool(){for(const e of this._shadersWithPools)this._returnShaderBuffersToPool(e)}_returnShaderBuffersToPool(e){if(e._uniformBufferGroups)for(const t of e._uniformBufferGroups){for(;t.nextBufferPool.length>0;)t.bufferPool.push(t.nextBufferPool.pop());for(const e of t.buffersInUse.keys())e!==t.currentBuffer&&t.nextBufferPool.push(e);t.buffersInUse.clear(),t.currentBuffer&&t.buffersInUse.add(t.currentBuffer)}}flushDraw(){if(this._finishActiveRenderPass(),this._hasPendingDraws){const e=this._pendingCommandEncoders;if(this._pendingCommandEncoders=[],this._hasPendingDraws=!1,this.activeUniformBuffers.length>0){const t=this.device.createCommandEncoder();for(const e of this.activeUniformBuffers)e.buffer.unmap(),t.copyBufferToBuffer(e.buffer,e.uniformBuffer);e.unshift(t.finish())}this.queue.submit(e);for(const e of this.activeUniformBuffers)e.offset=0,e.lastOffset=0,e.buffer.mapAsync(GPUMapMode.WRITE).then((()=>(e.data=new Float32Array(e.buffer.getMappedRange()),e.dataView=new DataView(e.data.buffer),this.uniformBufferPool.push(e),e)));if(this.activeUniformBuffers=[],this.currentUniformBuffer=void 0,this._postSubmitCallbacks.length>0){const e=this._postSubmitCallbacks;this._postSubmitCallbacks=[],this.device.queue.onSubmittedWorkDone().then((()=>{for(const t of e)t()}))}this.currentCanvasColorTexture=null,this.currentCanvasColorTextureView=null}}_ensurePixelReadCanvas(e,t){return this._pixelReadCanvas||(this._pixelReadCanvas=document.createElement("canvas"),this._pixelReadCtx=this._pixelReadCanvas.getContext("2d")),this._pixelReadCanvas.width===e&&this._pixelReadCanvas.height===t||(this._pixelReadCanvas.width=e,this._pixelReadCanvas.height=t),{canvas:this._pixelReadCanvas,ctx:this._pixelReadCtx}}resize(e,t){super.resize(e,t),this._hasPendingDraws=!0,this.flushDraw()}async finishDraw(){this.flushDraw();const e=[];if(this._frameState===Ke){for(;this.activeFramebuffers.length>0;){const t=this.activeFramebuffers.pop();e.unshift({fbo:t,diff:{...this.states}})}this.flushDraw(),this._pInst.push(),this.states.setValue("enableLighting",!1),this.states.setValue("activeImageLight",null),this._pInst.setCamera(this.finalCamera),this._pInst.shader(this._getBlitShader()),this._pInst.resetMatrix(),this._pInst.imageMode(this._pInst.CENTER),this._pInst.image(this.mainFramebuffer,0,0),this._pInst.pop(),this.flushDraw()}this._returnUniformBuffersToPool();for(const e of this._geometriesWithPools)this._markGeometryBuffersForReturn(e);this.resettingUniformBuffers=[],this._returnVertexBuffersToPool();const t=this._retiredBuffers;if(this._postSubmitCallbacks.push((()=>{for(const e of t)e&&e.destroy&&e.destroy()})),this._retiredBuffers=[],this._frameState===Ke)for(const{fbo:t,diff:n}of e){t===this.mainFramebuffer&&this._frameState===Ke||t.begin();for(const e in n)this.states.setValue(e,n[e])}}_drawBuffers(e,{mode:t=4,count:n=1}){const r=this.geometryBufferCache.getCached(e);if(!r)return;this._frameState!==We||this.activeFramebuffer()||this._promoteToFramebufferWithoutCopy(),this._beginActiveRenderPass();const i=this.activeRenderPass,o=this._curShader,s=this._shaderOptions({mode:t});(this.activeShader!==o||this._shaderOptionsDifferent(s))&&i.setPipeline(o.getPipeline(s)),this.activeShader=o,this.activeShaderOptions=s;this.drawTarget()._isClipApplied&&!this._clipping?i.setStencilReference(0):this._clipping&&i.setStencilReference(1);for(const e of o._vertexBuffers||this._getVertexBuffers(o)){const t=o.attributes[e.attr].location,n=r[e.dst];i.setVertexBuffer(t,n,0)}for(const e of o._uniformBufferGroups)if(e.dynamic){const t=this._getDynamicUniformBufferFromPool(e);o._lastDynamicBuffer[e.cacheKey]!==t&&(o._cachedBindGroup[e.group]=void 0,o._lastDynamicBuffer[e.cacheKey]=t),this._packUniformGroup(o,e.uniforms,t),t.lastOffset=t.offset,t.offset+=Math.ceil(e.size/this.uniformBufferAlignment)*this.uniformBufferAlignment,e.currentDynamicBuffer=t,e.lastOffset=t.lastOffset}else{let t;!this._hasGroupDataChanged(o,e)&&e.currentBuffer?(t=e.currentBuffer,e.buffersInUse.add(t)):(t=this._getUniformBufferFromPool(e),this._packUniformGroup(o,e.uniforms,t),this.device.queue.writeBuffer(t.buffer,0,t.data.buffer,t.data.byteOffset,t.data.byteLength),o.buffersDirty.delete(1e3*e.group+e.binding),o._cachedBindGroup[e.group]=void 0,e.currentBuffer=t)}for(const e of o.samplers){const t=1e3*e.group+e.binding;o.buffersDirty.has(t)&&(o._cachedBindGroup[e.group]=void 0,o.buffersDirty.delete(t))}for(const e of o._groupEntries){const t=e[0],n=e[1];let r=0;const s=[];let a=o._cachedBindGroup[t];for(const e of n){const t=e.bufferGroup,n=t?.currentBuffer||t?.currentDynamicBuffer;n?(t.dynamic&&(this.dynamicEntryOffsets[r++]=t.lastOffset),a||s.push({binding:e.binding,resource:t.dynamic?{buffer:n.uniformBuffer,offset:0,size:Math.ceil(t.size/this.uniformBufferAlignment)*this.uniformBufferAlignment}:{buffer:n.buffer}})):a||s.push({binding:e.binding,resource:"sampler"===e.uniform.type?(e.uniform.textureSource.texture||this._getEmptyTexture()).getSampler():(e.uniform.texture||this._getEmptyTexture()).textureHandle.view})}const u=o._bindGroupLayouts[t];a||(a=this.device.createBindGroup({layout:u,entries:s})),o._cachedBindGroup[t]=a,0===r?i.setBindGroup(t,a):i.setBindGroup(t,a,this.dynamicEntryOffsets,0,r)}if("fill"===o.shaderType)if(r.indexBuffer){const t=r.indexFormat||"uint16";i.setIndexBuffer(r.indexBuffer,t),i.drawIndexed(3*e.faces.length,n,0,0,0)}else i.draw(e.vertices.length,n,0,0);else if("text"===o.shaderType){if(!r.indexBuffer)throw new Error("Text geometry must have an index buffer");const t=r.indexFormat||"uint16";i.setIndexBuffer(r.indexBuffer,t),i.drawIndexed(3*e.faces.length,n,0,0,0)}r.lineVerticesBuffer&&"stroke"===o.shaderType&&i.draw(e.lineVertices.length/3,n,0,0),this._hasPendingDraws=!0}_packUniformGroup(e,t,n){const r=n.data,i=n.dataView,o=n.offset||0;for(const n of t){const t=e.uniforms[n.name];if(!t||t.isSampler)continue;const s=t._mappedData;if("u32"===t.baseType)if(4===t.size)i.setUint32(o+t.offset,s,!0);else for(let e=0;e<s.length;e++)i.setUint32(o+t.offset+4*e,s[e],!0);else if("i32"===t.baseType)if(4===t.size)i.setInt32(o+t.offset,s,!0);else for(let e=0;e<s.length;e++)i.setInt32(o+t.offset+4*e,s[e],!0);else if(t.packInPlace){const e=(o+t.offset)/4;r[e+0]=s[0],r[e+1]=s[1],r[e+2]=s[2],r[e+4]=s[3],r[e+5]=s[4],r[e+6]=s[5],r[e+8]=s[6],r[e+9]=s[7],r[e+10]=s[8]}else 4===t.size?r.set([s],(o+t.offset)/4):void 0!==s&&r.set(s,(o+t.offset)/4)}}_hasGroupDataChanged(e,t){return!t.currentBuffer||e.buffersDirty.has(1e3*t.group+t.binding)}_parseStruct(e,t){const n=e.match(new RegExp(`struct\\s+${t}\\s*\\{([^\\}]+)\\}`));if(!n)throw new Error(`Can't find a struct definition for ${t}`);const r=n[1],i={};let o,s=0,a=0;const u=/(?:@location\((\d+)\)\s+)?(\w+):\s*([^\n]+?),?\n/g,l=e=>{if(["f32","i32","u32","bool"].includes(e))return{align:4,size:4,items:1,baseType:e};if(/^vec[2-4](<f32>|f)$/.test(e)){const t=parseInt(e.match(/^vec([2-4])/)[1]);return{align:2===t?8:16,size:4*t,items:t,baseType:"f32"}}if(/^vec[2-4]<(i32|u32)>$/.test(e)){const t=parseInt(e.match(/^vec([2-4])/)[1]);return{align:2===t?8:16,size:4*t,items:t,baseType:e.match(/^vec[2-4]<(i32|u32)>$/)[1]}}if(/^mat[2-4](?:x[2-4])?(<f32>|f)$/.test(e)){if("x"===e[4]&&e[3]!==e[5])throw new Error("Non-square matrices not implemented yet");const t=parseInt(e[3]),n=2===t?8:16;return{align:n,size:Math.ceil(4*t/n)*n*t,pack:3===t?e=>[...e.slice(0,3),...e.slice(3,6),...e.slice(6,9)]:void 0,packInPlace:3===t,items:t*t,baseType:"f32"}}if(/^array<.+>$/.test(e)){const[,t,n]=e.match(/^array<(.+),\s*(\d+)>/),r=parseInt(n),{align:i,size:o,items:s,pack:a=e=>[...e],baseType:u}=l(t),p=Math.ceil(o/i)*i;return{align:i,size:p*r,items:s*r,pack:e=>{const t=[];for(let n=0;n<e.length;n+=s){const r=a(e.slice(n,s));t.push(...r);for(let e=0;e<p/4-r.length;e++)t.push(0)}return t},baseType:u}}throw new Error(`Unknown type in WGSL struct: ${e}`)};for(;null!==(o=u.exec(r));){const[e,t,n,r]=o,{size:u,align:p,pack:f,packInPlace:c,baseType:d}=l(r);a=Math.ceil(a/p)*p;const m=a+u;i[n]={name:n,location:t?parseInt(t):void 0,index:s,type:r,size:u,offset:a,offsetEnd:m,pack:f,packInPlace:c,baseType:d},s++,a=m}return i}_mapUniformData(e,t){return e.pack?e.pack(t):t}_getShaderAttributes(e){const t=/fn main\(.+:\s*([^\s\)]+)/.exec(e._vertSrc);if(!t)throw new Error("Can't find `fn main` in vertex shader source");const n=t[1];return this._parseStruct(e.vertSrc(),n)}getUniformMetadata(e){const t=[],n=/@group\((\d+)\)\s+@binding\((\d+)\)\s+var<uniform>\s+(\w+)\s*:\s*(\w+);/g;let r;for(;null!==(r=n.exec(e.vertSrc()));){const[n,i,o,s,a]=r,u=parseInt(o),l=this._parseStruct(e.vertSrc(),a);t.push({group:parseInt(i),binding:u,varName:s,structType:a,uniforms:l})}if(0===t.length)throw new Error("Expected at least one uniform struct bound to @group(0)");const i={};for(const e of t)for(const[t,n]of Object.entries(e.uniforms))i[t]={...n,group:e.group,binding:e.binding,varName:e.varName};e._uniformGroups=t;const o={},s=/@group\((\d+)\)\s*@binding\((\d+)\)\s*var\s+(\w+)\s*:\s*(texture_2d<f32>|sampler);/g,a={};for(const e of t)a[e.group+","+e.binding]=!0;for(const[t,n]of[[e.vertSrc(),GPUShaderStage.VERTEX],[e.fragSrc(),GPUShaderStage.FRAGMENT]]){let e;for(;null!==(e=s.exec(t));){const[t,r,i,s,u]=e,l=parseInt(r),p=parseInt(i);if(a[l+","+p])continue;const f=`${l},${p}`;o[f]={visibility:(o[f]?.visibility||0)|n,group:l,binding:p,name:s,type:u,isSampler:!0,noData:"sampler"===u}}for(const e of Object.values(o))if(e.type.startsWith("texture")){const t=e.name+"_sampler",n=Object.values(o).find((e=>e.name===t));if(!n)throw new Error(`Every shader texture needs an accompanying sampler. Could not find sampler ${t} for texture ${e.name}`);n.textureSource=e}}return[...Object.values(i).sort(((e,t)=>e.index-t.index)),...Object.values(o)]}getNextBindingIndex({vert:e,frag:t},n=0){const r=/@group\((\d+)\)\s*@binding\((\d+)\)\s*var(?:<uniform>)?\s+(\w+)\s*:\s*(texture_2d<f32>|sampler|uniform|\w+)/g;let i=-1;for(const[o,s]of[[e,GPUShaderStage.VERTEX],[t,GPUShaderStage.FRAGMENT]]){let e;for(;null!==(e=r.exec(o));){const[t,r,o]=e;parseInt(r)===n&&(i=Math.max(i,parseInt(o)))}}return i+1}updateUniformValue(e,t,n){t.isSampler?t.texture=n instanceof s?n:this.getTexture(n):t._mappedData=this._mapUniformData(t,t._cachedData),e.buffersDirty.add(1e3*t.group+t.binding)}_updateTexture(e,t){t.update()}bindTexture(e){}unbindTexture(e){}_unbindFramebufferTexture(e){}createTexture({width:e,height:t,format:n="rgba8unorm",usage:r}){const i=this.device.createTexture({size:[e,t],format:n,usage:r||GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT});return{gpuTexture:i,view:i.createView()}}uploadTextureFromSource({gpuTexture:e},t){this.queue.copyExternalImageToTexture({source:t},{texture:e},[t.width,t.height]),this._hasPendingDraws=!0,this.flushDraw()}uploadTextureFromData({gpuTexture:e},t,n,r){this.queue.writeTexture({texture:e},t,{bytesPerRow:4*n,rowsPerImage:r},{width:n,height:r,depthOrArrayLayers:1}),this._hasPendingDraws=!0,this.flushDraw()}setTextureParams(e){}getSampler(e){const t=`${e.minFilter}_${e.magFilter}_${e.wrapS}_${e.wrapT}`;if(this.samplers.has(t))return this.samplers.get(t);const n={[y]:"nearest",[T]:"linear",[C]:"clamp-to-edge",[b]:"repeat",[_]:"mirror-repeat"},r=this.device.createSampler({magFilter:n[e.magFilter],minFilter:n[e.minFilter],addressModeU:n[e.wrapS],addressModeV:n[e.wrapT]});return this.samplers.set(t,r),r}bindTextureToShader(e,t,n,r){}deleteTexture({gpuTexture:e}){this._postSubmitCallbacks.push((()=>e.destroy()))}_getLightShader(){return this._defaultLightShader||(this._defaultLightShader=new o(this,le,pe,{vertex:{"void beforeVertex":"() {}","Vertex getObjectInputs":"(inputs: Vertex) { return inputs; }","Vertex getWorldInputs":"(inputs: Vertex) { return inputs; }","Vertex getCameraInputs":"(inputs: Vertex) { return inputs; }","void afterVertex":"() {}"},fragment:{"void beforeFragment":"() {}","Inputs getPixelInputs":"(inputs: Inputs) { return inputs; }","vec4f combineColors":"(components: ColorComponents) {\n var rgb = vec3<f32>(0.0);\n rgb += components.diffuse * components.baseColor;\n rgb += components.ambient * components.ambientColor;\n rgb += components.specular * components.specularColor;\n rgb += components.emissive;\n return vec4<f32>(rgb, components.opacity);\n }","vec4f getFinalColor":"(color: vec4<f32>) { return color; }","void afterFragment":"() {}"}})),this._defaultLightShader}_getColorShader(){return this._defaultColorShader||(this._defaultColorShader=new o(this,re,ie,{vertex:{"void beforeVertex":"() {}","Vertex getObjectInputs":"(inputs: Vertex) { return inputs; }","Vertex getWorldInputs":"(inputs: Vertex) { return inputs; }","Vertex getCameraInputs":"(inputs: Vertex) { return inputs; }","void afterVertex":"() {}"},fragment:{"void beforeFragment":"() {}","vec4<f32> getFinalColor":"(color: vec4<f32>) { return color; }","void afterFragment":"() {}"}})),this._defaultColorShader}_getLineShader(){return this._defaultLineShader||(this._defaultLineShader=new o(this,r+se,r+ae,{vertex:{"void beforeVertex":"() {}","StrokeVertex getObjectInputs":"(inputs: StrokeVertex) { return inputs; }","StrokeVertex getWorldInputs":"(inputs: StrokeVertex) { return inputs; }","StrokeVertex getCameraInputs":"(inputs: StrokeVertex) { return inputs; }","void afterVertex":"() {}"},fragment:{"void beforeFragment":"() {}","Inputs getPixelInputs":"(inputs: Inputs) { return inputs; }","vec4<f32> getFinalColor":"(color: vec4<f32>) { return color; }","bool shouldDiscard":"(outside: bool) { return outside; };","void afterFragment":"() {}"}})),this._defaultLineShader}_getFontShader(){return this._defaultFontShader||(this._defaultFontShader=new o(this,ce,de)),this._defaultFontShader}_getBlitShader(){return this._defaultBlitShader||(this._defaultBlitShader=new o(this,he,ge)),this._defaultBlitShader}_adjustDimensions(e,t){return{adjustedWidth:e,adjustedHeight:t}}_applyClip(){const e=this.device.createCommandEncoder(),t=this.activeFramebuffer(),n=t?t.aaDepthTexture?t.aaDepthTextureView:t.depthTextureView:this.depthTextureView;if(!n)return;const r={colorAttachments:[],depthStencilAttachment:{view:n,stencilLoadOp:"clear",stencilStoreOp:"store",stencilClearValue:0,depthReadOnly:!0,stencilReadOnly:!1}};e.beginRenderPass(r).end(),this._pendingCommandEncoders.push(e.finish()),this._hasPendingDraws=!0}_unapplyClip(){}_clearClipBuffer(){this._finishActiveRenderPass();const e=this.device.createCommandEncoder(),t=this.activeFramebuffer(),n=t?t.aaDepthTexture?t.aaDepthTextureView:t.depthTextureView:this.depthTextureView;if(!n)return;const r={colorAttachments:[],depthStencilAttachment:{view:n,stencilLoadOp:"clear",stencilStoreOp:"store",stencilClearValue:1,depthReadOnly:!0,stencilReadOnly:!1}};e.beginRenderPass(r).end(),this._pendingCommandEncoders.push(e.finish()),this._hasPendingDraws=!0}_applyStencilTestIfClipping(){}uniformNameFromHookKey(e){return e.slice(0,e.indexOf(":"))}populateHooks(e,t,n){if(!t.includes("fn main"))return t;t=t.replace(/\/\/ @p5 (ifdef|ifndef) (\w+)\s+(\w+)\n((?:(?!\/\/ @p5)(?:.|\n))*)\/\/ @p5 endif/g,((t,n,r,i,o)=>{const s="ifdef"===n;return(!!e.hooks.modified.vertex[`${r} ${i}`]||!!e.hooks.modified.fragment[`${r} ${i}`])===s?o:""}));let[r,i,o]=t.split(/((?:@(?:vertex|fragment)\s*)?fn main[^{]+\{)/);"fragment"!==n&&(i.match(/\@builtin\s*\(\s*instance_index\s*\)/)||(i=i.replace(/\)\s*(->|\{)/,", @builtin(instance_index) instanceID: u32) $1")));let s="";for(const t in e.hooks.uniforms)t.endsWith(": sampler2D")||(s+=` ${t},\n`);if(s){const t=`\n// Hook Uniforms (from .modify())\nstruct HookUniforms {\n${s}}\n\n@group(0) @binding(${this.getNextBindingIndex({vert:"vertex"===n?r+(e.hooks.vertex?.declarations??"")+e.hooks.declarations:e._vertSrc,frag:"fragment"===n?r+(e.hooks.fragment?.declarations??"")+e.hooks.declarations:e._fragSrc},0)}) var<uniform> hooks: HookUniforms;\n`;r=r.replace(/(@group\(0\)\s+@binding)/,`${t}\n$1`)}if(e.hooks.varyingVariables&&e.hooks.varyingVariables.length>0){let t=this._getNextAvailableLocation(r,n),i="";for(const n of e.hooks.varyingVariables){const e=`@location(${t++}) ${n},`;i+=e+"\n"}"vertex"===n?r=r.replace(/struct\s+VertexOutput\s+\{([^}]*)\}/,((e,t)=>`struct VertexOutput {${t}\n${i}}`)):"fragment"===n&&(r=r.replace(/struct\s+FragmentInput\s+\{([^}]*)\}/,((e,t)=>`struct FragmentInput {${t}\n${i}}`)))}if(e.hooks.varyingVariables&&e.hooks.varyingVariables.length>0){let t="";for(const n of e.hooks.varyingVariables){const[e,r]=n.split(":").map((e=>e.trim()));t+=`var<private> ${e}: ${r};\n`}if(r+=t,"vertex"===n){let t="";for(const n of e.hooks.varyingVariables){const[e]=n.split(":").map((e=>e.trim()));t+=` OUTPUT_VAR.${e} = ${e};\n`}const n=o.match(/return\s+(\w+)\s*;/);if(n){const e=n[1];t=t.replace(/OUTPUT_VAR/g,e),o=o.replace(/(return\s+\w+\s*;)/g,`${t} $1`)}}else if("fragment"===n){let t="";for(const n of e.hooks.varyingVariables){const[e]=n.split(":").map((e=>e.trim()));t+=` ${e} = INPUT_VAR.${e};\n`}const n=i.match(/fn main\s*\((\w+):\s*\w+\)/);if(n){const e=n[1];t=t.replace(/INPUT_VAR/g,e),o=t+o}}}let a="",u="";e.hooks.declarations&&(a+=e.hooks.declarations+"\n"),e.hooks[n].declarations&&(a+=e.hooks[n].declarations+"\n");for(const t in e.hooks.helpers){const[n,r]=t.split(" "),[i,o,s]=/^(\([^\)]*\))((?:.|\n)*)$/.exec(e.hooks.helpers[t]);a+="void"===n?`fn ${r}${o}${s}\n`:`fn ${r}${o} -> ${n}${s}\n`}for(const t in e.hooks[n]){if("declarations"===t)continue;const[r,i]=t.split(" ");u+=`const AUGMENTED_HOOK_${i} = ${e.hooks.modified[n][t]?"true":"false"};\n`;let[o,s,l]=/^(\([^\)]*\))((?:.|\n)*)$/.exec(e.hooks[n][t]);if("fragment"!==n){let e=!!s.match(/^\(\s*\S+.*\)$/);s=s.slice(0,-1)+(e?", ":"")+"instanceID: u32)"}a+="void"===r?`fn HOOK_${i}${s}${l}\n`:`fn HOOK_${i}${s} -> ${r}${l}\n`}if("fragment"!==n){const e=e=>{let t,n=e,r=0;do{if(t=/HOOK_\w+\(/.exec(n.slice(r)),t){r+=t.index+t[0].length-1;let e=0,i=!1;for(;r<n.length&&("("===n[r]?e++:")"===n[r]?e--:n[r].match(/\S/)&&(i=!0),r++,0!==e););const o=(i?", ":"")+"instanceID";n=n.slice(0,r-1)+o+n.slice(r-1),r+=o.length}}while(t);return n};r=e(r),o=e(o)}return r+"\n"+u+a+i+o}_getNextAvailableLocation(e,t){let n=-1;const r="vertex"===t?"VertexOutput":"FragmentInput",i=e.match(new RegExp(`struct\\s+${r}\\s*\\{([^}]*)\\}`,"s"));if(i){const e=i[1].matchAll(/@location\((\d+)\)/g);for(const t of e){const e=parseInt(t[1]);e>n&&(n=e)}}return n+1}getShaderHookTypes(e,t){const n={f32:K.float1,"vec2<f32>":K.float2,"vec3<f32>":K.float3,"vec4<f32>":K.float4,vec2f:K.float2,vec3f:K.float3,vec4f:K.float4,i32:K.int1,"vec2<i32>":K.int2,"vec3<i32>":K.int3,"vec4<i32>":K.int4,bool:K.bool1,"vec2<bool>":K.bool2,"vec3<bool>":K.bool3,"vec4<bool>":K.bool4,"mat2x2<f32>":K.mat2,"mat3x3<f32>":K.mat3,"mat4x4<f32>":K.mat4,"texture_2d<f32>":K.sampler2D};let r=e._vertSrc,i=e.hooks.vertex[t];if(i||(i=e.hooks.fragment[t],r=e._fragSrc),!i)throw new Error(`Can't find hook ${t}!`);const o=t.split(/\s+/g),s=o.pop(),a=o.pop(),u=[...o],l=/\(([^\)]*)\)/.exec(i);if(!l)throw new Error(`Couldn't find function parameters in hook body:\n${i}`);const p=e=>{const t=new RegExp(`struct\\s+${e}\\s*{([^}]*)}`).exec(r);if(!t)return;const i=[];for(const e of t[1].split(",")){const t=e.trim();if(!t)continue;const r=/(?:@location\([^)]*\)\s*)?(\w+)\s*:\s*([^,\s]+)/.exec(t);if(!r)continue;const o=r[1];let s=r[2];const a=n[s]||null,u=p(s);i.push({name:o,type:{typeName:s,qualifiers:[],properties:u,dataType:a}})}return i},f=l[1].split(",").map((e=>{const t=e.trim();if(!t)return null;const r=t.split(":").map((e=>e.trim()));if(2!==r.length)return null;const i=r[0];let o=r[1];o.includes("texture_2d")&&(o="texture_2d<f32>");const s=n[o]||null;return{name:i,type:{typeName:o,qualifiers:[],properties:p(o),dataType:s}}})).filter(Boolean),c=n[a]||null;return{name:s,returnType:{typeName:a,qualifiers:u,properties:p(a),dataType:c},parameters:f}}_ensurePixelReadBuffer(e){if(!this.pixelReadBuffer||this.pixelReadBufferSize<e){this.pixelReadBuffer&&(this.flushDraw(),this.pixelReadBuffer.destroy());const t=Math.max(e,2*this.pixelReadBufferSize);this.pixelReadBuffer=this.device.createBuffer({size:t,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ}),this.pixelReadBufferSize=t}return this.pixelReadBuffer}_alignBytesPerRow(e){return 256*Math.ceil(e/256)}defaultFramebufferAlpha(){return!0}defaultFramebufferAntialias(){return!0}supportsFramebufferAntialias(){return!0}createFramebufferResources(e){}validateFramebufferFormats(e){[O,S,F].includes(e.format)||(console.warn("Unknown Framebuffer format. Please use UNSIGNED_BYTE, FLOAT, or HALF_FLOAT. Defaulting to UNSIGNED_BYTE."),e.format=O),e.useDepth&&![w,S].includes(e.depthFormat)&&(console.warn("Unknown Framebuffer depth format. Please use UNSIGNED_INT or FLOAT. Defaulting to FLOAT."),e.depthFormat=S)}recreateFramebufferTextures(e){if(this.flushDraw(),e.colorTexture&&e.colorTexture.destroy){const t=e.colorTexture;this._postSubmitCallbacks.push((()=>t.destroy())),e.colorTextureView=null}if(e.aaColorTexture&&e.aaColorTexture.destroy){const t=e.aaColorTexture;this._postSubmitCallbacks.push((()=>t.destroy())),e.aaColorTextureView=null}if(e.depthTexture&&e.depthTexture.destroy){const t=e.depthTexture;this._postSubmitCallbacks.push((()=>t.destroy())),e.depthTextureView=null}if(e.aaDepthTexture&&e.aaDepthTexture.destroy){const t=e.aaDepthTexture;this._postSubmitCallbacks.push((()=>t.destroy())),e.aaDepthTextureView=null}const t={size:{width:e.width*e.density,height:e.height*e.density,depthOrArrayLayers:1},format:this._getWebGPUColorFormat(e)},n={...t,usage:GPUTextureUsage.RENDER_ATTACHMENT|GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_SRC|(e._useCanvasFormat?GPUTextureUsage.COPY_DST:0),sampleCount:1};if(e.colorTexture=this.device.createTexture(n),e.colorTextureView=e.colorTexture.createView(),e.antialias){const n={...t,usage:GPUTextureUsage.RENDER_ATTACHMENT,sampleCount:this._getValidSampleCount(e.antialiasSamples)};e.aaColorTexture=this.device.createTexture(n),e.aaColorTextureView=e.aaColorTexture.createView()}if(e.useDepth){const t={size:{width:e.width*e.density,height:e.height*e.density,depthOrArrayLayers:1},format:this._getWebGPUDepthFormat(e)},n={...t,usage:GPUTextureUsage.RENDER_ATTACHMENT|GPUTextureUsage.TEXTURE_BINDING|(e._useCanvasFormat?GPUTextureUsage.COPY_DST:0),sampleCount:1};if(e.depthTexture=this.device.createTexture(n),e.depthTextureView=e.depthTexture.createView(),e.antialias){const n={...t,usage:GPUTextureUsage.RENDER_ATTACHMENT,sampleCount:this._getValidSampleCount(e.antialiasSamples)};e.aaDepthTexture=this.device.createTexture(n),e.aaDepthTextureView=e.aaDepthTexture.createView()}}this._clearFramebufferTextures(e)}_clearFramebufferTextures(e){this._finishActiveRenderPass();const t=this.device.createCommandEncoder(),n={colorAttachments:[{view:e.aaColorTexture?e.aaColorTextureView:e.colorTextureView,loadOp:"clear",storeOp:"store",clearValue:{r:0,g:0,b:0,a:0},resolveTarget:e.aaColorTexture?e.colorTextureView:void 0}],depthStencilAttachment:e.aaDepthTexture||e.depthTexture?{view:e.aaDepthTexture?e.aaDepthTextureView:e.depthTextureView,depthLoadOp:"clear",depthStoreOp:"store",depthClearValue:1,stencilLoadOp:"clear",stencilStoreOp:"store",depthReadOnly:!1,stencilReadOnly:!1}:void 0};t.beginRenderPass(n).end(),this._pendingCommandEncoders.push(t.finish()),this._hasPendingDraws=!0}_getFramebufferColorTextureView(e){return e.colorTexture?e.colorTextureView:null}createFramebufferTextureHandle(e){const t=e;let n=this;return{get view(){return n._getFramebufferColorTextureView(t.framebuffer)},get gpuTexture(){return t.framebuffer.colorTexture}}}_getWebGPUColorFormat(e){return e.format===S?(e.channels,"rgba32float"):e.format===F?(e.channels,"rgba16float"):e._useCanvasFormat?this.presentationFormat:(e.channels,"rgba8unorm")}_getWebGPUDepthFormat(e){return e._useCanvasFormat?this.depthFormat:e.useStencil?e.depthFormat===S?"depth32float-stencil8":"depth24plus-stencil8":e.depthFormat===S?"depth32float":"depth24plus"}_deleteFramebufferTexture(e){this.flushDraw();const t=e.rawTexture();if(t.texture&&t.texture.destroy){const e=t.texture;this._postSubmitCallbacks.push((()=>e.destroy()))}this.textures.delete(e)}deleteFramebufferTextures(e){this._deleteFramebufferTexture(e.color),e.depth&&this._deleteFramebufferTexture(e.depth)}deleteFramebufferResources(e){if(this.flushDraw(),e.colorTexture&&e.colorTexture.destroy){const t=e.colorTexture;this._postSubmitCallbacks.push((()=>t.destroy()))}if(e.depthTexture&&e.depthTexture.destroy){const t=e.depthTexture;this._postSubmitCallbacks.push((()=>t.destroy()))}if(e.aaDepthTexture&&e.aaDepthTexture.destroy){const t=e.aaDepthTexture;this._postSubmitCallbacks.push((()=>t.destroy()))}}getFramebufferToBind(e){}updateFramebufferTexture(e,t){}bindFramebuffer(e){}framebufferYScale(){return 1}async readFramebufferPixels(e){this.flushDraw();const t=e.width*e.density,n=e.height*e.density,r=4*t,i=this._alignBytesPerRow(r),o=i*n,s=this.device.createBuffer({size:o,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ}),a=this.device.createCommandEncoder();a.copyTextureToBuffer({texture:e.colorTexture,origin:{x:0,y:0,z:0},mipLevel:0,aspect:"all"},{buffer:s,bytesPerRow:i,rowsPerImage:n},{width:t,height:n,depthOrArrayLayers:1}),this.device.queue.submit([a.finish()]),await s.mapAsync(GPUMapMode.READ,0,o);const u=s.getMappedRange(0,o);let l;if(i===r)l=new Uint8Array(u.slice(0,t*n*4)),s.unmap();else{l=new Uint8Array(t*n*4);const e=new Uint8Array(u);for(let t=0;t<n;t++){const n=t*i,o=t*r;l.set(e.subarray(n,n+r),o)}s.unmap()}return this._ensurePixelsAreRGBA(e,l),l}async readFramebufferPixel(e,t,n){this.flushDraw();const r=this._alignBytesPerRow(4),i=r,o=this._ensurePixelReadBuffer(i),s=this.device.createCommandEncoder();s.copyTextureToBuffer({texture:e.colorTexture,origin:{x:t,y:n,z:0}},{buffer:o,bytesPerRow:r},{width:1,height:1,depthOrArrayLayers:1}),this.device.queue.submit([s.finish()]),await o.mapAsync(GPUMapMode.READ,0,i);const a=o.getMappedRange(0,i),u=new Uint8Array(a),l=[u[0],u[1],u[2],u[3]];return this._ensurePixelsAreRGBA(e,l),o.unmap(),l}async readFramebufferRegion(e,t,n,r,i){this.flushDraw();const o=r*e.density,s=i*e.density,a=4*o,l=this._alignBytesPerRow(a),p=l*s,f=this._ensurePixelReadBuffer(p),c=this.device.createCommandEncoder();c.copyTextureToBuffer({texture:e.colorTexture,mipLevel:0,origin:{x:t*e.density,y:n*e.density,z:0}},{buffer:f,bytesPerRow:l},{width:o,height:s,depthOrArrayLayers:1}),this.device.queue.submit([c.finish()]),await f.mapAsync(GPUMapMode.READ,0,p);const d=f.getMappedRange(0,p);let m;if(l===a)m=new Uint8Array(d.slice(0,o*s*4));else{m=new Uint8Array(o*s*4);const e=new Uint8Array(d);for(let t=0;t<s;t++){const n=t*l,r=t*a;m.set(e.subarray(n,n+a),r)}}this._ensurePixelsAreRGBA(e,m);const h=new u(o,s);return h.imageData=h.canvas.getContext("2d").createImageData(o,s),h.imageData.data.set(m),h.pixels=h.imageData.data,h.updatePixels(),1!==e.density&&h.pixelDensity(e.density),f.unmap(),h}updateFramebufferPixels(e){const t=e.width*e.density,n=e.height*e.density,r=t*n*4;if(!e.pixels||e.pixels.length!==r)throw new Error("The pixels array has not been set correctly. Please call loadPixels() before updatePixels().");this.device.queue.writeTexture({texture:e.colorTexture},e.pixels,{bytesPerRow:4*t,rowsPerImage:n},{width:t,height:n,depthOrArrayLayers:1})}_ensurePixelsAreRGBA(e,t){e._useCanvasFormat&&"bgra8unorm"===this.presentationFormat&&this._convertBGRtoRGB(t)}_convertBGRtoRGB(e){for(let t=0;t<e.length;t+=4){const n=e[t];e[t]=e[t+2],e[t+2]=n}}async loadPixels(){this._promoteToFramebuffer(),await this.mainFramebuffer.loadPixels(),this.pixels=this.mainFramebuffer.pixels.slice()}async get(e,t,n,r){return this._promoteToFramebuffer(),this.mainFramebuffer.get(e,t,n,r)}filter(...e){return this.activeFramebuffer()||this._promoteToFramebuffer(),super.filter(...e)}getNoiseShaderSnippet(){return"fn mod289Vec3(x: vec3<f32>) -> vec3<f32> {\n return x - floor(x * (1.0 / 289.0)) * 289.0;\n}\n\nfn mod289Vec4(x: vec4<f32>) -> vec4<f32> {\n return x - floor(x * (1.0 / 289.0)) * 289.0;\n}\n\nfn permute(x: vec4<f32>) -> vec4<f32> {\n return mod289Vec4(((x*34.0)+10.0)*x);\n}\n\nfn taylorInvSqrt(r: vec4<f32>) -> vec4<f32> {\n return vec4<f32>(1.79284291400159) - vec4<f32>(0.85373472095314) * r;\n}\n\nfn baseNoise(v: vec3<f32>) -> f32 {\n let C = vec2<f32>(1.0/6.0, 1.0/3.0);\n let D = vec4<f32>(0.0, 0.5, 1.0, 2.0);\n\n // First corner\n var i = floor(v + dot(v, C.yyy));\n let x0 = v - i + dot(i, C.xxx);\n\n // Other corners\n let g = step(x0.yzx, x0.xyz);\n let l = vec3<f32>(1.0) - g;\n let i1 = min(g.xyz, l.zxy);\n let i2 = max(g.xyz, l.zxy);\n\n // x0 = x0 - 0.0 + 0.0 * C.xxx;\n // x1 = x0 - i1 + 1.0 * C.xxx;\n // x2 = x0 - i2 + 2.0 * C.xxx;\n // x3 = x0 - 1.0 + 3.0 * C.xxx;\n let x1 = x0 - i1 + C.xxx;\n let x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y\n let x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y\n\n // Permutations\n i = mod289Vec3(i);\n let p = permute( permute( permute(\n i.z + vec4<f32>(0.0, i1.z, i2.z, 1.0 ))\n + i.y + vec4<f32>(0.0, i1.y, i2.y, 1.0 ))\n + i.x + vec4<f32>(0.0, i1.x, i2.x, 1.0 ));\n\n // Gradients: 7x7 points over a square, mapped onto an octahedron.\n // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)\n let n_ = 0.142857142857; // 1.0/7.0\n let ns = n_ * D.wyz - D.xzx;\n\n let j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)\n\n let x_ = floor(j * ns.z);\n let y_ = floor(j - 7.0 * x_ ); // mod(j,N)\n\n let x = x_ *ns.x + ns.yyyy;\n let y = y_ *ns.x + ns.yyyy;\n let h = vec4<f32>(1.0) - abs(x) - abs(y);\n\n let b0 = vec4<f32>( x.xy, y.xy );\n let b1 = vec4<f32>( x.zw, y.zw );\n\n //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;\n //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;\n let s0 = floor(b0)*2.0 + vec4<f32>(1.0);\n let s1 = floor(b1)*2.0 + vec4<f32>(1.0);\n let sh = -step(h, vec4<f32>(0.0));\n\n let a0 = b0.xzyw + s0.xzyw*sh.xxyy;\n let a1 = b1.xzyw + s1.xzyw*sh.zzww;\n\n let p0 = vec3<f32>(a0.xy, h.x);\n let p1 = vec3<f32>(a0.zw, h.y);\n let p2 = vec3<f32>(a1.xy, h.z);\n let p3 = vec3<f32>(a1.zw, h.w);\n\n //Normalise gradients\n let norm = taylorInvSqrt(vec4<f32>(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));\n let p0_norm = p0 * norm.x;\n let p1_norm = p1 * norm.y;\n let p2_norm = p2 * norm.z;\n let p3_norm = p3 * norm.w;\n\n // Mix final noise value\n var m = max(vec4<f32>(0.5) - vec4<f32>(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), vec4<f32>(0.0));\n m = m * m;\n return 105.0 * dot( m*m, vec4<f32>( dot(p0_norm,x0), dot(p1_norm,x1),\n dot(p2_norm,x2), dot(p3_norm,x3) ) );\n}\n\nfn noise(st: vec3<f32>, octaves: i32, ampFalloff: f32) -> f32 {\n var result = 0.0;\n var amplitude = 1.0;\n var frequency = 1.0;\n\n for (var i = 0; i < 8; i++) {\n if (i >= octaves) { break; }\n result += amplitude * baseNoise(st * frequency);\n frequency *= 2.0;\n amplitude *= ampFalloff;\n }\n return (result + 1.0) * 0.5;\n}"}baseFilterShader(){return this._baseFilterShader||(this._baseFilterShader=new o(this,Be,ke,{vertex:{},fragment:{"vec4<f32> getColor":"(inputs: FilterInputs, canvasContent: texture_2d<f32>, canvasContent_sampler: sampler) -> vec4<f32> {\n return textureSample(tex, tex_sampler, inputs.texCoord);\n }"},hookAliases:{getColor:["filterColor"]}})),this._baseFilterShader}_createImageLightShader(e){if("diffused"===e)return this._pInst.createShader($e,je);if("specular"===e)return this._pInst.createShader($e,He);throw new Error(`Unknown imageLight shader type: ${e}`)}_createMipmapTexture(e){return new a(this,e,{})}_prepareMipmapData(e,t){const n={size:{width:e,height:e,depthOrArrayLayers:1},mipLevelCount:t,format:"rgba8unorm",usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT};return{gpuTexture:this.device.createTexture(n),size:e,mipLevels:t,format:"rgba8unorm"}}_accumulateMipLevel(e,t,n,r,i){this.flushDraw();const o=this.device.createCommandEncoder(),s=e.color.rawTexture().texture;o.copyTextureToTexture({texture:s,origin:{x:0,y:0,z:0}},{texture:t.gpuTexture,mipLevel:n,origin:{x:0,y:0,z:0}},{width:r,height:i,depthOrArrayLayers:1}),this.device.queue.submit([o.finish()])}_finalizeMipmapTexture(e){return new a(this,e,{})}createMipmapTextureHandle({gpuTexture:e,format:t,dataType:n,width:r,height:i}){return{texture:e,view:e.createView(),glFormat:t||"rgba8unorm",glDataType:n||"uint8"}}}e.RendererWebGPU=L,e.renderers[t]=e.RendererWebGPU,n.setAttributes=async function(e,t){return this._renderer._setAttributes(e,t)}}"undefined"!=typeof p5&&Ye(p5,p5.prototype)}();
|