| .control-group input[type="checkbox"] { |
| width: auto; |
| margin-right: 5px; |
| }<!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>NGVT: Vortex Torus - Compatible Version</title> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> |
| <style> |
| body { |
| margin: 0; |
| background: linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 50%, #16213e 100%); |
| font-family: 'Arial', sans-serif; |
| overflow: hidden; |
| color: white; |
| } |
| |
| #container { |
| position: relative; |
| width: 100vw; |
| height: 100vh; |
| } |
| |
| #info { |
| position: absolute; |
| top: 20px; |
| left: 20px; |
| z-index: 100; |
| background: rgba(0, 0, 0, 0.9); |
| padding: 20px; |
| border-radius: 10px; |
| border: 1px solid #00ffff; |
| box-shadow: 0 0 20px rgba(0, 255, 255, 0.3); |
| max-width: 280px; |
| } |
| |
| #info h1 { |
| margin: 0 0 10px 0; |
| color: #00ffff; |
| font-size: 20px; |
| text-shadow: 0 0 10px rgba(0, 255, 255, 0.5); |
| } |
| |
| #info h2 { |
| margin: 15px 0 5px 0; |
| color: #ff6b6b; |
| font-size: 14px; |
| } |
| |
| #info p { |
| margin: 3px 0; |
| font-size: 12px; |
| color: #cccccc; |
| } |
| |
| #controls { |
| position: absolute; |
| bottom: 20px; |
| left: 20px; |
| z-index: 100; |
| background: rgba(0, 0, 0, 0.9); |
| padding: 15px; |
| border-radius: 10px; |
| border: 1px solid #ff6b6b; |
| } |
| |
| .control-group { |
| margin: 10px 0; |
| } |
| |
| .control-group label { |
| display: block; |
| color: #ff6b6b; |
| font-size: 11px; |
| margin-bottom: 5px; |
| } |
| |
| .control-group input { |
| width: 120px; |
| } |
| |
| #legend { |
| position: absolute; |
| bottom: 20px; |
| right: 20px; |
| z-index: 100; |
| background: rgba(0, 0, 0, 0.9); |
| padding: 15px; |
| border-radius: 10px; |
| border: 1px solid #4ecdc4; |
| } |
| |
| .legend-item { |
| display: flex; |
| align-items: center; |
| margin: 6px 0; |
| } |
| |
| .legend-color { |
| width: 16px; |
| height: 16px; |
| margin-right: 8px; |
| border-radius: 3px; |
| } |
| |
| .legend-text { |
| font-size: 11px; |
| color: #cccccc; |
| } |
| |
| #error-message { |
| position: absolute; |
| top: 50%; |
| left: 50%; |
| transform: translate(-50%, -50%); |
| background: rgba(255, 0, 0, 0.1); |
| border: 1px solid #ff6b6b; |
| padding: 20px; |
| border-radius: 10px; |
| color: #ff6b6b; |
| display: none; |
| text-align: center; |
| } |
| |
| #fallback-canvas { |
| display: none; |
| border: 1px solid #4ecdc4; |
| } |
| </style> |
| </head> |
| <body> |
| <div id="container"> |
| <div id="error-message"> |
| <h3>WebGL Not Available</h3> |
| <p>Your browser doesn't support WebGL or it's disabled.</p> |
| <p>Falling back to 2D visualization...</p> |
| </div> |
| |
| <div id="info"> |
| <h1>NGVT Vortex Torus</h1> |
| <p><strong>Directional Flow Visualization</strong></p> |
| |
| <h2>Torus Parameters:</h2> |
| <p>Rings: 1-5 concentric</p> |
| <p>Particles: ON SAME PLANE</p> |
| <p>Drag to orbit manually</p> |
| |
| <h2>Code Flow:</h2> |
| <p>6 colored spiral bands</p> |
| <p>Tight/loose spiral control</p> |
| <p>Multi-ring vortex</p> |
| |
| <h2>Performance:</h2> |
| <p>SWE-bench: 98.33%</p> |
| <p>Speed: 7.4× faster</p> |
| </div> |
| |
| <div id="controls"> |
| <h3 style="color: #ff6b6b; margin-top: 0; font-size: 14px;">Flow Controls</h3> |
| <div class="control-group"> |
| <label>Flow Speed:</label> |
| <input type="range" id="flowSpeed" min="0.1" max="2.0" step="0.1" value="0.8"> |
| </div> |
| <div class="control-group"> |
| <label>Number of Rings:</label> |
| <input type="range" id="numberOfRings" min="1" max="5" step="1" value="1"> |
| </div> |
| <div class="control-group"> |
| <label>Spiral Pitch:</label> |
| <input type="range" id="spiralPitch" min="0.5" max="4.0" step="0.1" value="2.0"> |
| </div> |
| <div class="control-group"> |
| <label>Spiral Tightness:</label> |
| <input type="range" id="spiralTightness" min="2" max="20" step="1" value="8"> |
| </div> |
| <div class="control-group"> |
| <label>Ring Size:</label> |
| <input type="range" id="ringSize" min="2.0" max="8.0" step="0.2" value="4.0"> |
| </div> |
| <div class="control-group"> |
| <label>Zoom Level:</label> |
| <input type="range" id="zoomLevel" min="5.0" max="30.0" step="1.0" value="15.0"> |
| </div> |
| <div class="control-group"> |
| <label>Rotation:</label> |
| <input type="checkbox" id="rotationToggle" checked> |
| <span style="color: #ff6b6b; font-size: 11px; margin-left: 5px;">Auto Orbit</span> |
| </div> |
| <div class="control-group"> |
| <label>Vortex Intensity:</label> |
| <input type="range" id="vortexIntensity" min="0.1" max="1.5" step="0.1" value="0.8"> |
| </div> |
| </div> |
| |
| <div id="legend"> |
| <h3 style="color: #4ecdc4; margin-top: 0; font-size: 14px;">Flow Elements</h3> |
| <div class="legend-item"> |
| <div class="legend-color" style="background: #00ffff; box-shadow: 0 0 8px #00ffff;"></div> |
| <div class="legend-text">Functions</div> |
| </div> |
| <div class="legend-item"> |
| <div class="legend-color" style="background: #ff6b6b; box-shadow: 0 0 8px #ff6b6b;"></div> |
| <div class="legend-text">Variables</div> |
| </div> |
| <div class="legend-item"> |
| <div class="legend-color" style="background: #4ecdc4; box-shadow: 0 0 8px #4ecdc4;"></div> |
| <div class="legend-text">Keywords</div> |
| </div> |
| <div class="legend-item"> |
| <div class="legend-color" style="background: #ffd93d; box-shadow: 0 0 8px #ffd93d;"></div> |
| <div class="legend-text">Operators</div> |
| </div> |
| <div class="legend-item"> |
| <div class="legend-color" style="background: #a8e6cf; box-shadow: 0 0 8px #a8e6cf;"></div> |
| <div class="legend-text">Comments</div> |
| </div> |
| <div class="legend-item"> |
| <div class="legend-color" style="background: #dda0dd; box-shadow: 0 0 8px #dda0dd;"></div> |
| <div class="legend-text">Strings</div> |
| </div> |
| </div> |
| |
| <canvas id="fallback-canvas" width="800" height="600"></canvas> |
| </div> |
|
|
| <script> |
| |
| let scene, camera, renderer; |
| let torus, codeSegments = []; |
| let animationId; |
| |
| |
| let flowSpeed = 0.8; |
| let spiralPitch = 2.0; |
| let ringSize = 4.0; |
| let vortexIntensity = 0.8; |
| let zoomLevel = 15.0; |
| let rotationEnabled = true; |
| let numberOfRings = 1; |
| let spiralTightness = 8; |
| |
| |
| let isDragging = false; |
| let previousMousePosition = { x: 0, y: 0 }; |
| let cameraAngleX = 0; |
| let cameraAngleY = 0; |
| |
| |
| function isWebGLAvailable() { |
| try { |
| const canvas = document.createElement('canvas'); |
| return !!(window.WebGLRenderingContext && ( |
| canvas.getContext('webgl') || |
| canvas.getContext('experimental-webgl') || |
| canvas.getContext('webgl2') |
| )); |
| } catch (e) { |
| return false; |
| } |
| } |
| |
| |
| function initThreeJS() { |
| try { |
| |
| scene = new THREE.Scene(); |
| camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); |
| |
| |
| const rendererOptions = { |
| antialias: false, |
| alpha: true, |
| powerPreference: "default" |
| }; |
| |
| renderer = new THREE.WebGLRenderer(rendererOptions); |
| renderer.setSize(window.innerWidth, window.innerHeight); |
| renderer.setClearColor(0x000000, 0); |
| |
| |
| const testGeometry = new THREE.BoxGeometry(1, 1, 1); |
| const testMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); |
| const testMesh = new THREE.Mesh(testGeometry, testMaterial); |
| scene.add(testMesh); |
| renderer.render(scene, camera); |
| scene.remove(testMesh); |
| |
| document.getElementById('container').appendChild(renderer.domElement); |
| return true; |
| |
| } catch (error) { |
| console.error('Three.js initialization failed:', error); |
| return false; |
| } |
| } |
| |
| |
| function initFallbackCanvas() { |
| const canvas = document.getElementById('fallback-canvas'); |
| canvas.style.display = 'block'; |
| canvas.style.position = 'absolute'; |
| canvas.style.top = '50%'; |
| canvas.style.left = '50%'; |
| canvas.style.transform = 'translate(-50%, -50%)'; |
| |
| const ctx = canvas.getContext('2d'); |
| let time = 0; |
| |
| function drawFallback() { |
| ctx.clearRect(0, 0, canvas.width, canvas.height); |
| |
| |
| const centerX = canvas.width / 2; |
| const centerY = canvas.height / 2; |
| const outerRadius = ringSize * 30; |
| const innerRadius = outerRadius * 0.4; |
| |
| |
| ctx.strokeStyle = '#4ecdc4'; |
| ctx.lineWidth = 2; |
| ctx.beginPath(); |
| ctx.arc(centerX, centerY, outerRadius, 0, Math.PI * 2); |
| ctx.stroke(); |
| |
| |
| ctx.beginPath(); |
| ctx.arc(centerX, centerY, innerRadius, 0, Math.PI * 2); |
| ctx.stroke(); |
| |
| |
| const colors = ['#00ffff', '#ff6b6b', '#4ecdc4', '#ffd93d', '#a8e6cf', '#dda0dd']; |
| |
| for (let i = 0; i < 60; i++) { |
| const angle = (i / 60) * Math.PI * 2 + time * flowSpeed * 0.02; |
| const spiralAngle = angle * spiralPitch + time * flowSpeed * 0.05; |
| |
| const radius = innerRadius + (outerRadius - innerRadius) * |
| (0.5 + 0.4 * Math.sin(spiralAngle)); |
| |
| const x = centerX + radius * Math.cos(angle); |
| const y = centerY + radius * Math.sin(angle); |
| |
| const colorIndex = Math.floor(i / 10) % colors.length; |
| const intensity = 0.5 + 0.5 * Math.sin(spiralAngle + time * 0.01); |
| |
| ctx.fillStyle = colors[colorIndex]; |
| ctx.globalAlpha = intensity; |
| ctx.beginPath(); |
| ctx.arc(x, y, 3, 0, Math.PI * 2); |
| ctx.fill(); |
| } |
| |
| ctx.globalAlpha = 1; |
| |
| |
| ctx.fillStyle = '#00ffff'; |
| ctx.font = '16px Arial'; |
| ctx.fillText('NGVT Vortex Torus (2D Fallback)', 20, 30); |
| ctx.fillStyle = '#ff6b6b'; |
| ctx.font = '12px Arial'; |
| ctx.fillText('Spiral flow visualization', 20, 50); |
| |
| time++; |
| requestAnimationFrame(drawFallback); |
| } |
| |
| drawFallback(); |
| } |
| |
| |
| function createSimplifiedVisualization() { |
| |
| codeSegments.forEach(segment => scene.remove(segment)); |
| codeSegments.length = 0; |
| |
| |
| const torusesToRemove = []; |
| scene.traverse((child) => { |
| if (child.userData && child.userData.isTorusRing) { |
| torusesToRemove.push(child); |
| } |
| }); |
| torusesToRemove.forEach(torus => scene.remove(torus)); |
| |
| |
| const ambientLight = new THREE.AmbientLight(0x404040, 0.8); |
| scene.add(ambientLight); |
| |
| const directionalLight = new THREE.DirectionalLight(0x00ffff, 0.5); |
| directionalLight.position.set(0, 5, 5); |
| scene.add(directionalLight); |
| |
| |
| for (let ringIndex = 0; ringIndex < numberOfRings; ringIndex++) { |
| const currentRingSize = ringSize - (ringIndex * 1.2); |
| if (currentRingSize <= 1.0) break; |
| |
| const torusGeometry = new THREE.TorusGeometry(currentRingSize, 0.6, 8, 32); |
| const ringHue = (ringIndex * 60) % 360; |
| const torusMaterial = new THREE.MeshBasicMaterial({ |
| color: new THREE.Color().setHSL(ringHue/360, 0.7, 0.5), |
| transparent: true, |
| opacity: 0.2 + (ringIndex * 0.1), |
| wireframe: true |
| }); |
| |
| const torusMesh = new THREE.Mesh(torusGeometry, torusMaterial); |
| torusMesh.userData.isTorusRing = true; |
| torusMesh.userData.ringIndex = ringIndex; |
| scene.add(torusMesh); |
| |
| if (ringIndex === 0) torus = torusMesh; |
| } |
| |
| |
| const colors = [0x00ffff, 0xff6b6b, 0x4ecdc4, 0xffd93d, 0xa8e6cf, 0xdda0dd]; |
| const particlesPerRing = 200; |
| |
| for (let ringIndex = 0; ringIndex < numberOfRings; ringIndex++) { |
| const currentRingSize = ringSize - (ringIndex * 1.2); |
| if (currentRingSize <= 1.0) break; |
| |
| for (let i = 0; i < particlesPerRing; i++) { |
| const particleGeometry = new THREE.SphereGeometry(0.03 + (ringIndex * 0.01), 6, 6); |
| const particleMaterial = new THREE.MeshBasicMaterial({ |
| color: colors[(i + ringIndex) % colors.length], |
| transparent: true, |
| opacity: 0.8 |
| }); |
| |
| const particle = new THREE.Mesh(particleGeometry, particleMaterial); |
| |
| |
| const spiralIndex = i % 6; |
| const t = (i / particlesPerRing); |
| const u = t * Math.PI * 2 * spiralTightness + (spiralIndex * Math.PI * 2 / 6); |
| |
| const v = (spiralIndex * Math.PI * 2 / 6) + (t * Math.PI * 2 * spiralPitch); |
| |
| const x = (currentRingSize + 0.6 * Math.cos(v)) * Math.cos(u); |
| const y = (currentRingSize + 0.6 * Math.cos(v)) * Math.sin(u); |
| const z = 0.6 * Math.sin(v); |
| |
| particle.position.set(x, y, z); |
| particle.userData = { |
| u, v, |
| originalU: u, originalV: v, |
| spiralIndex, t, |
| ringIndex: ringIndex, |
| ringSize: currentRingSize |
| }; |
| |
| codeSegments.push(particle); |
| scene.add(particle); |
| } |
| } |
| |
| |
| camera.position.set(0, 8, zoomLevel); |
| camera.lookAt(0, 0, 0); |
| } |
| |
| |
| function animate() { |
| if (!renderer) return; |
| |
| animationId = requestAnimationFrame(animate); |
| |
| |
| const time = Date.now() * 0.001; |
| codeSegments.forEach((particle, index) => { |
| const data = particle.userData; |
| |
| |
| const ringSpeedMultiplier = 1.0 + (data.ringIndex * 0.3); |
| data.t += flowSpeed * 0.008 * ringSpeedMultiplier; |
| if (data.t > 1) data.t = 0; |
| |
| |
| const u = (data.t * spiralTightness * Math.PI * 2) + (data.spiralIndex * Math.PI * 2 / 6); |
| |
| const baseV = (data.spiralIndex * Math.PI * 2 / 6); |
| const vVariation = Math.sin(data.t * Math.PI * 2 * spiralPitch) * 0.3; |
| const v = baseV + vVariation; |
| |
| |
| const x = (data.ringSize + 0.6 * Math.cos(v)) * Math.cos(u); |
| const y = (data.ringSize + 0.6 * Math.cos(v)) * Math.sin(u); |
| const z = 0.6 * Math.sin(v); |
| |
| particle.position.set(x, y, z); |
| |
| |
| const flowPosition = Math.abs(Math.cos(v)); |
| particle.material.opacity = 0.6 + 0.4 * flowPosition; |
| |
| const distanceFromCenter = Math.sqrt(x*x + y*y); |
| const scaleByDistance = 0.7 + 0.5 * (distanceFromCenter / data.ringSize); |
| particle.scale.setScalar(scaleByDistance); |
| }); |
| |
| |
| scene.traverse((child) => { |
| if (child.userData && child.userData.isTorusRing) { |
| child.rotation.z += 0.001 * (child.userData.ringIndex + 1); |
| } |
| }); |
| |
| |
| if (rotationEnabled) { |
| const autoAngle = time * 0.05; |
| cameraAngleY = autoAngle; |
| cameraAngleX = 0.3; |
| } |
| |
| |
| const x = Math.cos(cameraAngleY) * Math.cos(cameraAngleX) * zoomLevel; |
| const y = Math.sin(cameraAngleX) * zoomLevel + 8; |
| const z = Math.sin(cameraAngleY) * Math.cos(cameraAngleX) * zoomLevel; |
| |
| camera.position.set(x, y, z); |
| camera.lookAt(0, 0, 0); |
| |
| try { |
| renderer.render(scene, camera); |
| } catch (error) { |
| console.error('Render error:', error); |
| showError(); |
| } |
| } |
| |
| |
| function showError() { |
| document.getElementById('error-message').style.display = 'block'; |
| if (animationId) { |
| cancelAnimationFrame(animationId); |
| } |
| setTimeout(() => { |
| document.getElementById('error-message').style.display = 'none'; |
| initFallbackCanvas(); |
| }, 3000); |
| } |
| |
| |
| function setupControls() { |
| document.getElementById('flowSpeed').addEventListener('input', (e) => { |
| flowSpeed = parseFloat(e.target.value); |
| }); |
| |
| document.getElementById('numberOfRings').addEventListener('input', (e) => { |
| numberOfRings = parseInt(e.target.value); |
| createSimplifiedVisualization(); |
| }); |
| |
| document.getElementById('spiralTightness').addEventListener('input', (e) => { |
| spiralTightness = parseInt(e.target.value); |
| }); |
| |
| document.getElementById('spiralPitch').addEventListener('input', (e) => { |
| spiralPitch = parseFloat(e.target.value); |
| }); |
| |
| document.getElementById('ringSize').addEventListener('input', (e) => { |
| ringSize = parseFloat(e.target.value); |
| createSimplifiedVisualization(); |
| }); |
| |
| document.getElementById('zoomLevel').addEventListener('input', (e) => { |
| zoomLevel = parseFloat(e.target.value); |
| }); |
| |
| document.getElementById('rotationToggle').addEventListener('change', (e) => { |
| rotationEnabled = e.target.checked; |
| }); |
| |
| document.getElementById('vortexIntensity').addEventListener('input', (e) => { |
| vortexIntensity = parseFloat(e.target.value); |
| }); |
| } |
| |
| |
| function onMouseDown(event) { |
| if (event.button === 0) { |
| isDragging = true; |
| previousMousePosition.x = event.clientX; |
| previousMousePosition.y = event.clientY; |
| rotationEnabled = false; |
| document.getElementById('rotationToggle').checked = false; |
| } |
| } |
| |
| function onMouseMove(event) { |
| if (isDragging) { |
| const deltaX = event.clientX - previousMousePosition.x; |
| const deltaY = event.clientY - previousMousePosition.y; |
| |
| |
| cameraAngleY += deltaX * 0.01; |
| cameraAngleX += deltaY * 0.01; |
| |
| |
| cameraAngleX = Math.max(-Math.PI/2, Math.min(Math.PI/2, cameraAngleX)); |
| |
| previousMousePosition.x = event.clientX; |
| previousMousePosition.y = event.clientY; |
| } |
| } |
| |
| function onMouseUp(event) { |
| isDragging = false; |
| } |
| |
| |
| function onTouchStart(event) { |
| if (event.touches.length === 1) { |
| isDragging = true; |
| previousMousePosition.x = event.touches[0].clientX; |
| previousMousePosition.y = event.touches[0].clientY; |
| rotationEnabled = false; |
| document.getElementById('rotationToggle').checked = false; |
| event.preventDefault(); |
| } |
| } |
| |
| function onTouchMove(event) { |
| if (isDragging && event.touches.length === 1) { |
| const deltaX = event.touches[0].clientX - previousMousePosition.x; |
| const deltaY = event.touches[0].clientY - previousMousePosition.y; |
| |
| cameraAngleY += deltaX * 0.01; |
| cameraAngleX += deltaY * 0.01; |
| cameraAngleX = Math.max(-Math.PI/2, Math.min(Math.PI/2, cameraAngleX)); |
| |
| previousMousePosition.x = event.touches[0].clientX; |
| previousMousePosition.y = event.touches[0].clientY; |
| event.preventDefault(); |
| } |
| } |
| |
| function onTouchEnd(event) { |
| isDragging = false; |
| } |
| |
| |
| function onMouseWheel(event) { |
| event.preventDefault(); |
| const zoomDelta = event.deltaY > 0 ? 1 : -1; |
| zoomLevel = Math.max(5, Math.min(30, zoomLevel + zoomDelta)); |
| |
| |
| const zoomSlider = document.getElementById('zoomLevel'); |
| if (zoomSlider) { |
| zoomSlider.value = zoomLevel; |
| } |
| } |
| |
| |
| function onWindowResize() { |
| if (camera && renderer) { |
| camera.aspect = window.innerWidth / window.innerHeight; |
| camera.updateProjectionMatrix(); |
| renderer.setSize(window.innerWidth, window.innerHeight); |
| } |
| } |
| |
| |
| function init() { |
| setupControls(); |
| |
| if (!isWebGLAvailable()) { |
| showError(); |
| return; |
| } |
| |
| if (initThreeJS()) { |
| createSimplifiedVisualization(); |
| animate(); |
| window.addEventListener('resize', onWindowResize); |
| window.addEventListener('wheel', onMouseWheel, { passive: false }); |
| |
| |
| renderer.domElement.addEventListener('mousedown', onMouseDown); |
| window.addEventListener('mousemove', onMouseMove); |
| window.addEventListener('mouseup', onMouseUp); |
| |
| |
| renderer.domElement.addEventListener('touchstart', onTouchStart); |
| window.addEventListener('touchmove', onTouchMove); |
| window.addEventListener('touchend', onTouchEnd); |
| |
| } else { |
| showError(); |
| } |
| } |
| |
| |
| window.addEventListener('load', init); |
| |
| |
| window.addEventListener('error', (event) => { |
| console.error('Global error:', event.error); |
| showError(); |
| }); |
| </script> |
| </body> |
| </html> |