p5
Version:
[](https://www.npmjs.com/package/p5)
2 lines • 149 kB
JavaScript
!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",c="lighter",f="darken",d="lighten",h="subtract",m="exclusion",g="multiply",x="screen",v="copy",y="linear",T="nearest",b="repeat",_="clamp",C="mirror",w="unsigned-byte",S="unsigned-int",O="float",A="half-float",F=Symbol("include"),I=Symbol("exclude"),P=Symbol("join");var E=Object.freeze({__proto__:null,ADD:c,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:_,CLOSE:"close",CONTAIN:"contain",CONTROL:"Control",CORNER:"corner",CORNERS:"corners",COVER:"cover",CROSS:"crosshair",CURVE:"curve",DARKEST:f,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:m,FALLBACK:"fallback",FILL:"fill",FLAT:"flat",FLOAT:O,FULL:"full",GRAY:"gray",GRID:"grid",HALF_FLOAT:A,HALF_PI:n,HAND:"pointer",HARD_LIGHT:"hard-light",IMAGE:"image",IMMEDIATE:"immediate",INCLUDE:F,INVERT:"invert",ITALIC:"italic",JOIN:P,LABEL:"label",LANDSCAPE:"landscape",LEFT:"left",LEFT_ARROW:"ArrowLeft",LIGHTEST:d,LINEAR:y,LINEAR_MIPMAP:"linear_mipmap",LINES:1,LINE_LOOP:2,LINE_STRIP:3,MIRROR:C,MITER:"miter",MOVE:"move",MULTIPLY:g,NEAREST:T,NORMAL:"normal",OPAQUE:"opaque",OPEN:"open",OPTION:"Alt",OVERLAY:"overlay",P2D:"p2d",P2DP3:"p2d-p3",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:h,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:w,UNSIGNED_INT:S,UP_ARROW:"ArrowUp",VERSION:"2.3.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 L={OPERATION:"operation",LITERAL:"literal",VARIABLE:"variable",CONSTANT:"constant",STRUCT:"struct",PHI:"phi",STATEMENT:"statement",ASSIGNMENT:"assignment"},D="_p5_instanceID",R="_p5_param_",M=Object.fromEntries(Object.entries(L).map((([e,t])=>[t,e]))),N={[L.OPERATION]:["opCode","dependsOn","dimension","baseType"],[L.LITERAL]:["value","dimension","baseType"],[L.VARIABLE]:["identifier","dimension","baseType"],[L.CONSTANT]:["value","dimension","baseType"],[L.STRUCT]:[""],[L.PHI]:["dependsOn","phiBlocks","dimension","baseType"],[L.STATEMENT]:["statementType"],[L.ASSIGNMENT]:["dependsOn"]},V="discard",B="break",U="early_return",k="expression",z="empty",$="float",G="int",j="bool",H="mat",W="defer",q="assign_on_use",K="sampler2D",Y="sampler",X={[$]:3,[G]:2,[j]:1,[H]:0,[W]:-1,[q]:-2,[K]:-10,[Y]:-11},Q={float1:{fnName:"float",baseType:$,dimension:1,priority:3},float2:{fnName:"vec2",baseType:$,dimension:2,priority:3},float3:{fnName:"vec3",baseType:$,dimension:3,priority:3},float4:{fnName:"vec4",baseType:$,dimension:4,priority:3},int1:{fnName:"int",baseType:G,dimension:1,priority:2},int2:{fnName:"ivec2",baseType:G,dimension:2,priority:2},int3:{fnName:"ivec3",baseType:G,dimension:3,priority:2},int4:{fnName:"ivec4",baseType:G,dimension:4,priority:2},bool1:{fnName:"bool",baseType:j,dimension:1,priority:1},bool2:{fnName:"bvec2",baseType:j,dimension:2,priority:1},bool3:{fnName:"bvec3",baseType:j,dimension:3,priority:1},bool4:{fnName:"bvec4",baseType:j,dimension:4,priority:1},mat2:{fnName:"mat2x2",baseType:H,dimension:2,priority:0},mat3:{fnName:"mat3x3",baseType:H,dimension:3,priority:0},mat4:{fnName:"mat4x4",baseType:H,dimension:4,priority:0},defer:{fnName:null,baseType:W,dimension:null,priority:-1},assign_on_use:{fnName:null,baseType:q,dimension:null,priority:-2},sampler2D:{fnName:"sampler2D",baseType:K,dimension:1,priority:-10},sampler:{fnName:"sampler",baseType:Y,dimension:1,priority:-11}};const J={FLOAT:{baseType:$,dimension:null,priority:3},INT:{baseType:G,dimension:null,priority:2},BOOL:{baseType:j,dimension:null,priority:1}};Object.fromEntries(Object.values(Q).filter((e=>null!==e.fnName)).map((e=>[e.fnName,e])));const Z={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,ARRAY_ACCESS:14},ee={LOGICAL_NOT:100,NEGATE:101,PLUS:102,SWIZZLE:103},te={FUNCTION_CALL:200,CONSTRUCTOR:201,TERNARY:202},ne=[{arity:"unary",boolean:!0,name:"not",symbol:"!",opCode:ee.LOGICAL_NOT},{arity:"unary",name:"neg",symbol:"-",opCode:ee.NEGATE},{arity:"unary",name:"plus",symbol:"+",opCode:ee.PLUS},{arity:"binary",name:"add",symbol:"+",opCode:Z.ADD},{arity:"binary",name:"sub",symbol:"-",opCode:Z.SUBTRACT},{arity:"binary",name:"mult",symbol:"*",opCode:Z.MULTIPLY},{arity:"binary",name:"div",symbol:"/",opCode:Z.DIVIDE},{arity:"binary",name:"mod",symbol:"%",opCode:Z.MODULO},{arity:"binary",boolean:!0,name:"equalTo",symbol:"==",opCode:Z.EQUAL},{arity:"binary",boolean:!0,name:"notEqual",symbol:"!=",opCode:Z.NOT_EQUAL},{arity:"binary",boolean:!0,name:"greaterThan",symbol:">",opCode:Z.GREATER_THAN},{arity:"binary",boolean:!0,name:"greaterEqual",symbol:">=",opCode:Z.GREATER_EQUAL},{arity:"binary",boolean:!0,name:"lessThan",symbol:"<",opCode:Z.LESS_THAN},{arity:"binary",boolean:!0,name:"lessEqual",symbol:"<=",opCode:Z.LESS_EQUAL},{arity:"binary",boolean:!0,name:"and",symbol:"&&",opCode:Z.LOGICAL_AND},{arity:"binary",boolean:!0,name:"or",symbol:"||",opCode:Z.LOGICAL_OR}],re={};for(const{symbol:e,opCode:t,name:n,arity:r,boolean:i}of ne)re[t]=e;const ie={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(ie).map((([e,t])=>[t,e])));const oe="\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",se=`\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${oe}\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`,ae=`\nstruct FragmentInput {\n @location(0) vVertexNormal: vec3<f32>,\n @location(1) vVertTexCoord: vec2<f32>,\n @location(2) vColor: vec4<f32>,\n};\n\n${oe}\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, input.vVertTexCoord);\n outColor = vec4<f32>(outColor.rgb * outColor.a, outColor.a);\n HOOK_afterFragment();\n return outColor;\n}\n`,ue="\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",le=`\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${ue}\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}`,pe=`\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${ue}\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, vec2<f32>(0.0, 0.0));\n col = vec4<f32>(col.rgb, 1.0) * col.a;\n HOOK_afterFragment();\n return vec4<f32>(col);\n}\n`,ce="\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",fe=`\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${ce}\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`,de=`\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${ce}\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), input.vTexCoord\n );\n outColor = vec4<f32>(outColor.rgb * outColor.a, outColor.a);\n HOOK_afterFragment();\n return outColor;\n}\n`,he="\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",me=`\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${he}\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`,ge=`\nstruct FragmentInput {\n @location(0) vTexCoord: vec2<f32>,\n};\n\n${he}\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`,xe="\nstruct Uniforms {\n uModelViewMatrix: mat4x4<f32>,\n uProjectionMatrix: mat4x4<f32>,\n};\n",ve=`\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${xe}\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`,ye=`\nstruct FragmentInput {\n @location(0) vTexCoord: vec2<f32>,\n};\n\n${xe}\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 Te(e){throw new Error(`[p5.strands internal error]: ${e}`)}function be(e,t){throw new Error(`[p5.strands ${e}]: ${t}`)}function _e(e,t,n){be("dimension mismatch",`Cannot assign a value of dimension ${t} to \`${n}\`, which expects dimension ${e}.`)}function Ce(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 we(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=N[t];2===n.length&&Te(`Required fields for node type '${M[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&&Te(`Missing fields ${r.join(", ")} for a node type '${M[t]}'.`)}(t),t}function Se(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 Oe(e,t){return{baseType:e.baseTypes[t],dimension:e.dimensions[t],priority:X[e.baseTypes[t]]}}function Ae(e,t,n){void 0===n&&Te("undefined nodeID in `recordInBasicBlock()`"),void 0===t&&Te("undefined blockID in `recordInBasicBlock()"),e.blockInstructions[t]=e.blockInstructions[t]||[],e.blockInstructions[t].push(n)}class Fe{constructor(e,t,n){this.id=e,this.strandsContext=n,this.dimension=t,this.structProperties=null,this._schema=null,this.isStrandsNode=!0;const r=Se(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 Ie(this.id,this.dimension,this.strandsContext)}typeInfo(){return{baseType:this._originalBaseType||$,dimension:this.dimension}}bridge(e){const{dag:t,cfg:n}=this.strandsContext,r=Se(t,this.id),i=r?.baseType??$;let o;if(e?.isStrandsNode)o=e.id;else{o=Me(this.strandsContext,{baseType:i,dimension:this.dimension},e).id}if(this._originalIdentifier){const r=e?.isStrandsNode?e.dimension:Array.isArray(e)?e.length:1;r!==this._originalDimension&&1!==r&&_e(this._originalDimension,r,this._originalIdentifier);const{id:i}=Le(this.strandsContext,{baseType:this._originalBaseType,dimension:this._originalDimension},this._originalIdentifier),s=Ce(t,we({nodeType:L.ASSIGNMENT,dependsOn:[i,o],phiBlocks:[]}));Ae(n,n.currentBlock,s);const a=Ce(t,we({nodeType:L.VARIABLE,baseType:this._originalBaseType,dimension:this._originalDimension,identifier:this._originalIdentifier}));this.id=a}else this.id=o;return this}bridgeSwizzle(e,t){const{dag:n,cfg:r}=this.strandsContext,i=Se(n,this.id),o=i?.baseType??$;let s;if(t?.isStrandsNode)s=t.id;else{s=Me(this.strandsContext,{baseType:o,dimension:this.dimension},t).id}if(this._originalIdentifier){const i=t?.isStrandsNode?t.dimension:Array.isArray(t)?t.length:1;i!==e.length&&1!==i&&_e(e.length,i,`${this._originalIdentifier}.${e}`);const{id:o}=Le(this.strandsContext,{baseType:this._originalBaseType,dimension:this._originalDimension},this._originalIdentifier),a=Ce(n,we({nodeType:L.OPERATION,opCode:ee.SWIZZLE,baseType:this._originalBaseType,dimension:e.length,swizzle:e,dependsOn:[o]})),u=Ce(n,we({nodeType:L.ASSIGNMENT,dependsOn:[a,s],phiBlocks:[]}));Ae(r,r.currentBlock,u);const l=Ce(n,we({nodeType:L.VARIABLE,baseType:this._originalBaseType,dimension:this._originalDimension,identifier:this._originalIdentifier}));this.id=l}else this.id=s;return this}getValue(){if(this._originalIdentifier){const{id:e,dimension:t}=Le(this.strandsContext,{baseType:this._originalBaseType,dimension:this._originalDimension},this._originalIdentifier);return Ie(e,t,this.strandsContext)}return this}get(e){if("storage"===Se(this.strandsContext.dag,this.id).baseType&&this._schema)return Be(this.strandsContext,this,e,this._schema);const{id:t,dimension:n}=function(e,t,n){const{dag:r,cfg:i}=e;let o;if(n instanceof Fe)o=n;else{const{id:t,dimension:r}=Me(e,{baseType:G,dimension:1},n);o=Ie(t,r,e)}const s=we({nodeType:L.OPERATION,opCode:Z.ARRAY_ACCESS,dependsOn:[t.id,o.id],dimension:1,baseType:$}),a=Ce(r,s);return Ae(i,i.currentBlock,a),{id:a,dimension:1}}(this.strandsContext,this,e);return Ie(t,n,this.strandsContext)}set(e,t){if("storage"!==Se(this.strandsContext.dag,this.id).baseType)throw new Error("set() can only be used on storage buffers");if(!this._originalIdentifier)throw new Error("set() can only be used on storage buffers with an identifier");if(null!==t&&"object"==typeof t&&!t.isStrandsNode&&this._schema){const n=Be(this.strandsContext,this,e,this._schema);for(const[e,r]of Object.entries(t))n[e]=r;return this}return function(e,t,n,r){const{dag:i,cfg:o}=e;let s,a;if(n instanceof Fe)s=n;else{const{id:t,dimension:r}=Me(e,{baseType:G,dimension:1},n);s=Ie(t,r,e)}if(r instanceof Fe)a=r;else{const{id:t,dimension:n}=Me(e,{baseType:$,dimension:1},r);a=Ie(t,n,e)}const u=we({nodeType:L.OPERATION,opCode:Z.ARRAY_ACCESS,dependsOn:[t.id,s.id],dimension:1,baseType:$}),l=Ce(i,u),p=we({nodeType:L.ASSIGNMENT,dependsOn:[l,a.id],phiBlocks:[]}),c=Ce(i,p);Ae(o,o.currentBlock,c)}(this.strandsContext,this,e,t),this}}function Ie(e,t,n,r){return new Proxy(new Fe(e,t,n),function(e,t,n,r){const i=[["x","y","z","w"],["r","g","b","a"],["s","t","p","q"]].map((e=>e.slice(0,t))),o={get(e,t,r){if(t in e)return Reflect.get(...arguments);for(const r of i)if([...t.toString()].every((e=>r.includes(e)))){const o=[...t].map((e=>{const t=r.indexOf(e);return i[0][t]})).join(""),s=Ve(n,e,o);return Ie(s.id,s.dimension,n)}},set(e,t,o,s){for(const s of i){const i=[...t];if(!(i.every((e=>s.includes(e)))&&new Set(i).size===i.length&&e.dimension>=i.length))continue;const a=e.dimension,u=new Array(a);for(let t=0;t<a;t++){const{id:r,dimension:i}=Ve(n,e,"xyzw"[t]);u[t]=Ie(r,i,n)}let l=[];if(o?.isStrandsNode)if(1===o.dimension)l=Array(i.length).fill(o);else if(o.dimension===i.length)for(let e=0;e<i.length;e++){const{id:t,dimension:r}=Ve(n,o,"xyzw"[e]);l.push(Ie(t,r,n))}else _e(i.length,o.dimension,`${e._originalIdentifier||"value"}.${t}`);else if(Array.isArray(o)){const e=o.flat(1/0);1===e.length?l=Array(i.length).fill(e[0]):e.length===i.length?l=e:be("type error",`Swizzle assignment: RHS length ${e.length} does not match ${i.length}.`)}else"number"==typeof o?l=Array(i.length).fill(o):be("type error",`Unsupported RHS for swizzle assignment: ${o}`);for(let e=0;e<i.length;e++){u[s.indexOf(i[e])]=l[e]}const p=Se(n.dag,e.id),c=p?.baseType??$,{id:f}=Me(n,{baseType:c,dimension:a},u);return e.id=f,"function"==typeof r&&r(f),!0}return Reflect.set(...arguments)}};return o}(0,t,n,r))}const Pe={...{acos:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!0}],acosh:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!1}],asin:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!0}],asinh:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!1}],atan:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!0},{params:[J.FLOAT,J.FLOAT],returnType:J.FLOAT,isp5Function:!0}],atanh:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!1}],cos:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!0}],cosh:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!1}],degrees:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!0}],radians:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!0}],sin:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!0}],sinh:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!1}],tan:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!0}],tanh:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!1}],abs:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!0},{params:[J.FLOAT],returnType:J.INT,isp5Function:!0}],ceil:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!0}],clamp:[{params:[J.FLOAT,J.FLOAT,J.FLOAT],returnType:J.FLOAT,isp5Function:!1},{params:[J.FLOAT,Q.float1,Q.float1],returnType:J.FLOAT,isp5Function:!1},{params:[J.INT,J.INT,J.INT],returnType:J.INT,isp5Function:!1},{params:[J.INT,Q.int1,Q.int1],returnType:J.INT,isp5Function:!1}],dFdx:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!1}],dFdy:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!1}],exp:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!0}],exp2:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!1}],floor:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!0}],fma:[{params:[J.FLOAT,J.FLOAT,J.FLOAT],returnType:J.FLOAT,isp5Function:!1}],fract:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!0}],fwidth:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!1}],inversesqrt:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!0}],log:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!0}],log2:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!1}],max:[{params:[J.FLOAT,J.FLOAT],returnType:J.FLOAT,isp5Function:!0},{params:[J.FLOAT,Q.float1],returnType:J.FLOAT,isp5Function:!0},{params:[J.INT,J.INT],returnType:J.INT,isp5Function:!0},{params:[J.INT,Q.int1],returnType:J.INT,isp5Function:!0}],min:[{params:[J.FLOAT,J.FLOAT],returnType:J.FLOAT,isp5Function:!0},{params:[J.FLOAT,Q.float1],returnType:J.FLOAT,isp5Function:!0},{params:[J.INT,J.INT],returnType:J.INT,isp5Function:!0},{params:[J.INT,Q.int1],returnType:J.INT,isp5Function:!0}],mix:[{params:[J.FLOAT,J.FLOAT,J.FLOAT],returnType:J.FLOAT,isp5Function:!1},{params:[J.FLOAT,J.FLOAT,Q.float1],returnType:J.FLOAT,isp5Function:!1},{params:[J.FLOAT,J.FLOAT,J.BOOL],returnType:J.FLOAT,isp5Function:!1}],mod:[{params:[J.FLOAT,J.FLOAT],returnType:J.FLOAT,isp5Function:!0},{params:[J.FLOAT,Q.float1],returnType:J.FLOAT,isp5Function:!0}],pow:[{params:[J.FLOAT,J.FLOAT],returnType:J.FLOAT,isp5Function:!0}],round:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!0}],roundEven:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!1}],sign:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!1},{params:[J.INT],returnType:J.INT,isp5Function:!1}],smoothstep:[{params:[J.FLOAT,J.FLOAT,J.FLOAT],returnType:J.FLOAT,isp5Function:!1},{params:[Q.float1,Q.float1,J.FLOAT],returnType:J.FLOAT,isp5Function:!1}],sqrt:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!0}],step:[{params:[J.FLOAT,J.FLOAT],returnType:J.FLOAT,isp5Function:!1}],trunc:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!1}],cross:[{params:[Q.float3,Q.float3],returnType:Q.float3,isp5Function:!0}],distance:[{params:[J.FLOAT,J.FLOAT],returnType:Q.float1,isp5Function:!0}],dot:[{params:[J.FLOAT,J.FLOAT],returnType:Q.float1,isp5Function:!0}],equal:[{params:[J.FLOAT,J.FLOAT],returnType:J.BOOL,isp5Function:!1},{params:[J.INT,J.INT],returnType:J.BOOL,isp5Function:!1},{params:[J.BOOL,J.BOOL],returnType:J.BOOL,isp5Function:!1}],faceforward:[{params:[J.FLOAT,J.FLOAT,J.FLOAT],returnType:J.FLOAT,isp5Function:!1}],length:[{params:[J.FLOAT],returnType:Q.float1,isp5Function:!1}],normalize:[{params:[J.FLOAT],returnType:J.FLOAT,isp5Function:!0}],notEqual:[{params:[J.FLOAT,J.FLOAT],returnType:J.BOOL,isp5Function:!1},{params:[J.INT,J.INT],returnType:J.BOOL,isp5Function:!1},{params:[J.BOOL,J.BOOL],returnType:J.BOOL,isp5Function:!1}],reflect:[{params:[J.FLOAT,J.FLOAT],returnType:J.FLOAT,isp5Function:!1}],refract:[{params:[J.FLOAT,J.FLOAT,Q.float1],returnType:J.FLOAT,isp5Function:!1}]}};function Ee(e,t,n){const{cfg:r,dag:i}=e;let{dimension:o,baseType:s}=t;1!==o&&Te("Created a scalar literal node with dimension > 1.");const a=Ce(i,we({nodeType:L.LITERAL,dimension:o,baseType:s,value:n}));return Ae(r,r.currentBlock,a),{id:a,dimension:o}}function Le(e,t,n){const{cfg:r,dag:i}=e,{dimension:o,baseType:s}=t,a=Ce(i,we({nodeType:L.VARIABLE,dimension:o,baseType:s,identifier:n}));return Ae(r,r.currentBlock,a),{id:a,dimension:o}}function De(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=Se(a,t.id);if(l=t.id,s=e.baseType,e.opCode===te.CONSTRUCTOR)for(const t of e.dependsOn)i.push(t);else i.push(t.id);u+=e.dimension}else if("number"!=typeof t)if("boolean"!=typeof t)be("type error",`You've tried to construct a scalar or vector type with a non-numeric value: ${t}`);else{const{id:n,dimension:r}=Ee(e,{dimension:1,baseType:j},t);i.push(n),u+=r,s!==j&&(s=j)}else{const{id:n,dimension:r}=Ee(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&&be("type error",`You've tried to construct a ${s+o} with ${u} components`);return{originalNodeID:l,mappedDependencies:i,inferredTypeInfo:{dimension:o,baseType:s,priority:X[s]}}}function Re(e,t,n){const r=we({nodeType:L.OPERATION,opCode:te.CONSTRUCTOR,dimension:t.dimension,baseType:t.baseType,dependsOn:n});return Ce(e.dag,r)}function Me(e,t,n){const r=e.cfg;n=(Array.isArray(n)?n:[n]).flat(1/0).map((t=>t.isStrandsNode&&t.typeInfo().baseType===G&&1===t.typeInfo().dimension?function(e,t){const{id:n,dimension:r}=Ne(e,e.backend.getTypeName("float",t.typeInfo().dimension),[t],{overloads:[{params:[t.typeInfo()],returnType:{...t.typeInfo(),baseType:$}}]});return Ie(n,r,e)}(e,t):t));const{mappedDependencies:i,inferredTypeInfo:o}=De(e,t,n),s={baseType:o.baseType===j?j:t.baseType,dimension:o.dimension},a=Re(e,s,i);return t.baseType!==W&&Ae(r,r.currentBlock,a),{id:a,dimension:s.dimension,components:i}}function Ne(e,t,n,{overloads:r}={}){const{cfg:i,dag:o}=e,s=r||Pe[t],a=n.map((t=>De(e,Q.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 ");be("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,c=0,f=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>c)&&(p=e,c=n,f={...e.returnType},l(f)&&(f.dimension=d))}null===p&&be("parameter validation",`No matching overload for ${t} was found!`);let h=[];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&&(m=n.inferredTypeInfo,g=r,m.dimension===g.dimension&&m.baseType===g.baseType))h.push(n.originalNodeID);else{const t=Re(e,r,n.mappedDependencies);Ae(i,i.currentBlock,t),h.push(t)}}var m,g;const x=Ce(o,we({nodeType:L.OPERATION,opCode:te.FUNCTION_CALL,identifier:t,dependsOn:h,baseType:f.baseType,dimension:f.dimension}));return Ae(i,i.currentBlock,x),{id:x,dimension:f.dimension}}function Ve(e,t,n){const{dag:r,cfg:i}=e,o=r.baseTypes[t.id],s=Ce(r,we({nodeType:L.OPERATION,baseType:o,dimension:n.length,opCode:ee.SWIZZLE,dependsOn:[t.id],swizzle:n}));return Ae(i,i.currentBlock,s),{id:s,dimension:n.length}}function Be(e,t,n,r){const{dag:i,cfg:o}=e;let s;if(n instanceof Fe)s=n;else{const{id:t,dimension:r}=Me(e,{baseType:G,dimension:1},n);s=Ie(t,r,e)}const a={};for(const n of r.fields)Object.defineProperty(a,n.name,{get(){const r=we({nodeType:L.OPERATION,opCode:Z.ARRAY_ACCESS,dependsOn:[t.id,s.id],dimension:n.dim,baseType:$,identifier:n.name}),a=Ce(i,r);Ae(o,o.currentBlock,a);return Ie(a,n.dim,e,(e=>{const r=we({nodeType:L.OPERATION,opCode:Z.ARRAY_ACCESS,dependsOn:[t.id,s.id],dimension:n.dim,baseType:$,identifier:n.name}),a=Ce(i,r),u=we({nodeType:L.ASSIGNMENT,dependsOn:[a,e],phiBlocks:[]}),l=Ce(i,u);Ae(o,o.currentBlock,l)}))},set(r){const a=we({nodeType:L.OPERATION,opCode:Z.ARRAY_ACCESS,dependsOn:[t.id,s.id],dimension:n.dim,baseType:$,identifier:n.name}),u=Ce(i,a);let l;if(r?.isStrandsNode)l=r.id;else{const{id:t}=Me(e,{baseType:$,dimension:n.dim},r);l=t}const p=we({nodeType:L.ASSIGNMENT,dependsOn:[u,l],phiBlocks:[]}),c=Ce(i,p);Ae(o,o.currentBlock,c)},configurable:!0});return a}function Ue(e,t){if(e.nodeTypes[t]!==L.OPERATION)return!1;if(e.baseTypes[t]===K)return!1;return(e.usedBy[t]||[]).length>1}const ke={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>"},ze={[ie.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(Ue(r,e)){const t=$e.generateDeclaration(n,r,e);n.write(t)}t===L.STATEMENT&&$e.generateStatement(n,r,e),t===L.ASSIGNMENT&&($e.generateAssignment(n,r,e),n.visitedNodes.add(e))}},[ie.BRANCH](e,t,n){const{dag:r,cfg:i}=t,o=i.blockInstructions[e]||[];for(const e of o){const t=Se(r,e);if(t.nodeType===L.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=Oe(r,e),a=$e.getTypeName(s.baseType,s.dimension);let u;if(1===s.dimension)u=this.defaultScalarValue(s.baseType);else{const e=this.defaultScalarValue(s.baseType);u=`${a}(${Array(s.dimension).fill(e).join(", ")})`}n.write(`var ${o}: ${a} = ${u};`)}}this[ie.DEFAULT](e,t,n)},defaultScalarValue:e=>e===$?"0.0":e===j?"false":"0",[ie.IF_COND](e,t,n){const{dag:r,cfg:i}=t,o=i.blockConditions[e],s=$e.generateExpression(n,r,o);n.write(`if (${s})`),this[ie.DEFAULT](e,t,n)},[ie.ELSE_COND](e,t,n){n.write("else"),this[ie.DEFAULT](e,t,n)},[ie.IF_BODY](e,t,n){this[ie.DEFAULT](e,t,n),this.assignPhiNodeValues(e,t,n)},[ie.SCOPE_START](e,t,n){n.write("{"),n.indent++},[ie.SCOPE_END](e,t,n){n.indent--,n.write("}")},[ie.MERGE](e,t,n){this[ie.DEFAULT](e,t,n)},[ie.FUNCTION](e,t,n){this[ie.DEFAULT](e,t,n)},[ie.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=Se(r,t),s=e===o.length-1;if(n.suppressSemicolon=s,Ue(r,t)){const e=$e.generateDeclaration(n,r,t);n.write(e)}i.nodeType===L.STATEMENT&&$e.generateStatement(n,r,t),i.nodeType===L.ASSIGNMENT&&($e.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=Se(r,t);if(i.nodeType===L.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=$e.generateExpression(n,r,e);n.write(`${s} = ${t};`)}}}}}}},$e={hookEntry(e){const t=`(${e.parameters.map((e=>`${e.type.properties?`_p5_strands_raw_${e.name}`:`${R}${e.name}`}: ${e.type.typeName}`)).join(", ")}) {`,n=e.parameters.filter((e=>e.type.properties)).map((e=>` var ${R}${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,compute:e.baseShader._computeSrc});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}},addStorageBufferBindingsToDeclarations(e){if(!e.renderer||!e.baseShader)return;const t="compute"===e.baseShader.shaderType;let n=e.renderer.getNextBindingIndex({vert:e.baseShader._vertSrc,frag:e.baseShader._fragSrc,compute:e.baseShader._computeSrc});for(const{name:r,typeInfo:i}of e.uniforms)if("storage"===i.baseType){const o=t?"read_write":"read";let s;if(i.schema){const e=`${r}Element`;s=`struct ${e} ${i.schema.structBody}\n@group(0) @binding(${n}) var<storage, ${o}> ${r}: array<${e}>;`}else s=`@group(0) @binding(${n}) var<storage, ${o}> ${r}: array<f32>;`;t?e.computeDeclarations.add(s):(e.vertexDeclarations.add(s),e.fragmentDeclarations.add(s)),n+=1}},getTypeName(e,t){const n=ke[e+t];return n||e},getNoiseShaderSnippet:()=>"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}",getRandomFragmentShaderSnippet:()=>"\nvar<private> _p5_randomCallIndex: i32 = 0;\n\nfn _p5_hash(p: vec3<f32>) -> f32 {\n var p3 = fract(p * vec3<f32>(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn random(seed: f32, pixelCoord: vec2<f32>) -> f32 {\n let callIndex = f32(_p5_randomCallIndex);\n _p5_randomCallIndex = _p5_randomCallIndex + 1;\n let s = fract(seed * 0.7548776662);\n return _p5_hash(vec3<f32>(\n pixelCoord.x + s,\n pixelCoord.y + callIndex * 0.5698402910,\n s + callIndex * 0.6180339887\n ));\n}\n",getRandomVertexShaderSnippet:()=>"\nvar<private> _p5_randomCallIndex: i32 = 0;\n\nfn _p5_hash(p: vec3<f32>) -> f32 {\n var p3 = fract(p * vec3<f32>(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn random(seed: f32, vertexId: f32) -> f32 {\n let callIndex = f32(_p5_randomCallIndex);\n _p5_randomCallIndex = _p5_randomCallIndex + 1;\n let s = fract(seed * 0.7548776662);\n return _p5_hash(vec3<f32>(\n vertexId + s,\n vertexId * 0.5698402910 + callIndex * 0.6180339887,\n s + callIndex * 0.7548776662\n ));\n}\n",getRandomComputeShaderSnippet:()=>"\nvar<private> _p5_randomCallIndex: i32 = 0;\n\nfn _p5_hash(p: vec3<f32>) -> f32 {\n var p3 = fract(p * vec3<f32>(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn random(seed: f32, invocationId: vec3<u32>) -> f32 {\n let id = vec3<f32>(invocationId);\n let callIndex = f32(_p5_randomCallIndex);\n _p5_randomCallIndex = _p5_randomCallIndex + 1;\n let s = fract(seed * 0.7548776662);\n return _p5_hash(vec3<f32>(\n id.x + s,\n id.y + callIndex * 0.5698402910,\n id.z + s + callIndex * 0.6180339887\n ));\n}\n",generateHookUniformKey(e,t){return"sampler2D"===t.baseType?`${e}: sampler2D`:"storage"===t.baseType?null:`${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=Se(t,n),i=e.suppressSemicolon?"":";";if(r.statementType===V)e.write(`discard${i}`);else if(r.statementType===B)e.write(`break${i}`);else if(r.statementType===k){const n=r.dependsOn[0],o=this.generateExpression(e,t,n);e.write(`${o}${i}`)}else if(r.statementType===z)e.write(i);else if(r.statementType===U)if(r.dependsOn&&r.dependsOn.length>0){const n=r.dependsOn[0],o=this.generateExpression(e,t,n);e.write(`return ${o}${i}`)}else e.write(`return${i}`)},generateAssignment(e,t,n){const r=Se(t,n),i=r.dependsOn[0],o=r.dependsOn[1],s=Se(t,i),a=e.suppressSemicolon?"":";";if(s.opCode!==Z.ARRAY_ACCESS)if(s.opCode===ee.SWIZZLE){const n=s.dependsOn[0],r=Se(t,n),i=this.generateExpression(e,t,n),u=s.swizzle,l=r.dimension,p=this.generateExpression(e,t,o),c=[];for(let e=0;e<l;e++)c[e]={target:"self",index:e};const f=e=>"xyzw".includes(e)?"xyzw".indexOf(e):"rgba".includes(e)?"rgba".indexOf(e):-1;for(let e=0;e<u.length;e++){const t=f(u[e]);t>=0&&t<l&&(c[t]={target:"rhs",index:e})}const d=this.getTypeName(r.baseType,l),h=c.map((({target:e,index:t})=>`${"self"===e?i:p}.${"xyzw"[t]}`));e.write(`${i} = ${d}(${h.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}`)}else{const[n,r]=s.dependsOn,i=this.generateExpression(e,t,n),u=this.generateExpression(e,t,r),l=this.generateExpression(e,t,o),p=s.identifier?`.${s.identifier}`:"";e.write(`${i}[i32(${u})]${p} = ${l}${a}`)}},generateDeclaration(e,t,n){const r=this.generateExpression(e,t,n),i="T"+e.nextTempID++;e.tempNames[n]=i;const o=Oe(t,n);return`var ${i}: ${this.getTypeName(o.baseType,o.dimension)} = ${r};`},generateReturnStatement(e,t,n,r){if(!r)return void t.write("return;");const i=e.dag,o=Se(i,n);if((s=r)&&s.properties&&o.identifier){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=Se(t,n);if(e.tempNames?.[n])return e.tempNames[n];switch(r.nodeType){case L.LITERAL:return r.baseType===$?r.value.toFixed(4):r.value;case L.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)}if(r.identifier===this.instanceIdReference()&&"fragment"===e.shaderContext)return e.strandsContext._instanceIDUsedInFragment=!0,D;const i=e.strandsContext?.uniforms?.find((e=>e.name===r.identifier));return i&&"sampler2D"!==i.typeInfo.baseType&&"storage"!==i.typeInfo.baseType?`hooks.${r.identifier}`:r.identifier;case L.OPERATION:const o=r.usedBy.length>0;if(r.opCode===te.CONSTRUCTOR){if(r.baseType===K)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===te.TERNARY){const[n,i,o]=r.dependsOn,s=this.generateExpression(e,t,n),a=this.generateExpression(e,t,i);return`select(${this.generateExpression(e,t,o)}, ${a}, ${s})`}if(r.opCode===te.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)));if("random"===r.identifier){const t=e.shaderContext;"fragment"===t?n.push("_p5FragPos.xy"):"vertex"===t?n.push("f32(_p5VertexId)"):"compute"===t&&n.push("_p5GlobalId")}return`${r.identifier}(${n.join(", ")})`}if(r.opCode===Z.MEMBER_ACCESS){const[n,i]=r.dependsOn;return`${this.generateExpression(e,t,n)}.${this.generateExpression(e,t,i)}`}if(r.opCode===ee.SWIZZLE){const n=r.dependsOn[0];return`${this.generateExpression(e,t,n)}.${r.swizzle}`}if(r.opCode===Z.ARRAY_ACCESS){const[n,i]=r.dependsOn;return`${this.generateExpression(e,t,n)}[i32(${this.generateExpression(e,t,i)})]${r.identifier?`.${r.identifier}`:""}`}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===Z.MODULO)return`(${s} % ${a})`;const u=re[r.opCode];return o?`(${s} ${u} ${a})`:`${s} ${u} ${a}`}if(r.opCode===ee.LOGICAL_NOT||r.opCode===ee.NEGATE||r.opCode===ee.PLUS){const[n]=r.dependsOn,i=this.generateExpression(e,t,n);return`${re[r.opCode]}${i}`}case L.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 L.ASSIGNMENT:Te("ASSIGNMENT nodes should not be used as expressions");default:Te(`${M[r.nodeType]} code generation not implemented yet`)}},generateBlock(e,t,n){const r=t.cfg.blockTypes[e];(ze[r]||ze[ie.DEFAULT]).call(ze,e,t,n)},createGetTextureCall(e,t){const n=t[0],r=t[1],{dag:i}=e,o=Se(i,n.id).identifier+"_sampler",s=Le(e,{baseType:Y,dimension:1},o),a=Ie(s.id,s.dimension,e),u=Ee(e,{dimension:1,baseType:$},0),l=[n,a,r,Ie(u.id,u.dimension,e)],{id:p,dimension:c}=Ne(e,"textureSampleLevel",l,{overloads:[{params:[Q.sampler2D,Q.sampler,Q.float2,Q.float1],returnType:Q.float4}]});return{id:p,dimension:c}},instanceIdReference:()=>"instanceID",generateInstanceIDVarying:()=>({name:D,declaration:`${D}: i32`,source:"i32(instanceID)",interpolation:"flat"})},Ge="\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",je=Ge+"\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",He=Ge+"\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",We="\nstruct Uniforms {\n uModelViewMatrix: mat4x4<f32>,\n uProjectionMatrix: mat4x4<f32>,\n uNormalMatrix: mat3x3<f32>,\n roughness: f32,\n};\n",qe="\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",Ke=`\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${We}\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`,Ye=`\nstruct FragmentInput {\n @location(0) localPos: vec3<f32>,\n @location(3) vTexCoord: vec2<f32>,\n}\n\n${We}\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${qe}\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`,Xe=`\nstruct FragmentInput {\n @location(0) localPos: vec3<f32>,\n @location(3) vTexCoord: vec2<f32>,\n}\n\n${We}\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${qe}\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`,Qe=0,Je=1,Ze=2;function et(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[E[n]]=i},o=function(t,i){r+=e(`STROKE_JOIN_${t}`,`${i}`,"u32"),n[E[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:F,RGBA:I}=e;class P{constructor(e,t,n,r=null){this._isStorageBuffer=!0,this.buffer=e,this.size=t,this._renderer=n,this._schema=r}update(e){const t=this._renderer.device;if(null!==this._schema){if(!Array.isArray(e)||0===e.length||"object"!=typeof e[0]||Array.isArray(e[0]))throw new Error("update() expects an array of objects matching the original struct format");const n=this._renderer._inferStructSchema(e[0]);if(n.structBody!==this._schema.structBody)throw new Error(`update() data structure doesn't match the original.\n Expected: ${this._schema.structBody}\n Got: ${n.structBody}`);const r=this._renderer._packStructArray(e,this._schema);if(r.byteLength>this.size)throw new Error(`update() data (${r.byteLength} bytes) exceeds buffer size (${this.size} bytes)`);t.queue.writeBuffer(this.buffer,0,r)}else{let n;if(e instanceof Float32Array)n=e;else{if(!Array.isArray(e))throw new Error("update() expects a Float32Array or array of numbers for this buffer");n=new Float32Array(e)}if(n.byteLength>this.size)throw new Error(`update() data (${n.byteLength} bytes) exceeds buffer size (${this.size} bytes)`);t.queue.writeBuffer(this.buffer,0,n)}}async read(){const e=this._renderer.device;this._renderer.flushDraw();const t=e.createBuffer({size:this.size,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ}),n=e.createCommandEncoder();n.copyBufferToBuffer(this.buffer,0,t,0,this.size),e.queue.submit([n.finish()]),await t.mapAsync(GPUMapMode.READ,0,this.size);const r=t.getMappedRange(0,this.size),i=new Float32Array(r.byteLength/4);return i.set(new Float32Array(r)),t.unmap(),t.destroy(),null!==this._schema?this._renderer._unpackStructArray(i,this._schema):i}set(e,t){const n=this._renderer.device;if(null!==this._schema){if("object"!=typeof t||null===t||Array.isArray(t))throw new Error("set() expects a plain object matching the original struct format for this buffer");const{stride:r}=this._schema,i=e*r;if(i+r>this.size)throw new Error(`set() index ${e} is out of bounds for this buffer (buffer holds ${Math.floor(this.size/r)} elements)`);const o=this._renderer._packStructArray([t],this._schema);n.queue.writeBuffer(this.buffer,i,o.buffer,0,r)}else{if("number"!=typeof t)throw new Error("set() expects a number for this float buffer");const r=4*e;if(r+4>this.size)throw new Error(`set() index ${e} is out of bounds for this buffer (buffer holds ${Math.floor(this.size/4)} floats)`);n.queue.writeBuffer(this.buffer,r,new Float32Array([t]))}}}e.StorageBuffer=P;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=$e,this._shadersWithPools=[],this._geometriesWithPools=[],this._hasPendingDraws=!1,this._pendingCommandEncoders=[],this._postSubmitCallbacks=[],this._retiredBuffers=[],this._storageBuffers=new Set,this._pixelReadCanvas=null,this._pixelReadCtx=null,this.mainFramebuffer=null,this._frameState=Qe,this.finalCamera=new F(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._pInst._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===Ze||this.activeFramebuffer()||(this._pInst.frameCount>0?this._frameState=Je:this._promoteToFramebufferWithoutCopy()),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,compute:t,workgroupSize:n}){if(t)return{compute:!0,workgroupSize:n};const r=this.activeFramebuffer(),i=r?this._getWebGPUColorFormat(r):this.presentationFormat,o=r&&r.antialias?r.antialiasSamples:1,s=this._getValidSampleCount(o),a=r?r.useDepth?this._getWebGPUDepthFormat(r):void 0:this.depthFormat,u=this.drawTarget(),l=this._clipping,p=u._isClipApplied;return{topology:5===e?"triangle-strip":"triangle-list",blendMode:this.states.curBlendMode,sampleCount:s,format:i,depthFormat:a,clipping:l,clipApplied:p}}_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;if("compute"===e.shaderType)return e.computeModule=t.createShaderModule({code:e.computeSrc()}),e._computePipelineCache=null,e._workgroupSize=null,void(e.getPipeline=({workgroupSize:n})=>(e._computePipelineCache||(e._computePipelineCache=t.createComputePipeline({layout:e._pipelineLayout,compute:{module:e.computeModule,entryPoint:"main"}}),e._workgroupSize=n),e._computePipelineCache));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},...s?{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 n=r.get(t.group)||[];n.push({bufferGroup:t,binding:t.binding,visibility:"compute"===e.shaderType?GPUShaderStage.COMPUTE:GPUShaderStage.VERTEX|GPUShaderStage.FRAGMENT,buffer:{type:"uniform",hasDynamicOffset:t.dynamic}}),r.set(t.group,n)}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)}for(const t of e._storageBuffers||[]){const e=t.group,r=n.get(e)||[];r.push({binding:t.binding,visibility:t.visibility,buffer:{type:"read"===t.accessMode?"read-only-storage":"storage"},storage: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}),e._compiled=!0}_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 c: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 m: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 h:return{color:{operation:"reverse-subtract",srcFactor:"one",dstFactor:"one"},alpha:{operation:"add",srcFactor:"one",dstFactor:"one-minus-src-alpha"}};case f: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)}supportsTriangleFan(){return!1}viewport(){}zClipRange(){return[0,1]}defaultNearScale(){return.01}defaultFarScale(){return 100}_resetBuffersBeforeDraw(){this._finishActiveRenderPass(),this._pInst.frameCount>0&&(this._frameState=Qe);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===Ze)return;if(this.activeFramebuffer())return;this.flushDraw(),this._frameState=Ze;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.states.uModelMatrix.set(this.states.uModelMatrix.copy()),this.states.uModelMatrix.reset(),this.mainFramebuffer.defaultCamera.set(this.states.curCamera),this.mainFramebuffer.begin(),this.states.uModelMatrix.set(n)}_promoteToFramebufferWithoutCopy(){if(this._frameState===Ze)return;this.mainFramebuffer.width===this.width&&this.mainFramebuffer.height===this.height||this.mainFramebuffer.resize(this.width,this.height),this._frameState=Ze,this.flushDraw();const e=this.states.uModelMatrix.copy();this.states.uModelMatrix.set(this.states.uModelMatrix.copy()),this.states.uModelMatrix.reset(),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===Ze){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===Ze)for(const{fbo:t,diff:n}of e){t===this.mainFramebuffer&&this._frameState===Ze||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!==Qe||this.activeFramebuffer()||this._promoteToFramebufferWithoutCopy(),this._beginActiveRenderPass();const i=this.activeRenderPass,o=this._curShader;this.setupShaderBindGroups(o,i,{mode:t,buffers:r});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)}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}setupShaderBindGroups(e,t,n){const r=this._shaderOptions(n);if((r.compute||this.activeShader!==e||this._shaderOptionsDifferent(r))&&t.setPipeline(e.getPipeline(r)),!r.compute){this.activeShader=e,this.activeShaderOptions=r;this.drawTarget()._isClipApplied&&!this._clipping?t.setStencilReference(0):this._clipping&&t.setStencilReference(1)}for(const t of e._uniformBufferGroups)if(t.dynamic){const n=this._getDynamicUniformBufferFromPool(t);e._lastDynamicBuffer[t.cacheKey]!==n&&(e._cachedBindGroup[t.group]=void 0,e._lastDynamicBuffer[t.cacheKey]=n),this._packUniformGroup(e,t.uniforms,n),n.lastOffset=n.offset,n.offset+=Math.ceil(t.size/this.uniformBufferAlignment)*this.uniformBufferAlignment,t.currentDynamicBuffer=n,t.lastOffset=n.lastOffset}else{let n;!this._hasGroupDataChanged(e,t)&&t.currentBuffer?(n=t.currentBuffer,t.buffersInUse.add(n)):(n=this._getUniformBufferFromPool(t),this._packUniformGroup(e,t.uniforms,n),this.device.queue.writeBuffer(n.buffer,0,n.data.buffer,n.data.byteOffset,n.data.byteLength),e.buffersDirty.delete(1e3*t.group+t.binding),e._cachedBindGroup[t.group]=void 0,t.currentBuffer=n)}for(const t of e.samplers){const n=1e3*t.group+t.binding;e.buffersDirty.has(n)&&(e._cachedBindGroup[t.group]=void 0,e.buffersDirty.delete(n))}for(const t of e._storageBuffers||[]){const n=1e3*t.group+t.binding;e.buffersDirty.has(n)&&(e._cachedBindGroup[t.group]=void 0,e.buffersDirty.delete(n))}for(const n of e._groupEntries){const r=n[0],i=n[1];let o=0;const s=[];let a=e._cachedBindGroup[r];for(const t of i){const n=t.bufferGroup,r=n?.currentBuffer||n?.currentDynamicBuffer;if(r)n.dynamic&&(this.dynamicEntryOffsets[o++]=n.lastOffset),a||s.push({binding:t.binding,resource:n.dynamic?{buffer:r.uniformBuffer,offset:0,size:Math.ceil(n.size/this.uniformBufferAlignment)*this.uniformBufferAlignment}:{buffer:r.buffer}});else if(t.storage&&!a){const n=e.uniforms[t.storage.name];if(!n||!n._cachedData||!n._cachedData._isStorageBuffer)throw new Error(`Storage buffer "${t.storage.name}" not set. Use shader.setUniform("${t.storage.name}", storageBuffer)`);s.push({binding:t.binding,resource:{buffer:n._cachedData.buffer}})}else a||s.push({binding:t.binding,resource:"sampler"===t.uniform.type?(t.uniform.textureSource.texture||this._getEmptyTexture()).getSampler():(t.uniform.texture||this._getEmptyTexture()).textureHandle.view})}const u=e._bindGroupLayouts[r];a||(a=this.device.createBindGroup({layout:u,entries:s})),e._cachedBindGroup[r]=a,0===o?t.setBindGroup(r,a):t.setBindGroup(r,a,this.dynamicEntryOffsets,0,o)}return t}_packField(e,t,n,r,i){if(void 0===t)return;t?.isVector?t=t.values.length!==t.dimensions?t.values.slice(0,t.dimensions):t.values:t?.isColor&&(t=t._getRGBA([1,1,1,1]));const o=i+e.offset;if("u32"===e.baseType)if(4===e.size)r.setUint32(o,t,!0);else for(let e=0;e<t.length;e++)r.setUint32(o+4*e,t[e],!0);else if("i32"===e.baseType)if(4===e.size)r.setInt32(o,t,!0);else for(let e=0;e<t.length;e++)r.setInt32(o+4*e,t[e],!0);else if(e.packInPlace){const e=o/4;n[e+0]=t[0],n[e+1]=t[1],n[e+2]=t[2],n[e+4]=t[3],n[e+5]=t[4],n[e+6]=t[5],n[e+8]=t[6],n[e+9]=t[7],n[e+10]=t[8]}else 4===e.size?n.set([t],o/4):n.set(t,o/4)}_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];t&&!t.isSampler&&this._packField(t,t._mappedData,r,i,o)}}_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:c,packInPlace:f,baseType:d}=l(r);a=Math.ceil(a/p)*p;const h=a+u;i[n]={name:n,location:t?parseInt(t):void 0,index:s,type:r,size:u,offset:a,offsetEnd:h,pack:c,packInPlace:f,baseType:d},s++,a=h}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;const i="compute"===e.shaderType?e.computeSrc():e.vertSrc();for(;null!==(r=n.exec(i));){const[e,n,o,s,a]=r,u=parseInt(o),l=this._parseStruct(i,a);t.push({group:parseInt(n),binding:u,varName:s,structType:a,uniforms:l})}if(0===t.length&&"compute"!==e.shaderType)throw new Error("Expected at least one uniform struct bound to @group(0)");const o={};for(const e of t)for(const[t,n]of Object.entries(e.uniforms))o[t]={...n,group:e.group,binding:e.binding,varName:e.varName};e._uniformGroups=t;const s={},a=/@group\((\d+)\)\s*@binding\((\d+)\)\s*var\s+(\w+)\s*:\s*(texture_2d<f32>|sampler);/g,u={},l=/@group\((\d+)\)\s*@binding\((\d+)\)\s*var<storage,\s*(read|read_write)>\s+(\w+)\s*:\s*array<\w+>/g,p={};for(const e of t)p[e.group+","+e.binding]=!0;for(const[t,n]of[[e.vertSrc(),GPUShaderStage.VERTEX],[e.fragSrc(),GPUShaderStage.FRAGMENT],[e.computeSrc?e.computeSrc():null,GPUShaderStage.COMPUTE]]){if(!t)continue;let e;for(;null!==(e=a.exec(t));){const[t,r,i,o,a]=e,u=parseInt(r),l=parseInt(i);if(p[u+","+l])continue;const c=`${u},${l}`;s[c]={visibility:(s[c]?.visibility||0)|n,group:u,binding:l,name:o,type:a,isSampler:!0,noData:"sampler"===a}}for(const e of Object.values(s))if(e.type.startsWith("texture")){const t=e.name+"_sampler",n=Object.values(s).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}for(;null!==(e=l.exec(t));){const[t,r,i,o,s]=e,a=parseInt(r),l=parseInt(i),p=`${a},${l}`,c=u[p],f="read_write"===c?.accessMode||"read_write"===o?"read_write":o;u[p]={visibility:(c?.visibility||0)|n,group:a,binding:l,name:s,accessMode:f,isStorage:!0,type:"storage"}}}return e._storageBuffers=Object.values(u),[...Object.values(o).sort(((e,t)=>e.index-t.index)),...Object.values(s),...Object.values(u)]}getNextBindingIndex({vert:e,frag:t,compute:n},r=0){const i=/@group\((\d+)\)\s*@binding\((\d+)\)/g;let o=-1;const s=[];e&&s.push([e,GPUShaderStage.VERTEX]),t&&s.push([t,GPUShaderStage.FRAGMENT]),n&&s.push([n,GPUShaderStage.COMPUTE]);for(const[e,t]of s){let t;for(;null!==(t=i.exec(e));){const[e,n,i]=t;parseInt(n)===r&&(o=Math.max(o,parseInt(i)))}}return o+1}updateUniformValue(e,t,n){t.isSampler?t.texture=n instanceof s?n:this.getTexture(n):n?._isStorageBuffer||(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={[T]:"nearest",[y]:"linear",[_]:"clamp-to-edge",[b]:"repeat",[C]:"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,fe,de,{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>, texCoord: vec2<f32>) { return color; }","void afterFragment":"() {}"}})),this._defaultLightShader}_getColorShader(){return this._defaultColorShader||(this._defaultColorShader=new o(this,se,ae,{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>, texCoord: vec2<f32>) { return color; }","void afterFragment":"() {}"}})),this._defaultColorShader}_getLineShader(){return this._defaultLineShader||(this._defaultLineShader=new o(this,r+le,r+pe,{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>, texCoord: vec2<f32>) { return color; }","bool shouldDiscard":"(outside: bool) { return outside; };","void afterFragment":"() {}"}})),this._defaultLineShader}_getFontShader(){return this._defaultFontShader||(this._defaultFontShader=new o(this,me,ge)),this._defaultFontShader}_getBlitShader(){return this._defaultBlitShader||(this._defaultBlitShader=new o(this,ve,ye)),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|compute)\s*(?:@workgroup_size\([^)]+\)\s*)?)?fn main[^{]+\{)/);const s=(e,t)=>{const n=new RegExp(`@builtin\\s*\\(\\s*${t}\\s*\\)\\s*(\\w+)\\s*:`).exec(e);return n?n[1]:null},a=(e,t,n,r)=>{const i=s(e,t);if(i)return{mainSrc:e,argName:i};const o=/\(\s*\S/.test(e);return{mainSrc:e.replace(/\)\s*(->|\{)/,`${o?", ":""}@builtin(${t}) ${n}: ${r}) $1`),argName:n}},u=e=>{const t=/fn main\s*\(\s*(\w+)\s*:\s*(\w+)/.exec(e);return t?{inputName:t[1],structName:t[2]}:null},l=(e,t)=>{if(0===t.length)return e;const n=!/^\(\s*\)$/.test(e);return`${e.slice(0,-1)}${n?", ":""}${t.join(", ")})`};let p=[],c=[];if("vertex"===n){const e=a(i,"instance_index","instanceID","u32");i=e.mainSrc;const t=a(i,"vertex_index","_p5VertexId","u32");i=t.mainSrc,p=["instanceID: u32","_p5VertexId: u32"],c=[e.argName,t.argName]}else if("fragment"===n){let e=s(i,"position");if(!e){const t=u(i);if(t){const n=((e,t)=>{const n=new RegExp(`struct\\s+${e}\\s*\\{([^}]*)\\}`,"s").exec(r);if(!n)return null;const i=new RegExp(`@builtin\\s*\\(\\s*${t}\\s*\\)\\s*(\\w+)\\s*:`,"s").exec(n[1]);return i?i[1]:null})(t.structName,"position");n&&(e=`${t.inputName}.${n}`)}}if(!e){const t=a(i,"position","_p5FragPos","vec4<f32>");i=t.mainSrc,e=t.argName}p=["_p5FragPos: vec4<f32>"],c=[e]}else if("compute"===n){const e=a(i,"global_invocation_id","_p5GlobalId","vec3<u32>");i=e.mainSrc,p=["_p5GlobalId: vec3<u32>"],c=[e.argName]}let f="";for(const t in e.hooks.uniforms)t.endsWith(": sampler2D")||(f+=` ${t},\n`);if(f){const t=`\n// Hook Uniforms (from .modify())\nstruct HookUniforms {\n${f}}\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,compute:"compute"===n?r+(e.hooks.compute?.declarations??"")+e.hooks.declarations:e._computeSrc},0)}) var<uniform> hooks: HookUniforms;\n`,i=r.replace(/(@group\(0\)\s+@binding)/,`${t}\n$1`);r=i===r?r+"\n"+t:i}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=u(i);if(n){const e=n.inputName;t=t.replace(/INPUT_VAR/g,e),o=t+o}}}if(e.hooks.instanceIDVarying){const{name:t,declaration:s,source:a,interpolation:l}=e.hooks.instanceIDVarying,p=this._getNextAvailableLocation(r,n),c=l?` @interpolate(${l})`:"",[f,d]=s.split(":").map((e=>e.trim())),h=`@location(${p})${c} ${s},`;if("vertex"===n){r=r.replace(/struct\s+VertexOutput\s+\{([^}]*)\}/,((e,t)=>`struct VertexOutput {${t}\n${h}}`)),r+=`var<private> ${s};\n`,o=`\n ${f} = ${a};\n`+o;const e=o.match(/return\s+(\w+)\s*;/);if(e){const t=e[1];o=o.replace(/(return\s+\w+\s*;)/g,`${t}.${f} = ${f};\n $1`)}}else if("fragment"===n){r=r.replace(/struct\s+FragmentInput\s+\{([^}]*)\}/,((e,t)=>`struct FragmentInput {${t}\n${h}}`)),r+=`var<private> ${s};\n`;const e=u(i);if(e){o=`\n ${f} = ${e.inputName}.${f};\n`+o}}}let d="",h="";e.hooks.declarations&&(d+=e.hooks.declarations+"\n"),e.hooks[n]&&e.hooks[n].declarations&&(d+=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]);d+="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(" ");h+=`const AUGMENTED_HOOK_${i} = ${e.hooks.modified[n][t]?"true":"false"};\n`;let[o,s,a]=/^(\([^\)]*\))((?:.|\n)*)$/.exec(e.hooks[n][t]);s=l(s,p),d+="void"===r?`fn HOOK_${i}${s}${a}\n`:`fn HOOK_${i}${s} -> ${r}${a}\n`}if(c.length>0){const e=e=>{const t=[];let n,r=0;for(;null!==(n=/HOOK_\w+\(/.exec(e.slice(r)));){const i=r+n.index+n[0].length-1;let o=i+1,s=1,a=!1;for(;o<e.length&&s>0;){if("("===e[o])s++;else if(")"===e[o]){if(s--,0===s)break}else/\S/.test(e[o])&&(a=!0);o++}t.push({pos:o,hasParams:a}),r=i+1}t.sort(((e,t)=>t.pos-e.pos));let i=e;for(const{pos:e,hasParams:n}of t){const t=(n?", ":"")+c.join(", ");i=i.slice(0,e)+t+i.slice(e)}return i};r=e(r),o=e(o)}return r+"\n"+h+d+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:Q.float1,"vec2<f32>":Q.float2,"vec3<f32>":Q.float3,"vec4<f32>":Q.float4,vec2f:Q.float2,vec3f:Q.float3,vec4f:Q.float4,i32:Q.int1,"vec2<i32>":Q.int2,"vec3<i32>":Q.int3,"vec4<i32>":Q.int4,bool:Q.bool1,"vec2<bool>":Q.bool2,"vec3<bool>":Q.bool3,"vec4<bool>":Q.bool4,"mat2x2<f32>":Q.mat2,"mat3x3<f32>":Q.mat3,"mat4x4<f32>":Q.mat4,"texture_2d<f32>":Q.sampler2D};let r=e._vertSrc,i=e.hooks.vertex[t];if(i||(i=e.hooks.fragment[t],r=e._fragSrc),i||(i=e.hooks.compute[t],r=e._computeSrc),!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},c=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),f=n[a]||null;return{name:s,returnType:{typeName:a,qualifiers:u,properties:p(a),dataType:f},parameters:c}}_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!1!==this._pInst._webgpuAttributes?.antialias}supportsFramebufferAntialias(){return!0}createFramebufferResources(e){}validateFramebufferFormats(e){[w,O,A].includes(e.format)||(console.warn("Unknown Framebuffer format. Please use UNSIGNED_BYTE, FLOAT, or HALF_FLOAT. Defaulting to UNSIGNED_BYTE."),e.format=w),e.useDepth&&![S,O].includes(e.depthFormat)&&(console.warn("Unknown Framebuffer depth format. Please use UNSIGNED_INT or FLOAT. Defaulting to FLOAT."),e.depthFormat=O)}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}}}_jsValueToWgslType(e){if("number"==typeof e)return"f32";if(e?.isVector){if(2===e.dimensions)return"vec2f";if(3===e.dimensions)return"vec3f";if(4===e.dimensions)return"vec4f";throw new Error(`Unsupported vector dimension ${e.dimensions} for struct storage field`)}if(e?.isColor)return"vec4f";if(Array.isArray(e)){if(2===e.length)return"vec2f";if(3===e.length)return"vec3f";if(4===e.length)return"vec4f";throw new Error(`Unsupported array length ${e.length} for struct storage field`)}throw new Error(`Unsupported value type ${typeof e} for struct storage field`)}_inferStructSchema(t){const n=Object.entries(t);if(!e.disableFriendlyErrors)for(const[t,r]of n)null===r||"object"!=typeof r||Array.isArray(r)||r?.isVector||r?.isColor||e._friendlyError(`The "${t}" property in your storage data contains a nested object. Make sure you only use properties with numbers, arrays of numbers, or p5.Vector.`,"createStorage");const r=`{\n${n.map((([e,t])=>` ${e}: ${this._jsValueToWgslType(t)},`)).join("\n")}\n}`,i=this._parseStruct(`struct _Tmp ${r}`,"_Tmp");let o=0,s=1;return{fields:n.map((([e,t])=>{const n=i[e];o=Math.max(o,n.offsetEnd);const r=n.size<=4?4:n.size<=8?8:16;s=Math.max(s,r);const a=t?.isVector?"vector":t?.isColor?"color":void 0;return{name:e,baseType:n.baseType,size:n.size,offset:n.offset,packInPlace:n.packInPlace??!1,dim:n.size/4,kind:a}})),stride:Math.ceil(o/s)*s,structBody:r}}_packStructArray(e,t){const{fields:n,stride:r}=t,i=Math.max(e.length*r,16),o=16*Math.ceil(i/16),s=new ArrayBuffer(o),a=new Float32Array(s),u=new DataView(s);for(let t=0;t<e.length;t++){const i=e[t],o=t*r;for(const e of n)this._packField(e,i[e.name],a,u,o)}return a}_unpackStructArray(e,t){const{fields:n,stride:r}=t,i=new DataView(e.buffer),o=Math.floor(e.byteLength/r),s=[];for(let t=0;t<o;t++){const o={},a=t*r;for(const t of n){const n=a+t.offset,r=t.size/4;if("u32"===t.baseType)o[t.name]=1===r?i.getUint32(n,!0):Array.from({length:r},((e,t)=>i.getUint32(n+4*t,!0)));else if("i32"===t.baseType)o[t.name]=1===r?i.getInt32(n,!0):Array.from({length:r},((e,t)=>i.getInt32(n+4*t,!0)));else{const i=n/4;if(1===r)o[t.name]=e[i];else{const n=Array.from(e.slice(i,i+r));if("vector"===t.kind)o[t.name]=this._pInst.createVector(...n);else if("color"===t.kind){const e=this.states.colorMaxes[this.states.colorMode];o[t.name]=this._pInst.color(n[0]*e[0],n[1]*e[1],n[2]*e[2],n[3]*e[3])}else o[t.name]=n}}}s.push(o)}return s}createStorage(t){const n=this.device;if(Array.isArray(t)&&t.length>0&&"object"==typeof t[0]&&!Array.isArray(t[0])){if(!e.disableFriendlyErrors&&t.length>1){const n=Object.keys(t[0]);let r=!1;for(let i=1;i<t.length;i++){const o=t[i],s=Object.keys(o);if(!(n.length===s.length&&n.every(((e,t)=>e===s[t])))){e._friendlyError(`Element ${i} has different fields than element 0. All elements should have the same properties.`,"createStorage");break}for(const s of n){const n=this._jsValueToWgslType(t[0][s]),a=this._jsValueToWgslType(o[s]);if(n!==a){e._friendlyError(`The "${s}" property of element ${i} has type ${a} but element 0 has type ${n}. Proporties should have the same type across all elements.`,"createStorage"),r=!0;break}}if(r)break}}const r=this._inferStructSchema(t[0]),i=this._packStructArray(t,r),o=i.byteLength,s=n.createBuffer({size:o,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC,mappedAtCreation:!0});new Float32Array(s.getMappedRange()).set(i),s.unmap();const a=new P(s,o,this,r);return this._storageBuffers.add(a),a}let r,i;if("number"==typeof t)r=4*t,i=new Float32Array(t);else{if(t instanceof Float32Array)i=t;else{if(!Array.isArray(t))throw new Error("createStorage expects a number or array/Float32Array");i=new Float32Array(t)}r=i.byteLength}r=16*Math.ceil(r/16);const o=n.createBuffer({size:r,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC,mappedAtCreation:i.length>0});if(i.length>0){new Float32Array(o.getMappedRange()).set(i),o.unmap()}const s=new P(o,r,this);return this._storageBuffers.add(s),s}_getWebGPUColorFormat(e){return e.format===O?(e.channels,"rgba32float"):e.format===A?(e.channels,"rgba16float"):e._useCanvasFormat?this.presentationFormat:(e.channels,"rgba8unorm")}_getWebGPUDepthFormat(e){return e._useCanvasFormat?this.depthFormat:e.useStencil?e.depthFormat===O?"depth32float-stencil8":"depth24plus-stencil8":e.depthFormat===O?"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,c=this._ensurePixelReadBuffer(p),f=this.device.createCommandEncoder();f.copyTextureToBuffer({texture:e.colorTexture,mipLevel:0,origin:{x:t*e.density,y:n*e.density,z:0}},{buffer:c,bytesPerRow:l},{width:o,height:s,depthOrArrayLayers:1}),this.device.queue.submit([f.finish()]),await c.mapAsync(GPUMapMode.READ,0,p);const d=c.getMappedRange(0,p);let h;if(l===a)h=new Uint8Array(d.slice(0,o*s*4));else{h=new Uint8Array(o*s*4);const e=new Uint8Array(d);for(let t=0;t<s;t++){const n=t*l,r=t*a;h.set(e.subarray(n,n+a),r)}}this._ensurePixelsAreRGBA(e,h);const m=new u(o,s);return m.imageData=m.canvas.getContext("2d").createImageData(o,s),m.imageData.data.set(h),m.pixels=m.imageData.data,m.updatePixels(),1!==e.density&&m.pixelDensity(e.density),c.unmap(),m}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)}baseFilterShader(){return this._baseFilterShader||(this._baseFilterShader=new o(this,je,He,{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}baseComputeShader(){return this._baseComputeShader||(this._baseComputeShader=new o(this,"\nstruct ComputeUniforms {\n uTotalCount: vec3<i32>,\n uPhysicalCount: vec3<i32>,\n}\n@group(0) @binding(0) var<uniform> uniforms: ComputeUniforms;\n\n@compute @workgroup_size(8, 8, 1)\nfn main(\n @builtin(global_invocation_id) globalId: vec3<u32>,\n @builtin(local_invocation_id) localId: vec3<u32>,\n @builtin(workgroup_id) workgroupId: vec3<u32>,\n @builtin(local_invocation_index) localIndex: u32\n) {\n let totalIterations = u32(uniforms.uTotalCount.x) * u32(uniforms.uTotalCount.y) * u32(uniforms.uTotalCount.z);\n let physicalId = globalId.x + globalId.y * (u32(uniforms.uPhysicalCount.x)) + globalId.z * (u32(uniforms.uPhysicalCount.x) * u32(uniforms.uPhysicalCount.y));\n\n if (physicalId >= totalIterations) {\n return;\n }\n\n var index = vec3<i32>(0);\n index.x = i32(physicalId % u32(uniforms.uTotalCount.x));\n let remainingY = physicalId / u32(uniforms.uTotalCount.x);\n index.y = i32(remainingY % u32(uniforms.uTotalCount.y));\n index.z = i32(remainingY / u32(uniforms.uTotalCount.y));\n\n HOOK_iteration(index);\n}\n",{compute:{"void iteration":"(index: vec3<i32>) {}"}})),this._baseComputeShader}_createImageLightShader(e){if("diffused"===e)return this._pInst.createShader(Ke,Ye);if("specular"===e)return this._pInst.createShader(Ke,Xe);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"}}compute(e,t,n=1,r=1){if("compute"!==e.shaderType)throw new Error("compute() can only be called with a compute shader");this._finishActiveRenderPass(),e._compiled||e.init(),e.setDefaultUniforms(),e.setUniform("uTotalCount",[t,n,r]);const i=t*n*r,o=524280;let s=t,a=n,u=r;(t>o||n>o||r>o||i>1024&&1===n&&1===r)&&(s=Math.ceil(Math.sqrt(i)),a=Math.ceil(i/s),u=1),e.setUniform("uPhysicalCount",[s,a,u]);const l=Math.ceil(s/8),p=Math.ceil(a/8),c=Math.ceil(u/1),f=this.device.createCommandEncoder(),d=f.beginComputePass();this.setupShaderBindGroups(e,d,{compute:!0,workgroupSize:[8,8,1]}),d.dispatchWorkgroups(l,p,c),d.end(),this.device.queue.submit([f.finish()])}}e.RendererWebGPU=L,e.renderers[t]=e.RendererWebGPU,n.setAttributes=async function(e,t){return this._renderer._setAttributes(e,t)}}"undefined"!=typeof p5&&et(p5,p5.prototype)}();