-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDeepSeek-R1_Aesthetic_Fractal_Collisions.html
More file actions
450 lines (329 loc) · 23.2 KB
/
DeepSeek-R1_Aesthetic_Fractal_Collisions.html
File metadata and controls
450 lines (329 loc) · 23.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
<!DOCTYPE html>
<html>
<!--
DeepSeek-R1 prompt:
in HTML and javascript, i want to make a 2D particle collision simulator. start 24 particles in a circle centred around the square canvas, and all are initially moving outwards symmetrically. all collisions are perfectly elastic. furthermore, i want to make the animation fancy, so draw straight lines between adjacent particles. To make it pretty, add some fractal-like features, like on those lines, connect straight lines at a fraction t of each line, where 0<t<1, and t increases with time. repeat this for each "fractal" shape 20 times.
Response:
Okay, let's see. The user wants a 2D particle collision simulator using HTML and JavaScript. The particles start in a circle around a square canvas, moving outward symmetrically. All collisions are elastic. Also, they want fancy lines between adjacent particles with fractal-like features, where lines are drawn at a fraction t that increases over time, repeated 20 times.
First, I need to set up the HTML structure. A canvas element is necessary for drawing. Styling the canvas with a black background and some margins would make it look nice. Then, the JavaScript part.
For the particles, I'll create a Particle class. Each particle needs position, velocity, radius, mass, and color. Since collisions are elastic, I'll need to handle particle-particle and particle-wall collisions.
Starting 24 particles in a circle. The circle should be centered on the canvas. The initial positions can be calculated using trigonometric functions. Each particle's angle is 2π divided by 24, multiplied by their index. They start at a radius smaller than the canvas, maybe 100 units, and move outward radially. The velocity direction is determined by their angle.
Elastic collisions require conserving kinetic energy and momentum. For particle collisions, I'll check the distance between particles. If it's less than the sum of radii, they collide. The collision response involves calculating the new velocities using the collision formula based on their masses and velocities. Since all particles are the same mass, the equations simplify.
Wall collisions: Check if the particle's position is beyond the canvas boundaries. If so, reverse the velocity component (x for left/right walls, y for top/bottom) and adjust the position to prevent sticking.
Now, the lines between adjacent particles. Adjacent means in the order they were created, so the first connects to the second, and the last connects back to the first. Drawing lines is straightforward with the canvas lineTo method.
The fractal-like features. For each line between particles, divide it into segments at a fraction t. Then, for each segment, draw lines at a fraction of that, recursively. The fraction t increases over time, so maybe using a sine function to oscillate t between 0 and 1. Repeat this 20 times per frame.
Wait, the user said "connect straight lines at a fraction t of each line, where t increases with time. Repeat this for each 'fractal' shape 20 times." So maybe each line is split into segments, and each segment is further split, up to 20 iterations. But doing 20 iterations might be computationally heavy. Alternatively, for each original line, draw 20 lines at different fractions based on t. Hmm, perhaps using recursion with a depth limit of 20.
Alternatively, for each line between two points, create intermediate points at fraction t and 1-t, then connect those points. Then, do the same for the new lines, creating a fractal pattern. The fraction t increases over time, which can be controlled by a variable that increments each frame.
To make it pretty, colors can be based on HSV with varying hue. The lines' colors can transition smoothly.
Putting it all together:
1. Initialize particles in a circle, moving outward.
2. Each frame, update positions, check collisions.
3. Draw particles and lines between them.
4. For each line, recursively draw fractal lines based on t.
5. Animate with requestAnimationFrame.
Potential issues: Performance with 24 particles and 20 fractal iterations. Maybe limit the fractal depth to a lower number, but the user specified 20. So need to optimize the drawing code.
Another thing: The fractal lines are drawn between adjacent particles. So each original line is the base for the fractal. Each iteration adds more lines at fractions of the current lines. Using recursion, each level splits the line into two parts at t and 1-t, then draws lines between those points. But doing this 20 times would create 2^20 segments, which is too much. Wait, maybe the user meant that for each line, draw 20 lines at different t values that increase over time. For example, t starts at 0.1 and increases to 0.9, and each frame, t is updated. But the description says "repeat this for each 'fractal' shape 20 times." Hmm, perhaps each line is subdivided 20 times, creating a branching structure.
Alternatively, for each line, draw a line that's split into segments at t, then those segments are split again, up to 20 levels. But that's a lot. Maybe the fractal is drawn by connecting points at t along the lines, and then connecting those points in a pattern. For example, the first level draws a line between the original points. The second level draws lines between t and 1-t of the original line. Then, the third level draws lines between t of those new lines, etc. But this would create a tree-like structure. However, with 20 levels, it's too much. Maybe the user intended a single level of subdivision, but with t increasing over time, creating an animated fractal effect.
Alternatively, the fractal feature could be a single subdivision where each line has a point at t, and lines are drawn between those points, forming a polygon inside the original. As t increases, the polygon changes. Repeating this 20 times would mean drawing 20 such polygons with varying t. But the description says "repeat this for each 'fractal' shape 20 times." Maybe each frame, t is incremented, and for each line, 20 lines are drawn at different fractions, but that's unclear.
Perhaps the intended effect is similar to a Koch snowflake, where each line is divided into segments, and new lines are added at each iteration. But with 20 iterations, it's impractical. So maybe the user wants a visual where each line between particles has smaller lines drawn at a fraction t along them, and t increases over time, creating a moving pattern. For example, each line has a point at t, and another at 1-t, and lines are drawn between those points, forming a smaller polygon. Then, this process is repeated 20 times, each time using the same t value, which increases over time.
To implement this, for each line between two particles, calculate a point at t along the line. Then, connect all these points to form a new polygon. Repeat this process 20 times, each time taking the previous polygon's lines and subdividing them at the same t. As t increases, the polygons change shape.
But how to code this efficiently? Maybe using a recursive function that takes an array of points and a depth. For each depth, compute the new points by interpolating between the existing points at t, then draw lines between them. The depth is 20, but with 24 points, each recursion would create 24 new points. After 20 levels, it's 24*20 points, which might be manageable.
Wait, no. For each recursion level, the number of points remains the same. For example, starting with 24 points, after one subdivision, you have 24 new points (each at t between adjacent originals). Connecting those forms a new polygon. Repeating this 20 times would create 20 layers of polygons, each based on the previous one's t-interpolated points.
So, for each frame, after drawing the original lines, we draw 20 more layers, each with a t that's a function of time. But the user said "t increases with time. repeat this for each 'fractal' shape 20 times." So maybe each of the 20 layers has a t that's a multiple of the base t. For example, t = timeFactor, and each layer uses t * (layer + 1)/20. Or something like that.
Alternatively, the fractal lines are drawn by recursively subdividing each line into segments, up to 20 times, with t increasing each frame. This would create an animated fractal effect.
But to avoid excessive computation, perhaps precompute the points for each layer and draw them. Let's think of an example:
Original line between points A and B. At depth 1, we find point C at t from A to B. Then, at depth 2, find points at t from A to C and C to B. But this would create 2^(depth) segments. At depth 20, that's a million segments, which is impossible.
So maybe the user intended a different approach. Perhaps for each original line, draw 20 lines that are fractions of the original line, with t increasing each frame. For example, each frame, t is a value between 0 and 1, and for each line, we draw a line from the start to t*length, then another from t*length to end, and so on, but only for 20 iterations. But I'm not sure.
Alternatively, the fractal effect is created by drawing lines between points that are a fraction t along each line, and then connecting those points to form a new shape. Repeating this process 20 times, each time using the same t, which increases over time. So each frame, t is updated, and for that t, we draw 20 layers of lines, each based on the previous layer's points.
Wait, that might work. For example, start with the original 24 points. For the first fractal layer, compute points at t along each line between adjacent original points, then connect those new points to form a polygon. For the second layer, compute points at t along the lines of the first layer's polygon, and so on, up to 20 layers. As t increases, the polygons evolve.
This approach would require, for each frame, generating 20 layers of polygons. Each layer is based on the previous one's points, interpolated at t. So for each layer, you have an array of points, and you generate the next layer by interpolating between each adjacent pair.
Implementing this:
- Start with the original points.
- For each of 20 layers:
- For each adjacent pair in the current layer, compute the point at t.
- Collect these new points into the next layer.
- Draw lines between the new points.
- The t value increases over time, perhaps using a sine function to oscillate.
This way, each layer is a polygon inside the previous one, scaled by t. Repeating this 20 times creates a fractal-like pattern of nested polygons. As t changes, the polygons expand or contract.
Yes, this seems manageable. The number of points per layer remains 24, so 20 layers would be 24*20 = 480 points per frame, which is feasible.
Now, coding this:
In the draw function, after drawing the original lines, loop 20 times. For each iteration, take the current points (starting with the original particles' positions), compute the interpolated points at t, then draw lines between them. Then, use these new points as the current points for the next iteration.
But wait, the original particles are moving, so each frame, their positions change. The fractal lines are based on the current positions, so each frame, the fractal is redrawn based on the updated particle positions.
To implement this:
1. In the draw function, after updating and drawing the particles, get their current positions in an array.
2. For each fractal layer (up to 20):
a. Create a new array of points by interpolating between each adjacent pair in the current points array.
b. Draw lines between these new points.
c. Use the new points for the next layer.
3. Vary t over time, perhaps using a sine wave to oscillate between 0.1 and 0.9.
The interpolation function would be something like:
newX = currentX + (nextX - currentX) * t;
newY = currentY + (nextY - currentY) * t;
But since the particles are in a circular order, the last point connects back to the first.
For colors, using HSL with varying hue based on the layer index would create a nice effect. For example, each layer's hue is layer * 5, creating a rainbow effect.
Putting it all together:
- HTML: Set up canvas, set size, maybe handle resizing.
- JavaScript: Particle class, particle array, collision handling, animation loop.
Testing edge cases: Particles reaching the canvas edges, collision detection efficiency (since 24 particles, 24*23/2 = 276 collision checks per frame, which is manageable).
Potential optimizations: Use requestAnimationFrame, avoid unnecessary redraws, but since it's a simulation, it needs to redraw every frame.
Elastic collision code: When two particles collide, their velocities are updated based on the collision normal. Since all particles have the same mass, the velocity exchange is simpler.
Now, coding step by step:
1. Set up HTML with canvas.
2. Initialize particles in a circle.
3. Update particles each frame, handling collisions.
4. Draw particles and lines.
5. Draw fractal lines recursively 20 times per frame.
6. Adjust t over time.
For the fractal lines, in each frame:
- Start with the original particle positions.
- For each layer from 0 to 19:
- Calculate new points by interpolating adjacent particles at t.
- Draw lines between new points.
- Use these new points for the next layer.
The t value can be calculated as 0.5 + 0.4 * Math.sin(time / 1000), making it oscillate between 0.1 and 0.9.
Color can be based on layer index, using HSL.
Testing this approach should create a visually appealing effect with particles moving outward, colliding, and fractal lines evolving over time.
Here's an implementation of a 2D particle collision simulator with fractal-like visualization:
<this file's code here>
This implementation features:
24 particles starting in a circular formation moving outward
Perfectly elastic collisions between particles and walls
White connecting lines between adjacent particles
Recursive fractal patterns that:
Use a time-varying interpolation parameter (t)
Create 20 layers of fractal patterns
Use rainbow colors based on depth and time
Smooth animation using requestAnimationFrame
The fractal patterns are created by recursively connecting midpoints of the previous layer's lines, with the interpolation parameter (t) oscillating over time. The collision detection handles both particle-wall and particle-particle interactions using basic physics for elastic collisions.
You can modify various parameters like:
Canvas size
Number of particles
Base speed
Color schemes
Fractal depth (currently 20)
Oscillation speed of the t parameter
Line widths and colors
The particles will continue to bounce and interact while maintaining their kinetic energy due to the perfectly elastic collisions.
-->
<head>
<title>Particle Collision Fractal</title>
<style>
body {
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: #000;
color: #fff;
font-family: Arial, sans-serif;
}
canvas {
background: #000;
margin: 20px auto;
display: block;
}
.controls {
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: center;
margin: 10px;
}
.controls label {
display: flex;
flex-direction: column;
align-items: center;
font-size: 14px;
}
.controls input[type="range"] {
width: 100px;
}
.controls button {
padding: 5px 10px;
font-size: 14px;
cursor: pointer;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<div class="controls">
<label>
Speed: <input type="range" id="speed" min="1" max="10" value="3">
</label>
<label>
Depth: <input type="range" id="depth" min="1" max="50" value="10">
</label>
<label>
Particles: <input type="number" id="numParticles" min="3" max="100" value="24">
</label>
<label>
Particle Size: <input type="number" id="particleSize" min="1" max="20" value="8">
</label>
<button id="pausePlay">Pause</button>
<button id="restart">Restart</button>
</div>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 1024;
canvas.height = 1024;
let particles = [];
let numParticles = 16;
let particleRadius = 8;
let baseSpeed = 3;
let t = 0;
let time = 0;
let isPaused = false;
let depth = 10;
class Particle {
constructor(x, y, vx, vy) {
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
this.radius = particleRadius;
this.mass = 1;
this.color = `hsl(${(Math.random() * 360)}, 100%, 50%)`;
}
update() {
this.x += this.vx;
this.y += this.vy;
// Wall collisions
if (this.x < this.radius || this.x > canvas.width - this.radius) {
this.vx *= -1;
this.x = this.x < this.radius ? this.radius : canvas.width - this.radius;
}
if (this.y < this.radius || this.y > canvas.height - this.radius) {
this.vy *= -1;
this.y = this.y < this.radius ? this.radius : canvas.height - this.radius;
}
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fillStyle = this.color;
ctx.fill();
}
}
function initializeParticles() {
particles = [];
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const initialRadius = 200;
for (let i = 0; i < numParticles; i++) {
const angle = (i * Math.PI * 2) / numParticles;
const x = centerX + Math.cos(angle) * initialRadius;
const y = centerY + Math.sin(angle) * initialRadius;
const vx = Math.cos(angle) * baseSpeed;
const vy = Math.sin(angle) * baseSpeed;
particles.push(new Particle(x, y, vx, vy));
}
}
function checkCollisions() {
for (let i = 0; i < particles.length; i++) {
for (let j = i + 1; j < particles.length; j++) {
const p1 = particles[i];
const p2 = particles[j];
const dx = p2.x - p1.x;
const dy = p2.y - p1.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < p1.radius + p2.radius) {
// Elastic collision resolution
const angle = Math.atan2(dy, dx);
const speed1 = Math.sqrt(p1.vx * p1.vx + p1.vy * p1.vy);
const speed2 = Math.sqrt(p2.vx * p2.vx + p2.vy * p2.vy);
const direction1 = Math.atan2(p1.vy, p1.vx);
const direction2 = Math.atan2(p2.vy, p2.vx);
const velocityX1 = speed1 * Math.cos(direction1 - angle);
const velocityY1 = speed1 * Math.sin(direction1 - angle);
const velocityX2 = speed2 * Math.cos(direction2 - angle);
const velocityY2 = speed2 * Math.sin(direction2 - angle);
// Swap velocities (for equal mass)
const finalVelocityX1 = velocityX2;
const finalVelocityX2 = velocityX1;
p1.vx = Math.cos(angle) * finalVelocityX1 + Math.cos(angle + Math.PI / 2) * velocityY1;
p1.vy = Math.sin(angle) * finalVelocityX1 + Math.sin(angle + Math.PI / 2) * velocityY1;
p2.vx = Math.cos(angle) * finalVelocityX2 + Math.cos(angle + Math.PI / 2) * velocityY2;
p2.vy = Math.sin(angle) * finalVelocityX2 + Math.sin(angle + Math.PI / 2) * velocityY2;
}
}
}
}
function drawFractal(points, currentDepth) {
if (currentDepth >= depth) return;
const newPoints = [];
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
for (let i = 0; i < points.length; i++) {
const current = points[i];
const next = points[(i + 1) % points.length];
const midX = current.x + (next.x - current.x) * t;
const midY = current.y + (next.y - current.y) * t;
newPoints.push({ x: midX, y: midY });
ctx.lineTo(midX, midY);
}
ctx.strokeStyle = `hsl(${(time * 0.5 + currentDepth * 15) % 360}, 70%, 50%)`;
ctx.lineWidth = 1;
ctx.stroke();
ctx.closePath();
drawFractal(newPoints, currentDepth + 1);
}
function animate() {
if (isPaused) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
particles.forEach(particle => particle.update());
checkCollisions();
// Draw original connections
ctx.beginPath();
particles.forEach((p, i) => {
const next = particles[(i + 1) % particles.length];
if (i === 0) ctx.moveTo(p.x, p.y);
ctx.lineTo(next.x, next.y);
});
ctx.strokeStyle = 'rgba(255, 255, 255, 0.2)';
ctx.stroke();
// Update fractal parameter
time += 1;
t = 0.5 + Math.sin(time * 0.02) * 0.4;
// Draw fractal patterns
const initialPoints = particles.map(p => ({ x: p.x, y: p.y }));
drawFractal(initialPoints, 0);
particles.forEach(particle => particle.draw());
requestAnimationFrame(animate);
}
// UI Controls
document.getElementById('speed').addEventListener('input', (e) => {
baseSpeed = parseFloat(e.target.value);
particles.forEach(p => {
const speed = Math.sqrt(p.vx * p.vx + p.vy * p.vy);
p.vx = (p.vx / speed) * baseSpeed;
p.vy = (p.vy / speed) * baseSpeed;
});
});
document.getElementById('depth').addEventListener('input', (e) => {
depth = parseInt(e.target.value);
});
document.getElementById('numParticles').addEventListener('input', (e) => {
numParticles = parseInt(e.target.value);
initializeParticles();
});
document.getElementById('particleSize').addEventListener('input', (e) => {
particleRadius = parseInt(e.target.value);
particles.forEach(p => p.radius = particleRadius);
});
document.getElementById('pausePlay').addEventListener('click', () => {
isPaused = !isPaused;
document.getElementById('pausePlay').textContent = isPaused ? 'Play' : 'Pause';
if (!isPaused) animate();
});
document.getElementById('restart').addEventListener('click', () => {
initializeParticles();
});
initializeParticles();
animate();
</script>
</body>
</html>