Created
June 19, 2025 20:01
-
-
Save jaykdoe/3750fc082f3c93f08b814141feba98df to your computer and use it in GitHub Desktop.
Morphing Superformula Explorer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{"config":{"codeTheme":"OneDarkPro","pageThemeSyncCodeTheme":true,"openAlmightyConsole":false,"autoRun":false,"layout":"default","keepPreviousLogs":true,"codeFontSize":16},"title":"Morphing Superformula Explorer","code":{"HTML":{"language":"html","content":"<div id=\"scene-container\"></div>"},"CSS":{"language":"css","content":"* { margin: 0; padding: 0; box-sizing: border-box; }\r\nhtml, body { width: 100%; height: 100%; }\r\nbody { \r\n overflow: hidden; \r\n background: #050508; \r\n color: #fff; \r\n font-family: 'Inter', sans-serif;\r\n}\r\n#scene-container { position: fixed; top: 0; left: 0; width: 100%; height: 100%; }\r\n\r\n:root {\r\n --background-color: rgba(22, 22, 30, 0.5);\r\n --text-color: rgba(255, 255, 255, 0.95);\r\n --title-background-color: rgba(40, 40, 60, 0.6);\r\n --title-text-color: rgba(255, 255, 255, 0.95);\r\n --widget-color: rgba(60, 60, 80, 0.5);\r\n --hover-color: rgba(80, 80, 120, 0.6);\r\n --focus-color: rgba(100, 100, 150, 0.7);\r\n --number-color: #ff55aa;\r\n --string-color: #55aaff;\r\n}\r\n\r\n.energy-button {\r\n border-radius: 6px !important;\r\n background: linear-gradient(to right, rgba(255, 65, 108, 0.9), rgba(255, 75, 43, 0.9)) !important;\r\n color: white !important;\r\n font-weight: bold !important;\r\n font-size: 14px !important;\r\n box-shadow: 0 4px 15px rgba(255, 85, 170, 0.4) !important;\r\n transition: all 0.3s !important;\r\n width: 100% !important;\r\n height: 40px !important;\r\n margin-top: 15px !important;\r\n margin-bottom: 8px !important;\r\n cursor: pointer !important;\r\n border: 1px solid rgba(255, 255, 255, 0.2) !important;\r\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3) !important;\r\n position: relative !important;\r\n overflow: hidden !important;\r\n backdrop-filter: blur(4px) !important;\r\n}\r\n.energy-button:before {\r\n content: '';\r\n position: absolute;\r\n top: -10px;\r\n left: -10px;\r\n right: -10px;\r\n bottom: -10px;\r\n background: linear-gradient(45deg, \r\n rgba(255,255,255,0.1) 0%, \r\n rgba(255,255,255,0.5) 25%, \r\n rgba(255,255,255,0.1) 50%, \r\n rgba(255,255,255,0) 50%);\r\n z-index: 1;\r\n transform: translateX(-100%);\r\n transition: transform 0.6s;\r\n}\r\n.energy-button:hover {\r\n transform: translateY(-2px) !important;\r\n box-shadow: 0 8px 20px rgba(255, 75, 43, 0.6) !important;\r\n}\r\n.energy-button:hover:before {\r\n transform: translateX(100%);\r\n}\r\n.energy-button:active {\r\n transform: translateY(1px) !important;\r\n}\r\n\r\n@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap');","resources":[]},"JS":{"language":"javascript","content":"import * as THREE from \"https://esm.sh/three\";\r\nimport { OrbitControls } from \"https://esm.sh/three/examples/jsm/controls/OrbitControls.js\";\r\nimport { EffectComposer } from \"https://esm.sh/three/examples/jsm/postprocessing/EffectComposer.js\";\r\nimport { RenderPass } from \"https://esm.sh/three/examples/jsm/postprocessing/RenderPass.js\";\r\nimport { UnrealBloomPass } from \"https://esm.sh/three/examples/jsm/postprocessing/UnrealBloomPass.js\";\r\nimport GUI from \"https://esm.sh/lil-gui\"; \r\n\r\nclass SuperformulaWireframe {\r\n constructor() {\r\n this.presets = [\r\n { m1: 5, n11: 10, n12: 2, n13: 7, m2: 5, n21: 10, n22: 10, n23: 10 },\r\n { m1: 2, n11: 1, n12: 4, n13: 8, m2: 8, n21: 1, n22: 1, n23: 4 },\r\n { m1: 6, n11: 1, n12: 1, n13: 1, m2: 3, n21: 1, n22: 5, n23: 1 },\r\n { m1: 12, n11: 15, n12: 8, n13: 8, m2: 12, n21: 8, n22: 4, n23: 15 }\r\n ];\r\n \r\n this.presetOptions = {\r\n \"Star Crystal\": 0, \r\n \"Ocean Creature\": 1, \r\n \"Spiral Galaxy\": 2, \r\n \"Quantum Form\": 3\r\n };\r\n\r\n this.themes = {\r\n \"Synthwave\": { \r\n colors: [\"#ff1f5a\", \"#ff758a\", \"#1e3799\", \"#0984e3\"], \r\n burstColor: \"#ffffff\"\r\n },\r\n \"Forest\": { \r\n colors: [\"#38ef7d\", \"#11998e\", \"#ffe259\", \"#ffa751\"],\r\n burstColor: \"#ffff99\"\r\n },\r\n \"Ocean\": { \r\n colors: [\"#2193b0\", \"#38ef7d\", \"#00b4db\", \"#0083B0\"],\r\n burstColor: \"#8cffff\"\r\n },\r\n \"Sunset\": { \r\n colors: [\"#FF416C\", \"#FF4B2B\", \"#f5af19\", \"#f12711\"],\r\n burstColor: \"#ffffa8\"\r\n }\r\n };\r\n this.themeNames = Object.keys(this.themes);\r\n\r\n this.params = {\r\n preset: 0,\r\n morphDuration: 2.0,\r\n \r\n pulseSpeed: 1.0,\r\n pulseIntensity: 0.2,\r\n microAnimationIntensity: 0.15,\r\n colorTheme: \"Sunset\", \r\n \r\n burstSpeed: 0.8,\r\n burstDuration: 6.0,\r\n multiWave: true,\r\n \r\n bloomStrength: 1.4,\r\n bloomRadius: 0.5,\r\n bloomThreshold: 0.18\r\n };\r\n \r\n this.resolutionTheta = 100;\r\n this.resolutionPhi = 100;\r\n\r\n this.currentPresetParams = { ...this.presets[this.params.preset] };\r\n this.targetPresetParams = { ...this.presets[this.params.preset] };\r\n this.isMorphing = false;\r\n this.morphStartTime = 0;\r\n\r\n this.burstActive = 0.0;\r\n this.burstStartTime = -1.0;\r\n this.lastBurstTime = 0;\r\n \r\n this.superformula = (angle, m, n1, n2, n3, a = 1, b = 1) => {\r\n const term1 = Math.pow(Math.abs(Math.cos(m * angle / 4) / a), n2);\r\n const term2 = Math.pow(Math.abs(Math.sin(m * angle / 4) / b), n3);\r\n const sum = term1 + term2;\r\n if (sum === 0) return 0;\r\n return Math.pow(sum, -1 / n1);\r\n };\r\n\r\n this.init();\r\n this.setupGUI();\r\n this.animate();\r\n }\r\n\r\n init() {\r\n this.scene = new THREE.Scene();\r\n this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);\r\n this.camera.position.set(0, 0, 2.5);\r\n this.camera.lookAt(0, 0, 0);\r\n\r\n this.renderer = new THREE.WebGLRenderer({ antialias: true });\r\n this.renderer.setSize(window.innerWidth, window.innerHeight);\r\n this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));\r\n this.renderer.toneMapping = THREE.ACESFilmicToneMapping;\r\n this.renderer.toneMappingExposure = 1.0;\r\n this.container = document.getElementById(\"scene-container\");\r\n this.container.appendChild(this.renderer.domElement);\r\n\r\n this.composer = new EffectComposer(this.renderer);\r\n this.composer.addPass(new RenderPass(this.scene, this.camera));\r\n \r\n this.bloomPass = new UnrealBloomPass(\r\n new THREE.Vector2(window.innerWidth, window.innerHeight),\r\n this.params.bloomStrength,\r\n this.params.bloomRadius,\r\n this.params.bloomThreshold\r\n );\r\n this.composer.addPass(this.bloomPass);\r\n\r\n this.controls = new OrbitControls(this.camera, this.renderer.domElement);\r\n this.controls.enableDamping = true;\r\n this.controls.dampingFactor = 0.05;\r\n this.controls.autoRotate = false;\r\n this.controls.minDistance = 1;\r\n\r\n this.clock = new THREE.Clock();\r\n \r\n this.createWireframe();\r\n this.updateWireframeGeometry();\r\n \r\n window.addEventListener(\"resize\", () => this.onResize());\r\n \r\n this.container.addEventListener('click', (event) => {\r\n if (!this.isDragging) {\r\n this.triggerBurst();\r\n }\r\n });\r\n \r\n this.container.addEventListener('mousedown', () => { this.isDragging = false; });\r\n this.container.addEventListener('mousemove', () => { if (this.isMouseDown) this.isDragging = true; });\r\n this.container.addEventListener('mouseup', () => { this.isMouseDown = false; });\r\n this.container.addEventListener('mousedown', () => { this.isMouseDown = true; });\r\n }\r\n\r\n triggerBurst() {\r\n const currentTime = this.clock.getElapsedTime();\r\n \r\n if (currentTime - this.lastBurstTime > 0.3) {\r\n this.burstActive = 1.0;\r\n this.burstStartTime = currentTime;\r\n this.lastBurstTime = currentTime;\r\n \r\n const burstButton = document.querySelector('.energy-button');\r\n if (burstButton) {\r\n burstButton.style.boxShadow = '0 0 40px rgba(255, 175, 25, 0.9)';\r\n burstButton.style.transform = 'scale(1.15)';\r\n \r\n const theme = this.themes[this.params.colorTheme];\r\n const colors = theme.colors;\r\n burstButton.style.background = `linear-gradient(to right, ${colors[0]}, ${colors[2]})`;\r\n \r\n setTimeout(() => {\r\n burstButton.style.boxShadow = '0 0 30px rgba(255, 100, 75, 0.8)';\r\n burstButton.style.transform = 'scale(1.1)';\r\n }, 150);\r\n \r\n setTimeout(() => {\r\n burstButton.style.boxShadow = '0 0 20px rgba(245, 175, 25, 0.6)';\r\n burstButton.style.transform = 'scale(1.05)';\r\n burstButton.style.background = `linear-gradient(to right, ${colors[1]}, ${colors[3]})`;\r\n }, 300);\r\n \r\n setTimeout(() => {\r\n burstButton.style.boxShadow = '0 8px 20px rgba(255, 75, 43, 0.6)';\r\n burstButton.style.transform = '';\r\n burstButton.style.background = 'linear-gradient(to right, rgba(255, 65, 108, 0.9), rgba(255, 75, 43, 0.9))';\r\n }, 500);\r\n }\r\n \r\n if (this.wireframeMesh) {\r\n const theme = this.themes[this.params.colorTheme];\r\n const burstColor = theme.burstColor || \"#ffffff\";\r\n \r\n this.wireframeMesh.material.uniforms.burstActive.value = this.burstActive;\r\n this.wireframeMesh.material.uniforms.burstStartTime.value = this.burstStartTime;\r\n this.wireframeMesh.material.uniforms.burstColor.value.set(burstColor);\r\n }\r\n }\r\n }\r\n\r\n createWireframe() {\r\n const geometry = new THREE.BufferGeometry();\r\n const resTheta = this.resolutionTheta;\r\n const resPhi = this.resolutionPhi;\r\n const vertexCount = (resTheta + 1) * (resPhi + 1);\r\n\r\n const positions = new Float32Array(vertexCount * 3);\r\n const colors = new Float32Array(vertexCount * 3);\r\n const indices = [];\r\n\r\n for (let i = 0; i < resTheta; i++) {\r\n for (let j = 0; j < resPhi; j++) {\r\n const current = i * (resPhi + 1) + j;\r\n const nextTheta = (i + 1) * (resPhi + 1) + j;\r\n const nextPhi = current + 1;\r\n indices.push(current, nextTheta);\r\n indices.push(current, nextPhi);\r\n }\r\n indices.push(i * (resPhi + 1) + resPhi, (i + 1) * (resPhi + 1) + resPhi);\r\n }\r\n const lastRowStart = resTheta * (resPhi + 1);\r\n for (let j = 0; j < resPhi; j++) {\r\n indices.push(lastRowStart + j, lastRowStart + j + 1);\r\n }\r\n\r\n geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));\r\n geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));\r\n geometry.setIndex(new THREE.Uint32BufferAttribute(indices, 1));\r\n\r\n const material = new THREE.ShaderMaterial({\r\n uniforms: {\r\n time: { value: 0.0 },\r\n pulseSpeed: { value: this.params.pulseSpeed },\r\n pulseIntensity: { value: this.params.pulseIntensity },\r\n microAnimationIntensity: { value: this.params.microAnimationIntensity },\r\n dashSize: { value: 0.1 },\r\n dashRatio: { value: 0.5 },\r\n burstActive: { value: this.burstActive },\r\n burstStartTime: { value: this.burstStartTime },\r\n burstSpeed: { value: this.params.burstSpeed },\r\n burstDuration: { value: this.params.burstDuration },\r\n burstColor: { value: new THREE.Color(this.themes[this.params.colorTheme].burstColor) },\r\n multiWave: { value: this.params.multiWave ? 1.0 : 0.0 },\r\n morphProgress: { value: 0.0 }\r\n },\r\n vertexShader: `\r\n uniform float time;\r\n uniform float pulseSpeed;\r\n uniform float pulseIntensity;\r\n uniform float microAnimationIntensity;\r\n uniform float morphProgress;\r\n\r\n varying vec3 vColor;\r\n varying vec3 vPos;\r\n varying float vLineDistance;\r\n\r\n void main() {\r\n vColor = color;\r\n vPos = position;\r\n \r\n vLineDistance = length(position);\r\n \r\n float pulse = sin(length(position) * 2.0 - time * pulseSpeed) * pulseIntensity;\r\n \r\n float microAnim1 = sin(position.x * 8.0 + time * 3.0) * microAnimationIntensity;\r\n float microAnim2 = cos(position.y * 9.0 + time * 2.7) * microAnimationIntensity;\r\n float microAnim3 = sin(position.z * 7.0 + time * 3.3) * microAnimationIntensity;\r\n \r\n vec3 microOffset = vec3(microAnim1, microAnim2, microAnim3);\r\n vec3 pulseOffset = normalize(position) * pulse;\r\n \r\n microOffset *= (1.0 + morphProgress * 3.0);\r\n \r\n vec3 animatedPos = position + pulseOffset + microOffset;\r\n gl_Position = projectionMatrix * modelViewMatrix * vec4(animatedPos, 1.0);\r\n }\r\n `,\r\n fragmentShader: `\r\n uniform float time;\r\n uniform float dashSize;\r\n uniform float dashRatio;\r\n uniform float burstActive;\r\n uniform float burstStartTime;\r\n uniform float burstSpeed;\r\n uniform float burstDuration;\r\n uniform vec3 burstColor;\r\n uniform float multiWave;\r\n\r\n varying vec3 vColor;\r\n varying vec3 vPos;\r\n varying float vLineDistance;\r\n\r\n void main() {\r\n vec3 finalColor = vColor;\r\n float finalIntensity = 1.0;\r\n \r\n float totalSize = dashSize * (1.0 + dashRatio);\r\n float patternPos = mod(vLineDistance + time * 0.2, totalSize);\r\n float dashPart = step(patternPos, dashSize);\r\n \r\n if (dashPart < 0.1) {\r\n discard;\r\n }\r\n \r\n finalIntensity *= (1.0 + dashPart * 0.5);\r\n \r\n if (burstActive > 0.5) {\r\n float burstElapsed = max(0.0, time - burstStartTime);\r\n if (burstElapsed < burstDuration) {\r\n float distOrigin = length(vPos);\r\n float progress = burstElapsed / burstDuration;\r\n \r\n float baseSpeed = burstSpeed;\r\n float mainRadius = burstElapsed * baseSpeed;\r\n float mainThickness = 0.4 * (1.0 - 0.5 * progress);\r\n \r\n float mainDist = abs(distOrigin - mainRadius);\r\n float mainWave = 1.0 - smoothstep(0.0, mainThickness, mainDist);\r\n \r\n float trailFactor = smoothstep(0.0, mainRadius, distOrigin) * (1.0 - smoothstep(mainRadius * 0.5, mainRadius, distOrigin));\r\n \r\n float secondaryWave = 0.0;\r\n float tertiaryWave = 0.0;\r\n \r\n if (multiWave > 0.5) {\r\n float secondaryRadius = burstElapsed * (baseSpeed * 1.5);\r\n float secondaryThickness = 0.3 * (1.0 - 0.6 * progress);\r\n float secondaryDist = abs(distOrigin - secondaryRadius);\r\n secondaryWave = 1.0 - smoothstep(0.0, secondaryThickness, secondaryDist);\r\n secondaryWave *= 0.7 * (1.0 - progress * 0.7); \r\n \r\n float tertiaryRadius = burstElapsed * (baseSpeed * 0.7);\r\n float tertiaryThickness = 0.25 * (1.0 - 0.4 * progress);\r\n float tertiaryDist = abs(distOrigin - tertiaryRadius);\r\n tertiaryWave = 1.0 - smoothstep(0.0, tertiaryThickness, tertiaryDist);\r\n tertiaryWave *= 0.5 * (1.0 - progress * 0.5); \r\n }\r\n \r\n vec3 waveColorShift = burstColor;\r\n if (secondaryWave > 0.01) {\r\n waveColorShift = mix(burstColor, vec3(0.5, 0.8, 1.0), 0.3);\r\n }\r\n if (tertiaryWave > 0.01) {\r\n waveColorShift = mix(burstColor, vec3(0.8, 0.5, 1.0), 0.3);\r\n }\r\n \r\n float combinedWave = max(max(mainWave, secondaryWave * 0.8), tertiaryWave * 0.6);\r\n combinedWave = max(combinedWave, trailFactor * 0.4);\r\n \r\n float timeFade = 1.0 - smoothstep(burstDuration * 0.6, burstDuration, burstElapsed);\r\n combinedWave *= timeFade;\r\n \r\n finalColor = mix(finalColor, waveColorShift, combinedWave * 0.8);\r\n finalIntensity += combinedWave * 3.0;\r\n \r\n float rippleFactor = sin(distOrigin * 10.0 - burstElapsed * 5.0) * 0.5 + 0.5;\r\n rippleFactor *= smoothstep(0.0, mainRadius * 0.8, distOrigin) * (1.0 - smoothstep(mainRadius * 0.8, mainRadius, distOrigin));\r\n rippleFactor *= 0.15 * timeFade; \r\n \r\n finalIntensity += rippleFactor;\r\n }\r\n }\r\n\r\n gl_FragColor = vec4(finalColor * finalIntensity, 1.0);\r\n }\r\n `,\r\n vertexColors: true\r\n });\r\n\r\n this.wireframeMesh = new THREE.LineSegments(geometry, material);\r\n this.scene.add(this.wireframeMesh);\r\n }\r\n\r\n updateWireframeGeometry() {\r\n if (!this.wireframeMesh) return;\r\n\r\n const geometry = this.wireframeMesh.geometry;\r\n const positions = geometry.attributes.position.array;\r\n const colors = geometry.attributes.color.array;\r\n \r\n const sfParams = this.isMorphing ? this.getInterpolatedParams() : this.currentPresetParams;\r\n const resTheta = this.resolutionTheta;\r\n const resPhi = this.resolutionPhi;\r\n \r\n const theme = this.themes[this.params.colorTheme];\r\n const themeColors = theme.colors.map(color => new THREE.Color(color));\r\n \r\n let vertexIndex = 0;\r\n for (let i = 0; i <= resTheta; i++) {\r\n const theta = THREE.MathUtils.mapLinear(i, 0, resTheta, -Math.PI / 2, Math.PI / 2);\r\n const r1 = this.superformula(theta, sfParams.m1, sfParams.n11, sfParams.n12, sfParams.n13);\r\n \r\n for (let j = 0; j <= resPhi; j++) {\r\n const phi = THREE.MathUtils.mapLinear(j, 0, resPhi, -Math.PI, Math.PI);\r\n const r2 = this.superformula(phi, sfParams.m2, sfParams.n21, sfParams.n22, sfParams.n23);\r\n \r\n const x = r1 * Math.cos(theta) * r2 * Math.cos(phi);\r\n const y = r1 * Math.sin(theta);\r\n const z = r1 * Math.cos(theta) * r2 * Math.sin(phi);\r\n \r\n positions[vertexIndex * 3 + 0] = x;\r\n positions[vertexIndex * 3 + 1] = y;\r\n positions[vertexIndex * 3 + 2] = z;\r\n \r\n const colorMix = THREE.MathUtils.smoothstep(y, -1.5, 1.5);\r\n \r\n const colorIndex1 = Math.floor(colorMix * (themeColors.length - 1));\r\n const colorIndex2 = Math.min(colorIndex1 + 1, themeColors.length - 1);\r\n const colorFraction = (colorMix * (themeColors.length - 1)) - colorIndex1;\r\n \r\n const vertexColor = themeColors[colorIndex1].clone().lerp(\r\n themeColors[colorIndex2], colorFraction\r\n );\r\n \r\n colors[vertexIndex * 3 + 0] = vertexColor.r;\r\n colors[vertexIndex * 3 + 1] = vertexColor.g;\r\n colors[vertexIndex * 3 + 2] = vertexColor.b;\r\n \r\n vertexIndex++;\r\n }\r\n }\r\n \r\n geometry.attributes.position.needsUpdate = true;\r\n geometry.attributes.color.needsUpdate = true;\r\n geometry.computeBoundingSphere();\r\n }\r\n\r\n getInterpolatedParams() {\r\n const duration = Math.max(0.001, this.params.morphDuration);\r\n const elapsedTime = this.clock.getElapsedTime() - this.morphStartTime;\r\n const totalProgress = Math.min(1.0, elapsedTime / duration);\r\n \r\n if (this.wireframeMesh && this.wireframeMesh.material.uniforms.morphProgress) {\r\n const morphEffect = Math.sin(totalProgress * Math.PI);\r\n this.wireframeMesh.material.uniforms.morphProgress.value = morphEffect;\r\n }\r\n \r\n const interpolated = {};\r\n for (const key in this.currentPresetParams) {\r\n const factor = Math.sin(totalProgress * Math.PI / 2);\r\n interpolated[key] = THREE.MathUtils.lerp(\r\n this.currentPresetParams[key], \r\n this.targetPresetParams[key], \r\n factor\r\n );\r\n }\r\n return interpolated;\r\n }\r\n\r\n startMorphing(targetPresetIndex) {\r\n const targetIndex = Number(targetPresetIndex);\r\n if (isNaN(targetIndex) || targetIndex < 0 || targetIndex >= this.presets.length) {\r\n console.error(\"Invalid target preset index:\", targetPresetIndex);\r\n return;\r\n }\r\n\r\n if (!this.isMorphing) {\r\n this.currentPresetParams = { ...this.presets[this.params.preset] };\r\n } else {\r\n this.currentPresetParams = this.getInterpolatedParams();\r\n }\r\n\r\n this.params.preset = targetIndex; \r\n this.targetPresetParams = { ...this.presets[targetIndex] };\r\n \r\n this.isMorphing = true;\r\n this.morphStartTime = this.clock.getElapsedTime();\r\n }\r\n\r\n setupGUI() {\r\n const customStyles = document.createElement('style');\r\n customStyles.textContent = `\r\n .lil-gui {\r\n border-radius: 12px;\r\n width: 280px;\r\n font-family: 'Inter', sans-serif;\r\n backdrop-filter: blur(12px);\r\n border: 1px solid rgba(255, 255, 255, 0.15);\r\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\r\n overflow: hidden;\r\n opacity: 0.92;\r\n transition: opacity 0.3s;\r\n background: rgba(20, 20, 28, 0.5);\r\n }\r\n \r\n .lil-gui:hover {\r\n opacity: 0.98;\r\n }\r\n \r\n .lil-gui .title {\r\n border-radius: 10px 10px 0 0;\r\n font-weight: bold;\r\n letter-spacing: 0.5px;\r\n text-transform: uppercase;\r\n font-size: 11px;\r\n padding: 10px 12px;\r\n transition: background-color 0.3s;\r\n border-bottom: 1px solid rgba(255, 255, 255, 0.1);\r\n }\r\n \r\n .lil-gui .controller {\r\n border-radius: 6px;\r\n margin: 4px 6px;\r\n padding: 6px 10px;\r\n background: rgba(60, 60, 80, 0.25);\r\n border: 1px solid rgba(255, 255, 255, 0.05);\r\n transition: all 0.2s;\r\n }\r\n \r\n .lil-gui .controller:hover {\r\n background-color: rgba(80, 80, 120, 0.35);\r\n border-color: rgba(255, 255, 255, 0.1);\r\n }\r\n \r\n .lil-gui .controller.color input[type=\"color\"] {\r\n border-radius: 6px;\r\n border: 2px solid rgba(255, 255, 255, 0.2);\r\n width: 16px;\r\n height: 16px;\r\n }\r\n \r\n .lil-gui button {\r\n border-radius: 6px;\r\n background: linear-gradient(to right, rgba(60, 60, 100, 0.8), rgba(80, 80, 150, 0.8));\r\n border: 1px solid rgba(255, 255, 255, 0.1);\r\n transition: all 0.2s;\r\n box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);\r\n }\r\n \r\n .lil-gui button:hover {\r\n background: linear-gradient(to right, rgba(80, 80, 150, 0.9), rgba(100, 100, 200, 0.9));\r\n transform: translateY(-1px);\r\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);\r\n }\r\n \r\n .lil-gui .controller select {\r\n background: rgba(40, 40, 60, 0.7);\r\n border: 1px solid rgba(255, 255, 255, 0.1);\r\n border-radius: 4px;\r\n padding: 3px 6px;\r\n color: white;\r\n }\r\n \r\n .lil-gui .folder > .title {\r\n background: rgba(60, 60, 100, 0.4);\r\n }\r\n \r\n .lil-gui.root {\r\n margin-top: 10px;\r\n margin-right: 10px;\r\n }\r\n `;\r\n document.head.appendChild(customStyles);\r\n\r\n this.gui = new GUI();\r\n\r\n const shapeFolder = this.gui.addFolder('Shape & Morphing');\r\n this.guiPresetController = shapeFolder.add(this.params, 'preset', this.presetOptions).name('Shape Preset').listen().onChange((value) => {\r\n this.startMorphing(value);\r\n });\r\n shapeFolder.add(this.params, 'morphDuration', 0.5, 5.0, 0.1).name('Morph Duration');\r\n \r\n const animFolder = this.gui.addFolder('Animation & Color');\r\n animFolder.add(this.params, 'pulseSpeed', 0, 2, 0.05).name('Pulse Speed').onChange((value) => { \r\n if(this.wireframeMesh) this.wireframeMesh.material.uniforms.pulseSpeed.value = value; \r\n });\r\n animFolder.add(this.params, 'pulseIntensity', 0, 0.5, 0.01).name('Pulse Intensity').onChange((value) => { \r\n if(this.wireframeMesh) this.wireframeMesh.material.uniforms.pulseIntensity.value = value; \r\n });\r\n animFolder.add(this.params, 'microAnimationIntensity', 0, 0.3, 0.01).name('Micro-Animations').onChange(value => {\r\n if (this.wireframeMesh) this.wireframeMesh.material.uniforms.microAnimationIntensity.value = value;\r\n });\r\n animFolder.add(this.params, 'colorTheme', this.themeNames).name('Color Theme').onChange(() => {\r\n this.updateWireframeGeometry();\r\n if (this.wireframeMesh && this.wireframeMesh.material.uniforms.burstColor) {\r\n const themeColor = this.themes[this.params.colorTheme].burstColor || \"#ffffff\";\r\n this.wireframeMesh.material.uniforms.burstColor.value.set(themeColor);\r\n }\r\n });\r\n\r\n const bloomFolder = this.gui.addFolder('Bloom Effect');\r\n bloomFolder.add(this.params, 'bloomStrength', 0, 3).name('Strength').onChange((value) => { \r\n this.bloomPass.strength = value; \r\n });\r\n bloomFolder.add(this.params, 'bloomRadius', 0, 2).name('Radius').onChange((value) => { \r\n this.bloomPass.radius = value; \r\n });\r\n bloomFolder.add(this.params, 'bloomThreshold', 0, 1).name('Threshold').onChange((value) => { \r\n this.bloomPass.threshold = value; \r\n });\r\n \r\n const burstFolder = this.gui.addFolder('Energy Burst');\r\n burstFolder.add(this.params, 'multiWave').name('Multi-Wave Effect').onChange(value => {\r\n if (this.wireframeMesh) {\r\n this.wireframeMesh.material.uniforms.multiWave.value = value ? 1.0 : 0.0;\r\n }\r\n });\r\n \r\n const burstButton = document.createElement('button');\r\n burstButton.textContent = '✨ ENERGY BURST ✨';\r\n burstButton.className = 'energy-button';\r\n burstButton.onclick = () => this.triggerBurst();\r\n this.gui.domElement.appendChild(burstButton);\r\n \r\n shapeFolder.open();\r\n animFolder.open();\r\n burstFolder.open();\r\n }\r\n\r\n animate() {\r\n requestAnimationFrame(() => this.animate());\r\n const elapsedTime = this.clock.getElapsedTime();\r\n const delta = this.clock.getDelta();\r\n\r\n if (this.isMorphing) {\r\n const morphProgress = (elapsedTime - this.morphStartTime) / Math.max(0.001, this.params.morphDuration);\r\n if (morphProgress >= 1.0) {\r\n this.isMorphing = false;\r\n this.currentPresetParams = { ...this.targetPresetParams };\r\n this.updateWireframeGeometry(); \r\n if(this.guiPresetController) this.guiPresetController.updateDisplay();\r\n } else {\r\n this.updateWireframeGeometry(); \r\n }\r\n }\r\n\r\n if (this.wireframeMesh && this.wireframeMesh.material.uniforms) {\r\n this.wireframeMesh.material.uniforms.time.value = elapsedTime;\r\n this.wireframeMesh.material.uniforms.burstActive.value = this.burstActive;\r\n this.wireframeMesh.material.uniforms.burstStartTime.value = this.burstStartTime;\r\n\r\n if (this.burstActive > 0.5 && (elapsedTime - this.burstStartTime >= this.params.burstDuration)) {\r\n this.burstActive = 0.0;\r\n this.burstStartTime = -1.0;\r\n }\r\n }\r\n\r\n this.controls.update(delta);\r\n this.composer.render();\r\n }\r\n\r\n onResize() {\r\n this.camera.aspect = window.innerWidth / window.innerHeight;\r\n this.camera.updateProjectionMatrix();\r\n this.renderer.setSize(window.innerWidth, window.innerHeight);\r\n this.composer.setSize(window.innerWidth, window.innerHeight);\r\n }\r\n}\r\n\r\nwindow.onload = () => {\r\n new SuperformulaWireframe();\r\n}","resources":[]},"VUE":{"language":"vue2","content":"","resources":[]}}} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment