| Server IP : 127.0.0.1 / Your IP : 216.73.216.109 Web Server : Apache/2.4.54 (Win64) OpenSSL/1.1.1q PHP/8.1.10 System : Windows NT DESKTOP-E5T4RUN 10.0 build 19045 (Windows 10) AMD64 User : SERVERWEB ( 0) PHP Version : 8.1.10 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : C:/Users/SERVERWEB/AppData/Local/Microsoft/OneDrive/26.074.0420.0001/WebAssets/ |
Upload File : |
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Barber Pole</title><link rel="stylesheet" href="/static/tokens/scrollbar.css"><style>*{margin:0;padding:0;box-sizing:border-box}body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',system-ui,sans-serif;background:#111;height:100vh;overflow:hidden}canvas{display:block}#loading{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:#111;color:#888;font-size:14px;z-index:20;transition:opacity .6s}#loading.hidden{opacity:0;pointer-events:none}.controls{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);display:flex;align-items:center;gap:14px;z-index:10;background:rgba(0,0,0,.75);border:1px solid rgba(255,255,255,.1);border-radius:10px;padding:10px 20px;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.ctrl-group{display:flex;align-items:center;gap:6px}.ctrl-label{font-size:10px;text-transform:uppercase;letter-spacing:.8px;color:#555;margin-right:2px}.ctrl-btn{background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.12);color:rgba(255,255,255,.5);border-radius:6px;cursor:pointer;transition:background .15s,color .15s;padding:5px 10px;font-size:12px;font-family:inherit;line-height:1}.ctrl-btn:hover{background:rgba(255,255,255,.14);color:rgba(255,255,255,.85)}.ctrl-btn.active{background:rgba(123,157,255,.2);border-color:#7b9dff;color:#7b9dff}.ctrl-sep{width:1px;height:24px;background:rgba(255,255,255,.08)}.ctrl-value{font-size:14px;color:#7b9dff;font-weight:600;min-width:16px;text-align:center}</style><script src="/static/view-lib/theme-sync.js"></script><script src="/static/view-lib/user-activity.js"></script><script>ViewLib.ThemeSync.applyDirFromParent()</script></head><body><div id="loading">Loading photos...</div><canvas id="c"></canvas><div class="controls"><div class="ctrl-group"><span class="ctrl-label">Orient</span> <button class="ctrl-btn active" id="btnV">Vertical</button> <button class="ctrl-btn" id="btnH">Horizontal</button></div><div class="ctrl-sep"></div><div class="ctrl-group"><span class="ctrl-label">Poles</span> <button class="ctrl-btn" id="polesDown">−</button> <span class="ctrl-value" id="poleCountEl">4</span> <button class="ctrl-btn" id="polesUp">+</button></div><div class="ctrl-sep"></div><div class="ctrl-group"><span class="ctrl-label">Speed</span> <button class="ctrl-btn" id="speedDown">−</button> <span class="ctrl-value" id="speedEl">1</span> <button class="ctrl-btn" id="speedUp">+</button></div><div class="ctrl-sep"></div><div class="ctrl-group"><span class="ctrl-label">Spiral</span> <button class="ctrl-btn" id="spiralDown">−</button> <span class="ctrl-value" id="spiralEl">0</span> <button class="ctrl-btn" id="spiralUp">+</button></div></div><script src="/static/platform/vibe-telemetry.js"></script><script src="/static/platform/od4-fetch.js"></script><script src="/static/platform/icon.js"></script><script src="/static/view-lib/thumbnail.js"></script><script>const TEX_COLS=8,TEX_ROWS=16,PHOTO_PX=128,TEX_W=1024,TEX_H=2048;let orientation="vertical",numPoles=4,speedLevel=1,spiralLevel=0,rotation=0;const canvas=document.getElementById("c");canvas.width=window.innerWidth,canvas.height=window.innerHeight;const gl=canvas.getContext("webgl",{antialias:!1,alpha:!1}),vsSource="\nattribute vec2 aPos;\nvarying vec2 vUV;\nvoid main() {\n vUV = aPos * 0.5 + 0.5;\n gl_Position = vec4(aPos, 0.0, 1.0);\n}",fsSource="\nprecision mediump float;\n\nuniform sampler2D uTex;\nuniform vec2 uResolution;\nuniform float uPoleCenter;\nuniform float uPoleRadius;\nuniform float uRotation;\nuniform float uSpiral;\nuniform float uDirection;\nuniform float uHorizontal;\nuniform float uPhotoScale;\n\nvarying vec2 vUV;\n\nvoid main() {\n vec2 pixel = vUV * uResolution;\n\n // Swap axes for horizontal poles\n float coord, along;\n if (uHorizontal > 0.5) {\n coord = pixel.y;\n along = pixel.x;\n } else {\n coord = pixel.x;\n along = pixel.y;\n }\n\n float dx = (coord - uPoleCenter) / uPoleRadius;\n\n // Outside the cylinder\n if (abs(dx) > 1.0) {\n gl_FragColor = vec4(0.067, 0.067, 0.067, 1.0);\n return;\n }\n\n float angle = asin(dx);\n\n // Diffuse shading — squared cosine for more dramatic falloff\n float diff = cos(angle);\n float shade = diff * diff * 0.7 + diff * 0.3;\n\n // Specular highlight — glossy streak down the middle\n float spec = pow(max(0.0, diff), 32.0) * 0.2;\n\n // Texture coordinates\n // u wraps around the cylinder, rotation animates it\n float u = angle / 6.28318 + uRotation * uDirection;\n\n // v goes along the pole length; the spiral term offsets v\n // by the angular position, creating diagonal stripes\n float v = along * uPhotoScale + angle * uSpiral * uDirection;\n\n vec4 color = texture2D(uTex, vec2(u, v));\n gl_FragColor = vec4(color.rgb * shade + spec, 1.0);\n}";function compileShader(e,t){const n=gl.createShader(t);return gl.shaderSource(n,e),gl.compileShader(n),gl.getShaderParameter(n,gl.COMPILE_STATUS)||console.error("Shader error:",gl.getShaderInfoLog(n)),n}const prog=gl.createProgram();gl.attachShader(prog,compileShader(vsSource,gl.VERTEX_SHADER)),gl.attachShader(prog,compileShader(fsSource,gl.FRAGMENT_SHADER)),gl.linkProgram(prog),gl.useProgram(prog);const buf=gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER,buf),gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,1,1]),gl.STATIC_DRAW);const aPos=gl.getAttribLocation(prog,"aPos");gl.enableVertexAttribArray(aPos),gl.vertexAttribPointer(aPos,2,gl.FLOAT,!1,0,0);const loc={};["uTex","uResolution","uPoleCenter","uPoleRadius","uRotation","uSpiral","uDirection","uHorizontal","uPhotoScale"].forEach(e=>{loc[e]=gl.getUniformLocation(prog,e)});const texCanvas=document.createElement("canvas");texCanvas.width=1024,texCanvas.height=2048;const texCtx=texCanvas.getContext("2d");texCtx.fillStyle="#1a1a1a",texCtx.fillRect(0,0,1024,2048);let glTexture=null;function uploadTexture(){glTexture||(glTexture=gl.createTexture()),gl.bindTexture(gl.TEXTURE_2D,glTexture),gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,!0),gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,gl.RGBA,gl.UNSIGNED_BYTE,texCanvas),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.REPEAT),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.REPEAT),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.LINEAR)}async function init(){const e=await fetch(ViewLib.Thumbnail.buildThumbnailListUrl(0,128)),t=await e.json(),n=ViewLib.Thumbnail.filterValidThumbnails(t.items);for(let e=n.length-1;e>0;e--){const t=Math.floor(Math.random()*(e+1));[n[e],n[t]]=[n[t],n[e]]}const o=[];for(let e=0;e<128;e++){const t=n[e%n.length];o.push(window._od4FetchThumbnailBlob(t.thumbnailId).then(e=>new Promise(t=>{const n=new Image;n.onload=()=>t(n),n.onerror=()=>t(null),n.src=e})).catch(()=>null))}const l=await Promise.all(o);for(let e=0;e<128;e++){if(!l[e])continue;const t=e%8,n=Math.floor(e/8);texCtx.drawImage(l[e],128*t,128*n,128,128)}uploadTexture(),document.getElementById("loading").classList.add("hidden"),lastTime=0,requestAnimationFrame(animate)}let lastTime=0;function animate(e){const t=lastTime?(e-lastTime)/1e3:.016;lastTime=e,rotation+=t*speedLevel*.06;const n=canvas.width,o=canvas.height,l="vertical"===orientation;gl.viewport(0,0,n,o),gl.clearColor(.067,.067,.067,1),gl.clear(gl.COLOR_BUFFER_BIT),gl.uniform2f(loc.uResolution,n,o),gl.uniform1f(loc.uHorizontal,l?0:1),gl.uniform1f(loc.uRotation,rotation),gl.uniform1f(loc.uSpiral,.12*spiralLevel);const a=(l?n:o)/numPoles,i=.47*a,r=Math.max(40,Math.min(120,i/1.8));gl.uniform1f(loc.uPhotoScale,1/(16*r)),gl.uniform1f(loc.uPoleRadius,i),gl.activeTexture(gl.TEXTURE0),gl.bindTexture(gl.TEXTURE_2D,glTexture),gl.uniform1i(loc.uTex,0),gl.enable(gl.SCISSOR_TEST);for(let e=0;e<numPoles;e++){const t=a*(e+.5);gl.uniform1f(loc.uPoleCenter,t),gl.uniform1f(loc.uDirection,e%2==0?1:-1);const r=2;if(l){const e=Math.max(0,Math.floor(t-i-r)),l=Math.min(n-e,Math.ceil(2*i+2*r));gl.scissor(e,0,l,o)}else{const e=Math.max(0,Math.floor(t-i-r)),l=Math.min(o-e,Math.ceil(2*i+2*r));gl.scissor(0,e,n,l)}gl.drawArrays(gl.TRIANGLE_STRIP,0,4)}gl.disable(gl.SCISSOR_TEST),requestAnimationFrame(animate)}document.getElementById("btnV").onclick=()=>{"vertical"!==orientation&&(orientation="vertical",document.getElementById("btnV").classList.add("active"),document.getElementById("btnH").classList.remove("active"))},document.getElementById("btnH").onclick=()=>{"horizontal"!==orientation&&(orientation="horizontal",document.getElementById("btnH").classList.add("active"),document.getElementById("btnV").classList.remove("active"))},document.getElementById("polesDown").onclick=()=>{numPoles<=1||(numPoles--,document.getElementById("poleCountEl").textContent=numPoles)},document.getElementById("polesUp").onclick=()=>{numPoles>=8||(numPoles++,document.getElementById("poleCountEl").textContent=numPoles)},document.getElementById("speedDown").onclick=()=>{speedLevel<=1||(speedLevel--,document.getElementById("speedEl").textContent=speedLevel)},document.getElementById("speedUp").onclick=()=>{speedLevel>=6||(speedLevel++,document.getElementById("speedEl").textContent=speedLevel)},document.getElementById("spiralDown").onclick=()=>{spiralLevel<=0||(spiralLevel--,document.getElementById("spiralEl").textContent=spiralLevel)},document.getElementById("spiralUp").onclick=()=>{spiralLevel>=8||(spiralLevel++,document.getElementById("spiralEl").textContent=spiralLevel)},window.addEventListener("resize",()=>{canvas.width=window.innerWidth,canvas.height=window.innerHeight}),init()</script></body></html>