if(window.__PAN_MODEL__&&window.__PAN_MODEL__.model){const modelData=window.__PAN_MODEL__.model;pantherId=parseInt(window.__PAN_MODEL__.pantherId),baseModel=modelData[pantherId],baseModel||console.error(`Base model ID ${pantherId} not found.`)}else console.error("Model not loaded.");function sketch(p){!function(){let panther,lightDir,pantherFont,consoleTexture,typingStartTime,angle=0,angleX=0,angleY=0,maxAngle=p.PI/2,moving=!1,clicked=!1,typingStarted=!1,nftData=function(){const scriptTag=document.querySelector("script[nft]");return{seed:scriptTag?scriptTag.getAttribute("nft"):""}}(),currentSeed=nftData.seed;nftData.block,nftData.rarity;function generateTraits(seed,pantherId,MASK_STYLES){const rng=function(seed){seed=seed.toString();let hash=2166136261;for(let i=0;i(hash^=hash<<13,hash^=hash>>17,hash^=hash<<5,hash=16807*hash%2147483647,(2147483647&hash)/2147483647)}(seed),pick=arr=>arr[Math.floor(rng()*arr.length)];function rgbToHex(r,g,b){return"#"+[r,g,b].map((c=>c.toString(16).padStart(2,"0"))).join("")}function colorDistanceRGB(a,b){const dr=a.r-b.r,dg=a.g-b.g,db=a.b-b.b;return Math.sqrt(dr*dr+dg*dg+db*db)}const PANTHER_NAMES_BY_ID={1:{name:"Nebula",percentage:30},2:{name:"Obsidian",percentage:10},3:{name:"Cryo",percentage:30},4:{name:"Inferno",percentage:30}},selectedEffect=(Object.entries(PANTHER_NAMES_BY_ID).map((([id,data])=>({pantherId:parseInt(id),name:data.name,percentage:data.percentage/100}))),pick([{name:"Tourbillon"},{name:"Explosion"},{name:"ascii"},{name:"omdeyes"},{name:"lasereyes"},{name:"Pointillist"},{name:"tron"}])),name=pick([{name:"FUKK"},{name:"BLOW"},{name:"RRRR"},{name:"LUUV"},{name:"UTXO"},{name:"HASH"},{name:"BYTE"},{name:"LOOP"},{name:"LUKE"}]),maskStyleNames=Object.keys(MASK_STYLES),box=(PANTHER_NAMES_BY_ID[pantherId.toString()].percentage,pick([{name:"Obsidian Phantom",hex:"#0c0416"},{name:"Quantum Rift",hex:"#2a0c4b"},{name:"Infra Black",hex:"#080000"},{name:"Silent Haze",hex:"#424258"},{name:"Void Surge",hex:"#1d003f"},{name:"Toxic Ember",hex:"#c91058"},{name:"Subzero Ghost",hex:"#366dc2"},{name:"Crimson Signal",hex:"#b2003a"},{name:"Xeno Pulse",hex:"#8f0aff"},{name:"Graviton Ash",hex:"#50505a"},{name:"Eclipse Drone",hex:"#211221"},{name:"Gamma Vortex",hex:"#7800ff"},{name:"Dead Circuit",hex:"#460716"},{name:"Dark Synapse",hex:"#23005f"},{name:"Solar Flare",hex:"#ff5a00"},{name:"Cryo Prism",hex:"#24779F"},{name:"Neon Apex",hex:"#ff00b4"},{name:"Spectral Nova",hex:"#4C4B87"},{name:"Phantom Gloom",hex:"#09371B"},{name:"Cyber Rune",hex:"#3A3B3E"}])),[r,g,b]=function(hex){const bigint=parseInt(hex.slice(1),16);return[bigint>>16&255,bigint>>8&255,255&bigint]}(box.hex),boxRGB={r:r,g:g,b:b},eye=pick([{name:"Predator Gold",r:240,g:255,b:0},{name:"Predator Red",r:255,g:0,b:0}].map((opt=>({opt:opt,dist:colorDistanceRGB(opt,boxRGB)}))).sort(((a,b)=>a.dist-b.dist)).slice(0,3).map((e=>e.opt)));const maskName=box.name+" Mask",mask=[{name:"Obsidian Phantom Mask",r:12,g:4,b:22},{name:"Quantum Rift Mask",r:42,g:12,b:75},{name:"Infra Black Mask",r:8,g:0,b:0},{name:"Silent Haze Mask",r:66,g:66,b:88},{name:"Void Surge Mask",r:29,g:0,b:63},{name:"Toxic Ember Mask",r:201,g:16,b:88},{name:"Subzero Ghost Mask",r:54,g:109,b:194},{name:"Crimson Signal Mask",r:178,g:0,b:58},{name:"Xeno Pulse Mask",r:143,g:10,b:255},{name:"Graviton Ash Mask",r:80,g:80,b:90},{name:"Eclipse Drone Mask",r:33,g:18,b:33},{name:"Gamma Vortex Mask",r:120,g:0,b:255},{name:"Dead Circuit Mask",r:70,g:7,b:22},{name:"Dark Synapse Mask",r:35,g:0,b:95},{name:"Solar Flare Mask",r:255,g:90,b:0},{name:"Cryo Prism Mask",r:60,g:190,b:255},{name:"Neon Apex Mask",r:255,g:0,b:180},{name:"Spectral Nova Mask",r:144,g:144,b:255},{name:"Phantom Gloom Mask",r:18,g:18,b:30},{name:"Cyber Rune Mask",r:240,g:240,b:255}].find((m=>m.name===maskName)),mouth=pick([{name:"InfraWhite",r:256,g:230,b:132},{name:"White",r:255,g:255,b:255},{name:"InfraRed",r:247,g:175,b:175}]),maskStyle=pick(maskStyleNames);return{mouth:{...mouth,hex:rgbToHex(mouth.r,mouth.g,mouth.b)},eye:{...eye,hex:rgbToHex(eye.r,eye.g,eye.b)},box:{r:r,g:g,b:b,hex:box.hex},name:name.name,specialEffect:selectedEffect,mask:{...mask,hex:rgbToHex(mask.r,mask.g,mask.b),style:maskStyle},meta:{mouth:mouth.name,eye:eye.name,mask:mask.name,maskStyle:maskStyle,name:name.name,box:{name:box.name,hex:box.hex},specialEffect:selectedEffect.name,pantherId:pantherId,pantherName:PANTHER_NAMES_BY_ID[pantherId.toString()]||"Unknown"}}}p.preload=function(){pantherFont=p.loadFont("/content/74d8b14b61735bb0d738108ea9a9530406ffcbdd4b65a4206e0cfbc1ac1500e7i0")},p.keyPressed=function(){if("s"===p.key||"S"===p.key){const filename=`panther_${pantherId}_frame${p.frameCount}.png`;p.saveCanvas(filename,"png")}if("j"===p.key||"J"===p.key){const traits=panther?.traitNames||{},json=JSON.stringify(traits,null,2),blob=new Blob([json],{type:"application/json"}),a=document.createElement("a");a.href=URL.createObjectURL(blob),a.download=`panther_${pantherId}_traits.json`,document.body.appendChild(a),a.click(),document.body.removeChild(a)}},p.mousePressed=function(){clicked=!0},p.touchStarted=function(){clicked=!0},p.setup=function(){p.createCanvas(p.windowWidth,p.windowHeight,p.WEBGL);p.camera(0,0,400,0,0,0,0,1,0),panther=new Panther(p,baseModel.vertices,baseModel.faces,baseModel.MASK_STYLES,baseModel.FaceIndices,baseModel.mouthFaceIndices,pantherId),consoleTexture=p.createGraphics(1e3,1e3),typingStartTime=p.millis()},p.draw=function(){if(p.background(0),clicked){let dx=.001*p.movedX,dy=.001*p.movedY;moving=Math.abs(dx)>1e-4||Math.abs(dy)>1e-4,moving?(angleX=p.constrain(angleX+dx,-maxAngle,maxAngle),angleY=p.constrain(angleY+dy,-maxAngle,maxAngle)):(angleX=p.lerp(angleX,0,.05),angleY=p.lerp(angleY,0,.05))}let scaleFactor;scaleFactor=window.innerWidth>1e3?2.8:window.innerWidth>600?2.5:1.9,p.translate(0,25,0),p.rotateX(angleX),p.rotateY(angleY),p.scale(scaleFactor),p.ambientLight(30,30,40);let spotX=400*Math.sin(.02*p.frameCount),spotZ=400*Math.cos(.02*p.frameCount),oppositeSpotX=-spotX,oppositeSpotZ=-spotZ;p.spotLight(255,250,220,spotX,80,spotZ,-spotX,-10,-spotZ,p.PI/6,30),p.spotLight(220,200,255,oppositeSpotX,100,oppositeSpotZ,-oppositeSpotX,-10,-oppositeSpotZ,p.PI/6,30),p.pointLight(255,100,180,60,-150,60),p.pointLight(100,255,255,-60,-150,-60),p.pointLight(255,255,120,0,-2,0),p.directionalLight(80,120,180,Math.sin(.02*p.frameCount),-.02,Math.cos(.02*p.frameCount)),p.directionalLight(255,255,200,-Math.sin(.02*p.frameCount),.3,-Math.cos(.02*p.frameCount)),p.specularMaterial(20),p.shininess(20),angle=.02*p.frameCount,lightDir=p.createVector(.05,1,.3).normalize();let t=p.frameCount,fadeProgress=p.constrain(p.map(t,0,180,0,1),0,1);easeInOutQuad(fadeProgress),easeInOutQuad(fadeProgress<0?p.map(fadeProgress,0,.5,0,1):p.map(fadeProgress,.5,1,1,0)),fadeProgress<0||easeInOutQuad(p.map(fadeProgress,.5,1,0,1));"Tourbillon"===panther.traitNames.specialEffect?(panther.drawMaskExtrude(-.4,p),panther.drawModel(angle,lightDir),panther.drawTourbillon(p)):"Explosion"===panther.traitNames.specialEffect?(panther.drawMaskExtrude(-.4,p),panther.drawModel(angle,lightDir),panther.updateMorphing(p.millis(),p)):"Pointillist"===panther.traitNames.specialEffect?panther.drawPointillistPortraitFace(p):"ascii"===panther.traitNames.specialEffect?panther.drawFaceascii(p):"omdeyes"===panther.traitNames.specialEffect?(panther.drawMaskExtrude(-.4,p),panther.drawModel(angle,lightDir),panther.omdMask(p)):"lasereyes"===panther.traitNames.specialEffect?(panther.drawMaskExtrude(-.4,p),panther.drawModel(angle,lightDir),panther.drawLaserEyes(p)):"tron"===panther.traitNames.specialEffect?(panther.drawMaskExtrude(-.4,p),panther.drawModel(angle,lightDir),panther.drawTronInBox(p),panther.drawGlowOutline(p)):(panther.drawMaskExtrude(-.4,p),panther.drawModel(angle,lightDir)),!typingStarted&&p.millis()-typingStartTime>5e3&&(typingStarted=!0),panther.drawBase(),panther.drawHeadBlock()};class Panther{constructor(p,vertices,faces,VMASK_STYLES,FaceIndices,mouthFaceIndices,pantherId=0){this.p=p,this.vertices=vertices.map((([x,y,z])=>[x,-y,z])),this.faces=faces,this.originalVertices=this.vertices.map((v=>[...v])),this.speed=.01,this.maxRotation=30,this.modelCenter=this.p.createVector(0,0,0),this.eyeFaceIndices=FaceIndices,this.mouthFaceIndices=mouthFaceIndices,this.originalVertices=this.vertices.map((v=>[...v])),this.MASK_STYLES=VMASK_STYLES;const traits=generateTraits(currentSeed,pantherId,this.MASK_STYLES);this.TRAITS={mouth:traits.mouth,mask:traits.mask,name:traits.name,eyeVariants:{default:traits.eye},box:traits.box},this.currentEyeColor="default",this.traitNames=traits.meta,this.maskFaces=this.MASK_STYLES[traits.mask.style],this.isMorphing=!1,this.morphStartTime=p.millis(),this.morphStartTime=p.millis(),this.delayBeforeStart=1e4,this.morphDuration=1e4,this.pauseDuration=1e3,this.initSphereForm(),window.generateTraits=generateTraits}omdMask(p){p.push(),p.rotateY(p.radians(-this.maxRotation*p.sin(p.frameCount*this.speed))),p.noStroke(),p.colorMode(p.RGB),p.translate(0,3,.1);let t=.05*p.frameCount,leftEyeCenter=p.createVector(0,0,0),rightEyeCenter=p.createVector(0,0,0),leftCount=0,rightCount=0;for(let i=0;ithis.vertices[idx])),cx=(v[0][0]+v[1][0]+v[2][0])/3,cy=(v[0][1]+v[1][1]+v[2][1])/3,cz=(v[0][2]+v[1][2]+v[2][2])/3,center=p.createVector(cx,cy,cz);cx<0?(leftEyeCenter.add(center),leftCount++):(rightEyeCenter.add(center),rightCount++)}const drawJaggedEye=center=>{p.push(),p.translate(center.x,center.y,center.z+2),p.rotateZ(.5*t);var traitColor=this.TRAITS.eyeVariants[this.currentEyeColor],r=traitColor.r,g=traitColor.g,b=traitColor.b;p.fill(r,g,b),p.scale(.32),p.beginShape();for(let i=0;i<20;i++){const angle=p.TWO_PI*i/20,r=(i%2==0?9.5:20)/p.random(.8,1.3);p.vertex(p.cos(angle)*r,p.sin(angle)*r,0)}p.endShape(p.CLOSE),p.fill(255),p.beginShape();for(let i=0;i<20;i++){const angle=p.TWO_PI*i/20,r=(i%2==0?9.5*.8:16)*p.random(.09,1);p.vertex(p.cos(angle)*r,p.sin(angle)*r,.1)}p.endShape(p.CLOSE),p.pop()};leftCount>0&&drawJaggedEye(leftEyeCenter.div(leftCount)),rightCount>0&&drawJaggedEye(rightEyeCenter.div(rightCount)),p.pop()}drawFaceascii(p,duration=12e3,maxAngle=100,shapeType="bitcoin"){p.scale(.9);const viewDir=p.createVector(0,0,1),lightDir=p.createVector(0,0,1).normalize();let selectedShape,shapeOffsetX,shapeOffsetY;"bitcoin"===shapeType&&(selectedShape=["..#####.....","..#...##....","..#....#....","..#...##....","..#####.....","..#...##....","..#....#....","..#...##....","..#####....."],shapeOffsetX=8,shapeOffsetY=-20);if(!this.explosionStart){this.explosionStart=p.millis(),this.explosionFinished=!1;const pixelPositions=[],targetHeight=60,spacingX=60/selectedShape[0].length,spacingY=targetHeight/selectedShape.length,spacing=p.min(spacingX,spacingY),offsetX=-spacing*(selectedShape[0].length-1)/2+shapeOffsetX,offsetY=-spacing*(selectedShape.length-1)/2+shapeOffsetY;for(let y=0;ypixelPositions[i%pixelPositions.length]))}const totalElapsed=p.millis()-this.explosionStart;p.push(),p.rotateY(p.radians(-this.maxRotation*p.sin(p.frameCount*this.speed))),p.colorMode(p.HSB,360,100,255);p.frameCount;for(let i=0;i=6e3){const elapsed=totalElapsed-6e3,progress=p.constrain(elapsed/duration,0,1);morph=progress<.4?(t=progress/.4)*(2-t):progress<.6?1:easeInQuad(1-(progress-.6)/.4);const center=p5.Vector.add(v0,v1).add(v2).div(3),target=this.bTargets[i],offset=p5.Vector.sub(target,center).mult(morph);v0.add(offset),v1.add(offset),v2.add(offset),progress>=1&&!this.explosionFinished&&(this.explosionFinished=!0)}p.strokeWeight(.005),p.stroke(3,255,255,255);const normal=p5.Vector.cross(p5.Vector.sub(v1,v0),p5.Vector.sub(v2,v0)).normalize(),ambient=.001,diffuse=p.max(normal.dot(lightDir),0),reflectDir=p5.Vector.sub(p5.Vector.mult(normal,2*normal.dot(lightDir)),lightDir).normalize();let lighting=ambient+2.4*diffuse+1.2*p.pow(p.max(reflectDir.dot(viewDir),0),12)+10*p.pow(1-normal.dot(viewDir),2.5);lighting=p.constrain(lighting,0,1);let pulse,traitColor,glow=0;this.eyeFaceIndices.includes(i)?(pulse=p.sin(.5*p.frameCount+.3*i),glow=p.map(pulse,-1,1,-10,20)):this.maskFaces.includes(i)?glow=10:this.mouthFaceIndices.includes(i)?glow=0:(pulse=p.sin(.002*p.millis()+.7*i),glow=p.map(pulse,-1,1,-100,10));let r=0,g=0,b=0;if(this.eyeFaceIndices.includes(i))traitColor=this.TRAITS.eyeVariants[this.currentEyeColor],r=p.constrain((traitColor.r+glow)*lighting,0,255),g=p.constrain((traitColor.g+glow)*lighting,0,255),b=p.constrain((traitColor.b+.5*glow)*lighting,0,255);else if(this.mouthFaceIndices.includes(i))traitColor=this.TRAITS.mouth,r=traitColor.r*lighting,g=traitColor.g*lighting,b=traitColor.b*lighting;else if(this.maskFaces.includes(i)){traitColor=this.TRAITS.eyeVariants[this.currentEyeColor];const baseBoxColor=p.color(traitColor.r,traitColor.g,traitColor.b);p.colorMode(p.HSB);const h=p.hue(baseBoxColor),time=(p.saturation(baseBoxColor),p.brightness(baseBoxColor),p.millis()),satShift=p.sin(.01*time+i),briShift=p.cos(.001*time+i),shiftedBoxColor=p.color(h,p.constrain(10+satShift,90,100),p.constrain(10+briShift,90,100));p.colorMode(p.RGB),r=p.constrain((p.red(shiftedBoxColor)+2*glow)*lighting,0,255),g=p.constrain((p.green(shiftedBoxColor)+2*glow)*lighting,0,255),b=p.constrain((p.blue(shiftedBoxColor)+2*glow)*lighting,0,255)}else{traitColor=this.TRAITS.box;const baseBoxColor=p.color(traitColor.r,traitColor.g,traitColor.b);p.colorMode(p.HSB);const h=p.hue(baseBoxColor),time=(p.saturation(baseBoxColor),p.brightness(baseBoxColor),p.millis()),satShift=p.sin(.01*time+i),briShift=p.cos(.001*time+i),shiftedBoxColor=p.color(h,p.constrain(10+satShift,90,100),p.constrain(10+briShift,90,100));p.colorMode(p.RGB),r=p.constrain((p.red(shiftedBoxColor)+2*glow)*lighting,0,255),g=p.constrain((p.green(shiftedBoxColor)+2*glow)*lighting,0,255),b=p.constrain((p.blue(shiftedBoxColor)+2*glow)*lighting,0,255)}let normalColor=p.createVector(.5*(normal.x+1)*255,.5*(normal.y+1)*255,.5*(normal.z+1)*255);r=p.constrain(r+.1*normalColor.x,0,255),g=p.constrain(g+.1*normalColor.y,0,255),b=p.constrain(b+.1*normalColor.z,0,255),p.fill(r,g,b,200),p.beginShape(),p.vertex(v0.x,v0.y,v0.z),p.vertex(v1.x,v1.y,v1.z),p.vertex(v2.x,v2.y,v2.z),p.endShape(p.CLOSE)}var t;p.pop()}drawLaserEyes(){const p=this.p;p.translate(0,3,.1),p.push(),p.noStroke(),p.colorMode(p.HSB,60,100,100,255);let angleOffset=p.radians(this.maxRotation*p.sin(p.frameCount*this.speed)),cosA=p.cos(angleOffset),sinA=p.sin(angleOffset),baseHue=this.TRAITS.eyeVariants[this.currentEyeColor];for(let i=0;ithis.vertices[idx])).map((([x,y,z])=>[x*cosA-z*sinA,y,x*sinA+z*cosA]));p.push();for(let h=0;h<1;h++)for(let v=0;v<1;v++){let vOffset=.015*(v-0),beamAngle=angleOffset+.09*(h-0),cosSpread=p.cos(beamAngle),sinSpread=p.sin(beamAngle),hueShift=p.map(p.sin(.05*p.frameCount+.3*h+.2*v),-1,1,-5,5),flicker=p.map(p.sin(.3*p.frameCount+.8*i+.5*v),-1,1,-10,10);p.beginShape(p.TRIANGLES);for(let j=0;j<3;j++){const v0=rotatedVertices[j],v1=rotatedVertices[(j+1)%3];let apex=[(v0[0]+v1[0])/2,(v0[1]+v1[1])/2+120*vOffset- -25,(v0[2]+v1[2])/2+120],apexRotated=[apex[0]*cosSpread-apex[2]*sinSpread,apex[1],apex[0]*sinSpread+apex[2]*cosSpread];apexRotated[0]+=p.random(-1.5,1.5),apexRotated[1]+=p.random(-1.5,1.5),apexRotated[2]+=p.random(-1.5,1.5),p.fill(baseHue+hueShift,70,40+flicker,50),p.vertex(...v0),p.fill(baseHue+hueShift,70,40+flicker,50),p.vertex(...v1),p.fill(baseHue+hueShift,90,90+flicker,40),p.vertex(...apexRotated)}p.endShape(p.CLOSE),p.beginShape(p.TRIANGLES);for(let j=0;j<3;j++){const v0=rotatedVertices[j],v1=rotatedVertices[(j+1)%3];let apex=[(v0[0]+v1[0])/2,(v0[1]+v1[1])/2+120*vOffset- -25,(v0[2]+v1[2])/2+120],apexRotated=[apex[0]*cosSpread-apex[2]*sinSpread,apex[1],apex[0]*sinSpread+apex[2]*cosSpread];apexRotated[0]+=p.random(-.5,.5),apexRotated[1]+=p.random(-.5,.5),apexRotated[2]+=p.random(-.5,.5),p.fill(baseHue+hueShift,100,100+flicker,255),p.vertex(...v0),p.fill(baseHue+hueShift,90,50+flicker,80),p.vertex(...v1),p.fill(baseHue+hueShift,90,50+flicker,180),p.vertex(...apexRotated)}p.endShape(p.CLOSE)}p.pop()}p.pop()}drawPointillistPortraitFace(p,dotCount=3900,alpha=255){p.push(),p.rotateY(p.radians(-this.maxRotation*p.sin(p.frameCount*this.speed))),p.colorMode(p.RGB),p.noStroke();const light=p.createVector(1.4,1.6,.3).normalize(),explosionOrigin=p.createVector(0,-this.headHeight/2||0,0),allFaceIndices=Array.from({length:this.faces.length},((_,i)=>i)),usedFaces=new Set([...this.eyeFaceIndices,...this.mouthFaceIndices,...this.maskFaces]),baseFaceIndices=allFaceIndices.filter((i=>!usedFaces.has(i))),combinedFaces=[...this.eyeFaceIndices,...this.mouthFaceIndices,...this.maskFaces,...baseFaceIndices];for(let i=0;isum.add(p.createVector(...v)))),this.modelCenter=sum.div(this.originalVertices.length);this.sphereForm=this.originalVertices.map((v=>{let dir=p.createVector(v[0],v[1],v[2]).sub(this.modelCenter).normalize().mult(12);return[this.modelCenter.x+dir.x,this.modelCenter.y+dir.y,this.modelCenter.z+dir.z]}))}drawTronInBox(p){for(let i=0;i<4;i++){let dir;switch(i){case 0:dir=p.createVector(0,1,0);break;case 1:dir=p.createVector(-1,0,0);break;case 2:dir=p.createVector(0,-1,0);break;case 3:dir=p.createVector(1,0,0)}fireParticles.push({pos:p.createVector(p.random(-8,8),0,p.random(-8,8)),dir:dir,lifespan:255,length:p.random(8,16),speed:p.random(.8,1.5),glow:p.random(120,155),angle:p.random(p.TWO_PI)})}p.push(),p.translate(0,-10,0),p.colorMode(p.RGB,255),p.strokeWeight(.2),p.ambientLight(100);for(let i=fireParticles.length-1;i>=0;i--){let fp=fireParticles[i];if(fp.pos.add(p5.Vector.mult(fp.dir,fp.speed)),fp.lifespan-=3,fp.pos.x<-10||fp.pos.x>10||fp.pos.y<-30||fp.pos.y>30||fp.pos.z<-30||fp.pos.z>30){fireParticles.splice(i,1);continue}let alpha=p.map(fp.lifespan,255,0,255,0);p.stroke(p.color(0,255,255,alpha)),p.push(),p.translate(fp.pos.x,-fp.pos.y,fp.pos.z);let beamEnd=p5.Vector.mult(fp.dir,fp.length);p.beginShape(p.LINES),p.vertex(0,0,0),p.vertex(beamEnd.x,-beamEnd.y,beamEnd.z),p.endShape(),p.pop(),fp.lifespan<=0&&fireParticles.splice(i,1)}p.pop(),p.colorMode(p.RGB)}drawBase(){const p=this.p;p.push(),p.noStroke(),p.fill(255,0,0),p.box(10),p.pop()}drawGlowOutline(p){p.push(),p.colorMode(p.HSB,360,100,100,100),p.noFill(),p.rotateY(p.radians(-this.maxRotation*p.sin(p.frameCount*this.speed))),p.scale(.95);let t=.5*p.frameCount;for(let i=0;ithis.vertices[idx]));v[0][1],v[1][1],v[2][1],v[0][2],v[1][2],v[2][2];if(0===p.floor(p.sin(.1*t+.3*i)>0?1:0)&&p.random()<.8)continue;let flicker=p.sin(.15*t+.2*i),hueVal=181,glowAlpha=p.map(flicker,-1,1,40,100),glowBrightness=100,jitter=()=>p.random(-.6,.6);p.stroke(hueVal,100,glowBrightness,glowAlpha),p.strokeWeight(.4),p.beginShape();for(let j=0;j{let v=this.vertices[idx],scaled=[.9*v[0],.9*v[1],.9*v[2]],y1=scaled[1]*cosInc-scaled[2]*sinInc,z1=scaled[1]*sinInc+scaled[2]*cosInc;return p.createVector(scaled[0]*cosA-z1*sinA,y1,scaled[0]*sinA+z1*cosA)}));const normal=p5.Vector.cross(p5.Vector.sub(v1,v0),p5.Vector.sub(v2,v0)).normalize(),ambient=.001,diffuse=p.max(normal.dot(lightDir),0),reflectDir=p5.Vector.sub(p5.Vector.mult(normal,2*normal.dot(lightDir)),lightDir).normalize();let lighting=ambient+2.4*diffuse+1.2*p.pow(p.max(reflectDir.dot(viewDir),0),12)+10*p.pow(1-normal.dot(viewDir),2.5);lighting=p.constrain(lighting,0,1);let pulse,traitColor,glow=0;this.eyeFaceIndices.includes(i)?(pulse=p.sin(.5*p.frameCount+.3*i),glow=p.map(pulse,-1,1,-10,20)):this.maskFaces.includes(i)||this.mouthFaceIndices.includes(i)?glow=0:(pulse=p.sin(.002*p.millis()+.7*i),glow=p.map(pulse,-1,1,-100,10));let r=0,g=0,b=0;if(this.eyeFaceIndices.includes(i))traitColor=this.TRAITS.eyeVariants[this.currentEyeColor],r=p.constrain((traitColor.r+glow)*lighting,0,255),g=p.constrain((traitColor.g+glow)*lighting,0,255),b=p.constrain((traitColor.b+.5*glow)*lighting,0,255);else if(this.mouthFaceIndices.includes(i))traitColor=this.TRAITS.mouth,r=traitColor.r*lighting,g=traitColor.g*lighting,b=traitColor.b*lighting;else if(this.maskFaces.includes(i)){traitColor=this.TRAITS.eyeVariants[this.currentEyeColor];const baseColor=p.color(traitColor.r,traitColor.g,traitColor.b);p.colorMode(p.HSB);const h=p.hue(baseColor),s=p.saturation(baseColor),bri=p.brightness(baseColor),hueShift=(p.millis()/50+10*i)%360,shifted=p.color((h+hueShift)%360,p.min(1.2*s,100),p.min(1.2*bri,100));p.colorMode(p.RGB),r=p.red(shifted)*lighting,g=p.green(shifted)*lighting,b=p.blue(shifted)*lighting}else{traitColor=this.TRAITS.box;const baseBoxColor=p.color(traitColor.r,traitColor.g,traitColor.b);p.colorMode(p.HSB);const h=p.hue(baseBoxColor),time=(p.saturation(baseBoxColor),p.brightness(baseBoxColor),p.millis()),satShift=p.sin(.01*time+i),briShift=p.cos(.001*time+i),shiftedBoxColor=p.color(h,p.constrain(10+satShift,90,100),p.constrain(10+briShift,90,100));p.colorMode(p.RGB),r=p.constrain((p.red(shiftedBoxColor)+2*glow)*lighting,0,255),g=p.constrain((p.green(shiftedBoxColor)+2*glow)*lighting,0,255),b=p.constrain((p.blue(shiftedBoxColor)+2*glow)*lighting,0,255)}let normalColor=p.createVector(.5*(normal.x+1)*255,.5*(normal.y+1)*255,.5*(normal.z+1)*255);r=p.constrain(r+.1*normalColor.x,0,255),g=p.constrain(g+.1*normalColor.y,0,255),b=p.constrain(b+.1*normalColor.z,0,255),p.push(),p.fill(r,g,b,2048*alpha),p.noStroke(),p.beginShape(),face.forEach((idx=>{let v=this.vertices[idx],scaled=[.9*v[0],.9*v[1],.9*v[2]],y1=scaled[1]*cosInc-scaled[2]*sinInc,z1=scaled[1]*sinInc+scaled[2]*cosInc,rot=p.radians(this.maxRotation*p.sin(p.frameCount*this.speed)),x=scaled[0]*p.cos(rot)-z1*p.sin(rot),z=scaled[0]*p.sin(rot)+z1*p.cos(rot);p.vertex(x,y1,z)})),p.endShape(p.CLOSE),p.pop()}p.colorMode(p.RGB)}drawMaskExtrude(amount=2,p){p.push(),p.noStroke(),p.colorMode(p.RGB,255);const angleOffset=p.radians(this.maxRotation*p.sin(p.frameCount*this.speed)),cosA=p.cos(angleOffset),sinA=p.sin(angleOffset),inclination=p.radians(0),cosInc=p.cos(inclination),sinInc=p.sin(inclination),viewDir=p.createVector(0,0,1),lightDir=p.createVector(0,0,1).normalize();for(let i=0;ithis.vertices[idx])).map((v=>{const scaled=[.9*v[0],.9*v[1],.9*v[2]],y1=scaled[1]*cosInc-scaled[2]*sinInc,z1=scaled[1]*sinInc+scaled[2]*cosInc;return p.createVector(scaled[0]*cosA-z1*sinA,y1,scaled[0]*sinA+z1*cosA)})),normal=p5.Vector.cross(p5.Vector.sub(transformedV[1],transformedV[0]),p5.Vector.sub(transformedV[2],transformedV[0])).normalize(),extruded=transformedV.map((v=>v.copy().add(p5.Vector.mult(normal,amount)))),traitColor=this.TRAITS.mask,baseR=traitColor.r,baseG=traitColor.g,baseB=traitColor.b,ambient=.1,diffuse=p.max(normal.dot(lightDir),0),reflectDir=p5.Vector.sub(p5.Vector.mult(normal,2*normal.dot(lightDir)),lightDir).normalize();let lighting=ambient+1.4*diffuse+1.2*p.pow(p.max(reflectDir.dot(viewDir),0),60)+.08*p.pow(1-normal.dot(viewDir),2.5);lighting=p.constrain(lighting,.5,1.4);const r=p.lerp(0,baseR,lighting),g=p.lerp(0,baseG,lighting),b=p.lerp(0,baseB,lighting);p.fill(r,g,b,220),p.beginShape(),extruded.forEach((v=>p.vertex(v.x,v.y,v.z))),p.endShape(p.CLOSE)}p.pop()}updateMorphing(currentTime,p){if(!this.morphStarted&¤tTime>=this.delayBeforeStart&&(this.morphStarted=!0,this.morphStartTime=currentTime,this.isMorphing=!0),!this.isMorphing)return;let elapsed=currentTime-this.morphStartTime;if(elapsed{let s=this.sphereForm[i];return[p.lerp(v[0],s[0],morph),p.lerp(v[1],s[1],morph),p.lerp(v[2],s[2],morph)]}))}else if(elapsed[...v]))),this.vertices=this.pausedAt;else if(elapsed{let s=this.sphereForm[i];return[p.lerp(s[0],v[0],t),p.lerp(s[1],v[1],t),p.lerp(s[2],v[2],t)]}))}else this.vertices=this.originalVertices.map((v=>[...v])),this.isMorphing=!1,this.pausedAt=null;var x}drawTourbillon(p,duration=3e3,swirlRadius=20,tornadoHeight=40){this.tourbillonStart||(this.tourbillonStart=p.millis(),this.tourbillonFinished=!1);let totalElapsed=p.millis()-this.tourbillonStart;if(totalElapsed<5e3)return;let swirlProgress,elapsed=totalElapsed-5e3,progress=p.constrain(elapsed/duration,0,1);swirlProgress=easeInOutQuad(progress<=.6?progress/.6:1-(progress-.6)/.4),p.push(),p.rotateY(p.radians(this.maxRotation*p.sin(p.frameCount*this.speed))),p.noStroke(),p.colorMode(p.HSB,360,100,100);const center=p.createVector(0,0,0),hueShift=.5*p.frameCount%360;for(let i=0;i.7){let energyPulse=p.map(p.sin(.2*p.frameCount+i),-1,1,.5,1.5),sphereAlpha=p.map(swirlProgress,.01,1,0,80);p.fill((hueShift+60*heightFactor)%360,80,100,sphereAlpha),p.push(),p.translate(...this.vertices[i]),p.sphere(.2*energyPulse),p.pop()}}p.pop(),progress>=1&&!this.tourbillonFinished&&(this.vertices=this.originalVertices.map((v=>[...v])),this.tourbillonFinished=!0)}hueWobble(baseHue,id,range=2.5){const n=p.noise(.1*id);return p.constrain(baseHue+p.map(n,0,1,-range,range),0,60)}getColorForFace(i){const traitColor=this.eyeFaceIndices.includes(i)?this.TRAITS.eyeVariants?.[this.currentEyeColor]||this.TRAITS.eyeVariants?.default:this.mouthFaceIndices.includes(i)?this.TRAITS.mouth:this.maskFaces.includes(i)?this.TRAITS.mask:this.TRAITS.base;return{hue:traitColor?.hue??0,sat:traitColor?.sat??100,bri:traitColor?.bri??100}}drawBase(){const p=this.p;let boxColor=this.TRAITS.box;p.colorMode(p.RGB,255);let r=p.constrain(boxColor.r+-30,0,255),g=p.constrain(boxColor.g+-30,0,255),b=p.constrain(boxColor.b+-30,0,255);p.directionalLight(255,255,255,0,-1,0),p.directionalLight(255,255,255,0,1,0),p.noStroke(),p.push(),p.scale(.2),p.fill(r,g,b),p.translate(0,-301,0),p.rotateY(p.radians(this.maxRotation*p.sin(p.frameCount*this.speed))),p.box(253,10,253),p.pop(),p.push(),p.scale(.2),p.ambientMaterial(255),p.translate(0,-293,0),p.rotateY(p.radians(this.maxRotation*p.sin(p.frameCount*this.speed))),p.rotateX(p.HALF_PI),p.plane(100,5),p.pop(),p.push(),p.scale(.2),p.ambientMaterial(255),p.translate(0,-293,0),p.rotateY(p.radians(this.maxRotation*p.sin(p.frameCount*this.speed))),p.rotateX(p.HALF_PI),p.rotateZ(p.HALF_PI),p.plane(100,5),p.pop(),p.push(),p.scale(.2),p.fill(r,g,b),p.translate(0,102,0),p.rotateY(p.radians(this.maxRotation*p.sin(p.frameCount*this.speed))),p.box(252,110,252),p.push(),p.translate(0,0,126.1),p.textFont(pantherFont),p.textAlign(p.LEFT,p.TOP),p.push(),p.translate(-110,-40),p.fill(255),p.textSize(12),p.text("Ordinal",0,0),p.text("Panthers",0,20),p.text("on Chain",0,40),p.textSize(7),p.fill(180),p.text("ART ON BITCOIN",0,70),p.pop(),p.push(),p.translate(115,-10),p.textAlign(p.RIGHT,p.TOP),p.fill(this.TRAITS.eyeVariants[this.currentEyeColor].r,this.TRAITS.eyeVariants[this.currentEyeColor].g,this.TRAITS.eyeVariants[this.currentEyeColor].b),p.textSize(18),p.text(this.TRAITS.name,0,0),p.textSize(12),p.fill(160),p.text("#"+currentSeed,0,-30),p.pop(),p.push(),p.translate(-127,-10,-90),p.rotateY(-p.HALF_PI),p.fill(this.TRAITS.eyeVariants[this.currentEyeColor].r,this.TRAITS.eyeVariants[this.currentEyeColor].g,this.TRAITS.eyeVariants[this.currentEyeColor].b),p.textSize(18),p.text(this.TRAITS.name,20,0),p.textSize(12),p.fill(160),p.text("#"+currentSeed,40,-30),p.pop(),p.pop(),p.pop(),p.push(),p.scale(.2),p.translate(0,156,0),p.rotateY(p.radians(this.maxRotation*p.sin(p.frameCount*this.speed)));let glow=p.map(p.sin(.1*p.frameCount),-1,1,180,255);p.emissiveMaterial(0,glow,255);for(let i=0;i<4;i++)p.push(),i%2==0?(p.translate(0,0,0===i?127:-127),p.plane(253,1)):(p.translate(1===i?-127:127,0,0),p.rotateY(p.HALF_PI),p.plane(253,1)),p.pop();p.pop(),p.push(),p.scale(.2),p.ambientMaterial(255),p.translate(0,46,0),p.rotateY(p.radians(this.maxRotation*p.sin(p.frameCount*this.speed))),p.rotateX(p.HALF_PI),p.plane(100,5),p.pop(),p.push(),p.scale(.2),p.ambientMaterial(255),p.translate(0,46,0),p.rotateY(p.radians(this.maxRotation*p.sin(p.frameCount*this.speed))),p.rotateX(p.HALF_PI),p.rotateZ(p.HALF_PI),p.plane(100,5),p.pop()}}let fireParticles=[];function easeInQuad(t){return t*t}function easeInOutQuad(x){return x<.5?2*x*x:1-p.pow(-2*x+2,2)/2}}()}new p5(sketch);