awacke1 commited on
Commit
ed6a27c
·
verified ·
1 Parent(s): d92e279

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +249 -92
app.py CHANGED
@@ -1,100 +1,257 @@
1
  import streamlit as st
2
 
3
  def create_animation_app():
4
- st.title("Animated Spiral Creature")
5
-
6
- # JavaScript code for the animation
7
- js_code = """
8
- <canvas id="animationCanvas"></canvas>
9
-
10
- <script>
11
- const canvas = document.getElementById('animationCanvas');
12
- const ctx = canvas.getContext('2d');
13
-
14
- // Set canvas size
15
- canvas.width = 3840;
16
- canvas.height = 2160;
17
-
18
- let t = 0;
19
-
20
- function mag(x, y) {
21
- return Math.sqrt(x * x + y * y);
22
- }
23
-
24
- function a(x, y) {
25
- const k = x/8 - 25;
26
- const e = y/8 - 25;
27
- const d = mag(k, e)**2 / 99;
28
-
29
- const q = x/3 + k * 0.5 / Math.cos(y*5) * Math.sin(d*d - t);
30
- const c = d/2 - t/8;
31
-
32
- const xPos = q * Math.sin(c) + e * Math.sin(d + k - t) + 200;
33
- const yPos = (q + y/8 + d*9) * Math.cos(c) + 200;
34
-
35
- return [xPos, yPos];
36
- }
37
-
38
- function getColor(x, y, t) {
39
- // Create shifting colors based on position and time
40
- const hue = (Math.sin(t/2) * 360 + x/3 + y/3) % 360;
41
- const saturation = 70 + Math.sin(t) * 30;
42
- const lightness = 50 + Math.cos(t/2) * 20;
43
- return `hsla(${hue}, ${saturation}%, ${lightness}%, 0.5)`;
44
- }
45
-
46
- function draw() {
47
- // Clear canvas with a fade effect
48
- ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
49
- ctx.fillRect(0, 0, canvas.width, canvas.height);
50
-
51
- ctx.lineWidth = 1.5;
52
-
53
- for(let y = 99; y < 300; y += 4) {
54
- for(let x = 99; x < 300; x += 2) {
55
- const [px, py] = a(x, y);
56
- const color = getColor(x, y, t);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
- ctx.strokeStyle = color;
59
- ctx.beginPath();
60
- ctx.moveTo(px, py);
61
- ctx.lineTo(px + 1, py + 1); // Make points slightly larger
62
- ctx.stroke();
 
 
 
 
 
 
 
63
  }
64
- }
65
-
66
- t += Math.PI / 120; // Slowed down the animation slightly
67
- requestAnimationFrame(draw);
68
- }
69
-
70
- // Ensure canvas is cleared on start
71
- ctx.fillStyle = 'black';
72
- ctx.fillRect(0, 0, canvas.width, canvas.height);
73
-
74
- // Start the animation
75
- draw();
76
- </script>
77
- """
78
-
79
- # CSS to center the canvas and ensure proper display
80
- css = """
81
- <style>
82
- canvas {
83
- display: block;
84
- margin: auto;
85
- background: black;
86
- }
87
- .stApp {
88
- background-color: black;
89
- }
90
- </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  """
92
-
93
- # Combine CSS and JavaScript
94
- html_content = css + js_code
95
-
96
- # Display using st.components.v1.html
97
- st.components.v1.html(html_content, height=2160)
98
 
99
  if __name__ == "__main__":
100
- create_animation_app()
 
1
  import streamlit as st
2
 
3
  def create_animation_app():
