1(function() { 2 function initCubeApp() { 3 try { 4 const container = document.getElementById('header-cube-app'); 5 if (!container || container.dataset.initialized) return; 6 container.dataset.initialized = 'true'; 7 8 const svgNS = 'http://www.w3.org/2000/svg'; 9 const svg = document.createElementNS(svgNS, 'svg'); 10 svg.setAttribute('class', 'cube-svg'); 11 svg.setAttribute('width', '64'); 12 svg.setAttribute('height', '64'); 13 svg.setAttribute('viewBox', '0 0 64 64'); 14 15 // 3D Cube Vertices centered at origin 16 const s = 14; 17 const baseVertices = [ 18 [-s, -s, -s], 19 [ s, -s, -s], 20 [ s, s, -s], 21 [-s, s, -s], 22 [-s, -s, s], 23 [ s, -s, s], 24 [ s, s, s], 25 [-s, s, s] 26 ]; 27 28 const edges = [ 29 [0,1], [1,2], [2,3], [3,0], 30 [4,5], [5,6], [6,7], [7,4], 31 [0,4], [1,5], [2,6], [3,7] 32 ]; 33 34 const lineElems = edges.map(() => { 35 const line = document.createElementNS(svgNS, 'line'); 36 line.setAttribute('class', 'cube-edge'); 37 svg.appendChild(line); 38 return line; 39 }); 40 41 container.appendChild(svg); 42 43 // Target 45, 45, 45 degree rotation matrix 44 const rad45 = Math.PI / 4; 45 function matMult3x3(A, B) { 46 const C = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]; 47 for (let i = 0; i < 3; i++) { 48 for (let j = 0; j < 3; j++) { 49 C[i][j] = A[i][0] * B[0][j] + A[i][1] * B[1][j] + A[i][2] * B[2][j]; 50 } 51 } 52 return C; 53 } 54 55 const Rx45 = [ 56 [1, 0, 0], 57 [0, Math.cos(rad45), -Math.sin(rad45)], 58 [0, Math.sin(rad45), Math.cos(rad45)] 59 ]; 60 const Ry45 = [ 61 [Math.cos(rad45), 0, Math.sin(rad45)], 62 [0, 1, 0], 63 [-Math.sin(rad45), 0, Math.cos(rad45)] 64 ]; 65 const Rz45 = [ 66 [Math.cos(rad45), -Math.sin(rad45), 0], 67 [Math.sin(rad45), Math.cos(rad45), 0], 68 [0, 0, 1] 69 ]; 70 const targetR = matMult3x3(Rz45, matMult3x3(Ry45, Rx45)); 71 72 // Initial orientation set directly to the 45 degree target position 73 let R = targetR; 74 75 const MIN_SPEED = 0.008; 76 const MAX_SPEED = 0.18; 77 const FRICTION = 0.985; 78 const HOLD_DURATION = 5000; // 5 seconds still hold time 79 const RAMP_DURATION = 1500; // 1.5 seconds smooth drift ramp up 80 81 function getRandomDirection() { 82 let rx = (Math.random() - 0.5) * 2; 83 let ry = (Math.random() - 0.5) * 2; 84 let rz = (Math.random() - 0.5) * 2; 85 if (Math.abs(rx) < 0.2) rx += (rx >= 0 ? 0.4 : -0.4); 86 if (Math.abs(ry) < 0.2) ry += (ry >= 0 ? 0.4 : -0.4); 87 const len = Math.hypot(rx, ry, rz) || 1; 88 return [rx / len, ry / len, rz / len]; 89 } 90 91 let vx = 0; 92 let vy = 0; 93 let vz = 0; 94 95 // Interaction & Animation States 96 let prevMousePos = null; 97 let centeringTimer = null; 98 let isCentering = false; 99 let isDriftRamping = false; 100 let driftRampStartTime = 0; 101 let startDriftVel = [0, 0, 0]; 102 let targetDriftVel = [0, 0, 0]; 103 104 function startDrifting() { 105 const dir = getRandomDirection(); 106 const targetSpeed = MIN_SPEED * 1.5; 107 targetDriftVel = [ 108 dir[0] * targetSpeed, 109 dir[1] * targetSpeed, 110 dir[2] * targetSpeed 111 ]; 112 startDriftVel = [vx, vy, vz]; 113 isDriftRamping = true; 114 driftRampStartTime = performance.now(); 115 } 116 117 function triggerCenter() { 118 if (centeringTimer) clearTimeout(centeringTimer); 119 isCentering = true; 120 isDriftRamping = false; 121 vx = 0; 122 vy = 0; 123 vz = 0; 124 centeringTimer = setTimeout(() => { 125 isCentering = false; 126 centeringTimer = null; 127 startDrifting(); 128 }, HOLD_DURATION); 129 } 130 131 function triggerFastSpin() { 132 if (centeringTimer) { 133 clearTimeout(centeringTimer); 134 centeringTimer = null; 135 } 136 isCentering = false; 137 isDriftRamping = false; 138 const dir = getRandomDirection(); 139 vx = dir[0] * MAX_SPEED; 140 vy = dir[1] * MAX_SPEED; 141 vz = dir[2] * MAX_SPEED; 142 } 143 144 container.style.cursor = 'pointer'; 145 146 // Start initial state held still at 45 degree angle for 5 seconds 147 triggerCenter(); 148 149 container.addEventListener('click', (e) => { 150 if (e.button === 0) { 151 triggerFastSpin(); 152 } 153 }); 154 155 container.addEventListener('contextmenu', (e) => { 156 e.preventDefault(); 157 triggerCenter(); 158 }); 159 160 container.addEventListener('mouseenter', (e) => { 161 prevMousePos = { x: e.clientX, y: e.clientY }; 162 }); 163 164 container.addEventListener('mouseleave', () => { 165 prevMousePos = null; 166 }); 167 168 container.addEventListener('mousemove', (e) => { 169 if (isCentering) return; 170 if (!prevMousePos) { 171 prevMousePos = { x: e.clientX, y: e.clientY }; 172 return; 173 } 174 const dx = e.clientX - prevMousePos.x; 175 const dy = e.clientY - prevMousePos.y; 176 prevMousePos = { x: e.clientX, y: e.clientY }; 177 178 const deltaDist = Math.hypot(dx, dy); 179 if (deltaDist > 0) { 180 if (isDriftRamping) { 181 isDriftRamping = false;
182 } 183 vy += dx * 0.0025; 184 vx -= dy * 0.0025; 185 vz += (dx - dy) * 0.001; 186 187 const currentSpeed = Math.hypot(vx, vy, vz); 188 if (currentSpeed > MAX_SPEED) { 189 const scale = MAX_SPEED / currentSpeed; 190 vx *= scale; 191 vy *= scale; 192 vz *= scale; 193 } 194 } 195 }); 196 197 function transpose3x3(M) { 198 return [ 199 [M[0][0], M[1][0], M[2][0]], 200 [M[0][1], M[1][1], M[2][1]], 201 [M[0][2], M[1][2], M[2][2]] 202 ]; 203 } 204 205 function animate() { 206 if (isCentering) { 207 const R_T = transpose3x3(R); 208 const R_rel = matMult3x3(targetR, R_T); 209 const trace = R_rel[0][0] + R_rel[1][1] + R_rel[2][2]; 210 const cosTheta = Math.min(1, Math.max(-1, (trace - 1) / 2)); 211 const theta = Math.acos(cosTheta); 212 213 if (theta < 0.001) { 214 R = targetR; 215 vx = 0; 216 vy = 0; 217 vz = 0; 218 } else { 219 let rx = R_rel[2][1] - R_rel[1][2]; 220 let ry = R_rel[0][2] - R_rel[2][0]; 221 let rz = R_rel[1][0] - R_rel[0][1]; 222 let len = Math.hypot(rx, ry, rz); 223 if (len < 1e-6) { 224 rx = 1; ry = 0; rz = 0; len = 1; 225 } 226 const w = Math.min(0.2, Math.max(0.01, theta * 0.15)); 227 vx = (rx / len) * w; 228 vy = (ry / len) * w; 229 vz = (rz / len) * w; 230 } 231 } else if (isDriftRamping) { 232 const elapsed = performance.now() - driftRampStartTime; 233 const t = Math.min(1, elapsed / RAMP_DURATION); 234 // Smoothstep interpolation t * t * (3 - 2 * t) 235 const easeT = t * t * (3 - 2 * t); 236 vx = startDriftVel[0] + (targetDriftVel[0] - startDriftVel[0]) * easeT; 237 vy = startDriftVel[1] + (targetDriftVel[1] - startDriftVel[1]) * easeT; 238 vz = startDriftVel[2] + (targetDriftVel[2] - startDriftVel[2]) * easeT; 239 if (t >= 1) { 240 isDriftRamping = false; 241 } 242 } else { 243 vx *= FRICTION; 244 vy *= FRICTION; 245 vz *= FRICTION; 246 247 let speed = Math.hypot(vx, vy, vz); 248 249 if (speed < MIN_SPEED) { 250 if (speed < 0.0001) { 251 const dir = getRandomDirection(); 252 vx = dir[0] * MIN_SPEED; 253 vy = dir[1] * MIN_SPEED; 254 vz = dir[2] * MIN_SPEED; 255 } else { 256 const scale = MIN_SPEED / speed; 257 vx *= scale; 258 vy *= scale; 259 vz *= scale; 260 } 261 speed = MIN_SPEED; 262 } else if (speed > MAX_SPEED) { 263 const scale = MAX_SPEED / speed; 264 vx *= scale; 265 vy *= scale; 266 vz *= scale; 267 speed = MAX_SPEED; 268 } 269 } 270 271 const speed = Math.hypot(vx, vy, vz); 272 if (speed > 0.00001) { 273 const ax = vx / speed, ay = vy / speed, az = vz / speed; 274 const cos = Math.cos(speed), sin = Math.sin(speed), c1 = 1 - cos; 275 276 const dR = [ 277 [cos + ax*ax*c1, ax*ay*c1 - az*sin, ax*az*c1 + ay*sin], 278 [ay*ax*c1 + az*sin, cos + ay*ay*c1, ay*az*c1 - ax*sin], 279 [az*ax*c1 - ay*sin, az*ay*c1 + ax*sin, cos + az*az*c1] 280 ]; 281 282 const nextR = [ 283 [0, 0, 0], 284 [0, 0, 0], 285 [0, 0, 0] 286 ]; 287 for (let i = 0; i < 3; i++) { 288 for (let j = 0; j < 3; j++) { 289 nextR[i][j] = dR[i][0] * R[0][j] + dR[i][1] * R[1][j] + dR[i][2] * R[2][j]; 290 } 291 } 292 R = nextR; 293 } 294 295 const proj = baseVertices.map(v => [ 296 32 + (R[0][0]*v[0] + R[0][1]*v[1] + R[0][2]*v[2]), 297 32 + (R[1][0]*v[0] + R[1][1]*v[1] + R[1][2]*v[2]) 298 ]); 299
300 edges.forEach((edge, idx) => { 301 const p1 = proj[edge[0]]; 302 const p2 = proj[edge[1]]; 303 lineElems[idx].setAttribute('x1', p1[0].toFixed(2)); 304 lineElems[idx].setAttribute('y1', p1[1].toFixed(2)); 305 lineElems[idx].setAttribute('x2', p2[0].toFixed(2)); 306 lineElems[idx].setAttribute('y2', p2[1].toFixed(2)); 307 }); 308 309 requestAnimationFrame(animate); 310 } 311 312 requestAnimationFrame(animate); 313 } catch(err) { 314 console.error("Cube init error:", err); 315 } 316 } 317 318 if (document.readyState === 'loading') { 319 document.addEventListener('DOMContentLoaded', initCubeApp); 320 } 321 initCubeApp(); 322 setTimeout(initCubeApp, 100); 323 setTimeout(initCubeApp, 500); 324})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.