| import streamlit as st | |
| def create_animation_app(): | |
| st.title("Bouncing Balls & Jellyfish in a Sphere") | |
| html_content = """ | |
| <style> | |
| .container { | |
| position: relative; | |
| width: 800px; | |
| height: 600px; | |
| margin: auto; | |
| } | |
| #animationCanvas, #p5Canvas { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| } | |
| canvas { | |
| background-color: transparent; | |
| } | |
| </style> | |
| <div class="container"> | |
| <canvas id="animationCanvas"></canvas> | |
| <div id="p5Container"></div> | |
| </div> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script> | |
| <script> | |
| // Jellyfish Animation | |
| const jellyfishCanvas = document.getElementById('animationCanvas'); | |
| const ctx = jellyfishCanvas.getContext('2d'); | |
| jellyfishCanvas.width = 800; | |
| jellyfishCanvas.height = 600; | |
| let t = 0; | |
| function mag(x, y) { | |
| return Math.sqrt(x * x + y * y); | |
| } | |
| function a(x, y) { | |
| const k = x/8 - 25; | |
| const e = y/8 - 25; | |
| const d = mag(k, e)**2 / 99; | |
| const q = x/3 + k * 0.5 / Math.cos(y*5) * Math.sin(d*d - t); | |
| const c = d/2 - t/8; | |
| const xPos = q * Math.sin(c) + e * Math.sin(d + k - t) + 400; | |
| const yPos = (q + y/8 + d*9) * Math.cos(c) + 300; | |
| return [xPos, yPos]; | |
| } | |
| function getColor(x, y, t) { | |
| const hue = (Math.sin(t/2) * 360 + x/3 + y/3) % 360; | |
| const saturation = 70 + Math.sin(t) * 30; | |
| const lightness = 50 + Math.cos(t/2) * 20; | |
| return `hsla(${hue}, ${saturation}%, ${lightness}%, 0.5)`; | |
| } | |
| function drawJellyfish() { | |
| ctx.fillStyle = 'rgba(0, 0, 0, 0.05)'; | |
| ctx.fillRect(0, 0, jellyfishCanvas.width, jellyfishCanvas.height); | |
| ctx.lineWidth = 1.5; | |
| for(let y = 99; y < 300; y += 4) { | |
| for(let x = 99; x < 300; x += 2) { | |
| const [px, py] = a(x, y); | |
| const color = getColor(x, y, t); | |
| ctx.strokeStyle = color; | |
| ctx.beginPath(); | |
| ctx.moveTo(px, py); | |
| ctx.lineTo(px + 1, py + 1); | |
| ctx.stroke(); | |
| } | |
| } | |
| t += Math.PI / 120; | |
| requestAnimationFrame(drawJellyfish); | |
| } | |
| // Initialize jellyfish animation | |
| drawJellyfish(); | |
| // Bouncing Balls Animation with p5.js | |
| new p5((sketch) => { | |
| let balls = []; | |
| const numBalls = 100; | |
| const sphereRadius = 200; | |
| let rotation = {x: 0, y: 0}; | |
| class Ball { | |
| constructor() { | |
| this.pos = p5.Vector.random3D().mult(sphereRadius * 0.8); | |
| this.vel = p5.Vector.random3D().mult(sketch.random(1, 3)); | |
| this.radius = 3; | |
| } | |
| update() { | |
| // Update position | |
| this.pos.add(this.vel); | |
| // Sphere collision | |
| let distance = this.pos.mag(); | |
| if (distance > sphereRadius - this.radius) { | |
| let normal = this.pos.copy().normalize(); | |
| let reflection = this.vel.copy().reflect(normal); | |
| this.vel.set(reflection); | |
| this.pos = normal.mult(sphereRadius - this.radius); | |
| } | |
| } | |
| display() { | |
| sketch.push(); | |
| sketch.translate(this.pos.x, this.pos.y, this.pos.z); | |
| sketch.fill(255, 255, 0); | |
| sketch.noStroke(); | |
| sketch.sphere(this.radius); | |
| sketch.pop(); | |
| } | |
| } | |
| function checkCollisions() { | |
| for(let i = 0; i < balls.length; i++) { | |
| for(let j = i+1; j < balls.length; j++) { | |
| let b1 = balls[i]; | |
| let b2 = balls[j]; | |
| let d = b1.pos.dist(b2.pos); | |
| let minDist = b1.radius + b2.radius; | |
| if (d < minDist) { | |
| let normal = p5.Vector.sub(b1.pos, b2.pos).normalize(); | |
| let overlap = (minDist - d) / 2; | |
| // Position correction | |
| b1.pos.add(p5.Vector.mult(normal, overlap)); | |
| b2.pos.sub(p5.Vector.mult(normal, overlap)); | |
| // Velocity adjustment | |
| let velDiff = p5.Vector.sub(b1.vel, b2.vel); | |
| let impulse = velDiff.dot(normal) / (1 + 1); | |
| b1.vel.sub(p5.Vector.mult(normal, impulse)); | |
| b2.vel.add(p5.Vector.mult(normal, impulse)); | |
| } | |
| } | |
| } | |
| } | |
| sketch.setup = () => { | |
| const p5Canvas = sketch.createCanvas(800, 600, sketch.WEBGL); | |
| p5Canvas.parent('p5Container'); | |
| sketch.frameRate(30); | |
| // Create balls | |
| for(let i = 0; i < numBalls; i++) { | |
| balls.push(new Ball()); | |
| } | |
| }; | |
| sketch.draw = () => { | |
| sketch.background(0, 0); | |
| sketch.rotateX(rotation.x); | |
| sketch.rotateY(rotation.y); | |
| // Slowly rotate the sphere | |
| rotation.x += 0.002; | |
| rotation.y += 0.003; | |
| // Draw wireframe sphere | |
| sketch.push(); | |
| sketch.noFill(); | |
| sketch.stroke(255, 50); | |
| sketch.sphere(sphereRadius); | |
| sketch.pop(); | |
| // Update and display balls | |
| balls.forEach(ball => { | |
| ball.update(); | |
| ball.display(); | |
| }); | |
| checkCollisions(); | |
| }; | |
| }, 'p5Container'); | |
| </script> | |
| """ | |
| st.components.v1.html(html_content, height=600) | |
| if __name__ == "__main__": | |
| create_animation_app() |