4
+ st.title("Bouncing Yellow Balls & Jellyfish in a Rotating Sphere")
5
+ # We embed an HTML document that includes:
6
+ # 1. p5.js (from a CDN)
7
+ # 2. a container div for our canvas
8
+ # 3. our p5.js sketch that simulates 100 bouncing yellow balls with collision detection
9
+ # and one bouncing jellyfish (using a spiral drawing similar to your jellyfish code)
10
+ # all inside a sphere (drawn as a circle) that slowly rotates.
11
+ html_code = r"""
12
+ <!DOCTYPE html>
13
+ <html>
14
+ <head>
15
+ <meta charset="UTF-8">
16
+ <!-- Include p5.js from a CDN -->
17
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.min.js"></script>
18
+ <style>
19
+ /* Remove default margins & center the canvas */
20
+ body {
21
+ margin: 0;
22
+ padding: 0;
23
+ overflow: hidden;
24
+ background: black;
25
+ }
26
+ #p5-container {
27
+ display: flex;
28
+ justify-content: center;
29
+ align-items: center;
30
+ }
31
+ </style>
32
+ </head>
33
+ <body>
34
+ <div id="p5-container"></div>
35
+ <script>
36
+ // --- Global Variables ---
37
+ let balls = [];
38
+ let jellyfish;
39
+ const sphereRadius = 300; // radius of the bounding sphere
40
+ let sphereCenter;
41
+ let rotationAngle = 0;
42
+ const numBalls = 100;
43
+
44
+ // --- p5.js Setup ---
45
+ function setup() {
46
+ // Create an 800x800 canvas and attach it to our container
47
+ let canvas = createCanvas(800, 800);
48
+ canvas.parent('p5-container');
49
+ sphereCenter = createVector(width/2, height/2);
50
+
51
+ // Set color mode to HSB for our jellyfish drawing.
52
+ colorMode(HSB, 360, 100, 100, 1);
53
+
54
+ // Create 100 balls. Each ball is given a random position (inside the sphere)
55
+ // and a random velocity.
56
+ for (let i = 0; i < numBalls; i++) {
57
+ balls.push(new Ball());
58
+ }
59
+
60
+ // Create one jellyfish object. (Its drawn “shape” is produced by the jellyA() and getJellyColor() functions below.)
61
+ jellyfish = new Jellyfish();
62
+ }
63
+
64
+ // --- p5.js Draw Loop ---
65
+ function draw() {
66
+ // Create a fade/trail effect by drawing a semi-transparent black background.
67
+ // (Note: using HSB mode so black is 0 saturation and 0 brightness.)
68
+ background(0, 0, 0, 0.1);
69
+
70
+ // Increment our rotation angle very slowly.
71
+ rotationAngle += 0.005;
72
+
73
+ // All drawing and physics is done in a coordinate system with origin at sphereCenter.
74
+ push();
75
+ translate(sphereCenter.x, sphereCenter.y);
76
+ // Slowly rotate the entire coordinate system.
77
+ rotate(rotationAngle);
78
+
79
+ // Draw the bounding sphere (a circle)
80
+ noFill();
81
+ stroke(255);
82
+ strokeWeight(2);
83
+ ellipse(0, 0, sphereRadius * 2, sphereRadius * 2);
84
+
85
+ // --- Update Physics ---
86
+ // 1. Update positions for all balls and the jellyfish.
87
+ for (let ball of balls) {
88
+ ball.update();
89
+ }
90
+ jellyfish.update();
91
+
92
+ // 2. Check and resolve ball-ball collisions.
93
+ for (let i = 0; i < balls.length; i++) {
94
+ for (let j = i + 1; j < balls.length; j++) {
95
+ balls[i].collide(balls[j]);
96
+ }
97
+ }
98
+
99
+ // 3. Check each ball for collisions with the sphere boundary and display them.
100
+ for (let ball of balls) {
101
+ ball.checkBoundaryCollision();
102
+ ball.display();
103
+ }
104
+
105
+ // 4. Check the jellyfish for sphere collisions and display it.
106
+ jellyfish.checkBoundaryCollision();
107
+ jellyfish.display();
108
+
109
+ pop();
110
+ }
111
+
112
+ // --- Ball Class ---
113
+ class Ball {
114
+ constructor() {
115
+ this.r = 5; // radius of the ball
116
+ // Random position inside the sphere (by picking a random angle and radius)
117
+ let angle = random(TWO_PI);
118
+ let rad = random(sphereRadius - this.r);
119
+ this.pos = createVector(rad * cos(angle), rad * sin(angle));
120
+ // Random speed and direction
121
+ let speed = random(1, 3);
122
+ let vAngle = random(TWO_PI);
123
+ this.vel = createVector(speed * cos(vAngle), speed * sin(vAngle));
124
+ // Yellow color in HSB (hue=60, saturation=100, brightness=100)
125
+ this.col = color(60, 100, 100);
126
+ }
127
+
128
+ update() {
129
+ this.pos.add(this.vel);
130
+ }
131
+
132
+ // Check for collision with the circular (spherical) boundary.
133
+ checkBoundaryCollision() {
134
+ let d = this.pos.mag();
135
+ if (d + this.r > sphereRadius) {
136
+ // Compute the normal (pointing outward)
137
+ let normal = this.pos.copy().normalize();
138
+ // Reflect the velocity: v = v - 2*(v·normal)*normal
139
+ let dot = this.vel.dot(normal);
140
+ this.vel.sub(p5.Vector.mult(normal, 2 * dot));
141
+ // Ensure the ball is repositioned just inside the boundary.
142
+ this.pos = normal.mult(sphereRadius - this.r);
143
+ }
144
+ }
145
+
146
+ // Check for collisions with another ball (elastic collision for equal masses)
147
+ collide(other) {
148
+ let diff = p5.Vector.sub(other.pos, this.pos);
149
+ let distBetween = diff.mag();
150
+ if (distBetween < this.r + other.r) {
151
+ // Separate the overlapping balls by moving them apart equally.
152
+ let overlap = (this.r + other.r) - distBetween;
153
+ let displacement = diff.copy().normalize().mult(overlap / 2);
154
+ this.pos.sub(displacement);
155
+ other.pos.add(displacement);
156
 
157
+ // Compute collision response
158
+ let normal = diff.copy().normalize();
159
+ let relativeVelocity = p5.Vector.sub(this.vel, other.vel);
160
+ let dotProd = relativeVelocity.dot(normal);
161
+ // Only resolve if balls are moving toward each other.
162
+ if (dotProd > 0) {
163
+ // For equal masses, exchange the velocity components along the collision normal.
164
+ let impulse = normal.copy().mult(dotProd);
165
+ this.vel.sub(impulse);
166
+ other.vel.add(impulse);
167
+ }
168
+ }
169
  }
170
+
171
+ display() {
172
+ noStroke();
173
+ fill(this.col);
174
+ ellipse(this.pos.x, this.pos.y, this.r * 2, this.r * 2);
175
+ }
176
+ }
177
+
178
+ // --- Jellyfish Class ---
179
+ class Jellyfish {
180
+ constructor() {
181
+ // For the jellyfish we choose a larger effective “radius” (for collision) of about 40.
182
+ this.size = 40;
183
+ // Start at a random position inside the sphere (with a margin)
184
+ this.pos = createVector(random(-sphereRadius + this.size, sphereRadius - this.size),
185
+ random(-sphereRadius + this.size, sphereRadius - this.size));
186
+ // Give it a random velocity.
187
+ let speed = random(1, 2);
188
+ let angle = random(TWO_PI);
189
+ this.vel = createVector(speed * cos(angle), speed * sin(angle));
190
+ // A time parameter used in the jellyfish drawing.
191
+ this.t = 0;
192
+ }
193
+
194
+ update() {
195
+ this.pos.add(this.vel);
196
+ this.t += 0.05;
197
+ }
198
+
199
+ // Bounce off the sphere boundary using a simple circular collision.
200
+ checkBoundaryCollision() {
201
+ if (this.pos.mag() + this.size > sphereRadius) {
202
+ let normal = this.pos.copy().normalize();
203
+ let dot = this.vel.dot(normal);
204
+ this.vel.sub(p5.Vector.mult(normal, 2 * dot));
205
+ this.pos = normal.mult(sphereRadius - this.size);
206
+ }
207
+ }
208
+
209
+ display() {
210
+ push();
211
+ translate(this.pos.x, this.pos.y);
212
+ // Draw the jellyfish using a grid of points computed by the jellyA() function.
213
+ // (We subtract 200 from the computed positions so that the drawing is centered.)
214
+ strokeWeight(1.5);
215
+ for (let y = 99; y < 300; y += 4) {
216
+ for (let x = 99; x < 300; x += 2) {
217
+ let res = jellyA(x, y, this.t);
218
+ let px = res[0] - 200;
219
+ let py = res[1] - 200;
220
+ stroke(getJellyColor(x, y, this.t));
221
+ point(px, py);
222
+ }
223
+ }
224
+ pop();
225
+ }
226
+ }
227
+
228
+ // --- Jellyfish Drawing Functions ---
229
+ // Replicate the provided jellyfish “a(x,y)” function.
230
+ function jellyA(x, y, t) {
231
+ let k = x / 8 - 25;
232
+ let e = y / 8 - 25;
233
+ // d is computed as (k^2+e^2)/99
234
+ let d = (k * k + e * e) / 99;
235
+ let q = x / 3 + k * 0.5 / cos(y * 5) * sin(d * d - t);
236
+ let c = d / 2 - t / 8;
237
+ let xPos = q * sin(c) + e * sin(d + k - t) + 200;
238
+ let yPos = (q + y / 8 + d * 9) * cos(c) + 200;
239
+ return [xPos, yPos];
240
+ }
241
+
242
+ // Replicate the provided getColor function for the jellyfish.
243
+ function getJellyColor(x, y, t) {
244
+ let hue = (sin(t / 2) * 360 + x / 3 + y / 3) % 360;
245
+ let saturation = 70 + sin(t) * 30;
246
+ let brightness = 50 + cos(t / 2) * 20;
247
+ return color(hue, saturation, brightness, 0.5);
248
+ }
249
+ </script>
250
+ </body>
251
+ </html>
252
  """
253
+ # The height is set to 800px; adjust if needed.
254
+ st.components.v1.html(html_code, height=820, scrolling=False)
 
 
 
 
255
 
256
  if __name__ == "__main__":
257
+ create_animation_app()