Show code
<think>
Okay, I need to create a self-contained HTML file that draws a Voronoi diagram with 25 seeds on a canvas. The seeds should drift slowly, bounce off the edges, and the diagram should update in real-time. Also, display the number of cells and the area of the largest one. Let's start by recalling what a Voronoi diagram is—it's a partitioning of a plane into regions based on distance to points in a specific subset of the plane.
First, I'll structure the HTML file with a canvas element. The canvas will be the main area where everything is drawn. Since it's self-contained, all the code has to be in the HTML, probably in a script tag. I'll need to handle the animation loop using requestAnimationFrame, which is smooth and efficient.
Next, I'll set up variables for the canvas context, the seeds array, and some parameters like canvas size, animation speed, etc. The seeds need to be random points initially. Each seed will have an x and y position, and a velocity. The velocity can be a small value so they move slowly. The direction and speed should be randomized.
The bouncing off the edges part means that when a seed hits the edge of the canvas, its velocity direction reverses. So, if the x position goes beyond 0 or the canvas width, we flip the vx component. Same with y for the vertical edges.
Now, the Voronoi diagram calculation. To compute the Voronoi cells, one approach is to iterate over each pixel in the canvas and determine which seed is closest. The cell is then colored based on that. But doing this for every pixel every frame might be computationally intensive for 25 seeds. Wait, but 25 seeds aren't too bad. The canvas dimensions—maybe 600x400? That's 240,000 pixels. Hmm, that might slow down. Maybe need to optimize.
Alternatively, use the canvas's imageData to process in bulk. Wait, but how exactly? Let me think. The standard approach is to loop over each pixel, for each pixel, compute distance to each seed, track the closest one. Assign the color based on that seed's color. Then, draw that pixel. But with 25 seeds, this could be manageable for a 600x400 canvas at 60fps? Maybe not. Maybe need to use a more efficient algorithm?
Alternatively, perhaps using the canvas's fill methods with polygons? But that's complicated. Alternatively, using the canvas's 'putImageData' after computing each pixel. Let me think about the steps.
First, create an array for each seed with their color, position, and velocity. Then, every frame:
1. Update each seed's position by adding their velocity. Check if they hit the edges (x < 0 or x > canvasWidth, similarly for y). If so, reverse the velocity component (vx *= -1, etc.)
2. Compute the Voronoi diagram. For each pixel (x,y), find the closest seed, then set that pixel's color to the seed's color. The seeds can have their own colors, stored in an array.
3. After computing the Voronoi, we can draw the seeds as small circles or crosses.
4. Calculate the number of cells (which is the number of seeds, which is always 25, but perhaps the question counts the cells? Wait, the seeds define the cells, so the number of cells should be 25. But maybe in some cases overlapping or edges cause different counts, but probably not. Hmm, the problem says "number of cells"—so maybe it's just 25. But maybe I need to count the regions? Maybe I can just display 25, but the problem says to show it dynamically. Hmm, perhaps better to compute the number of regions as the number of seeds, which is fixed? Alternatively, maybe in some edge cases, but perhaps it's okay to just write 25. Wait the problem says "the number of cells" which is the same as the number of seeds, so maybe just display 25. But the user might want to see that it's 25, but perhaps better to just output it as 25. Alternatively, maybe the code can just log it as 25, since it's fixed. But perhaps the problem expects the count to be the actual regions, which might not change, so the answer is 25. Hmm. Maybe the problem says "number of cells" as 25, so just state that. Alternatively, perhaps I should code it as text.textContent = seeds.length or something. Since seeds.length is 25, maybe that's okay.
The area of the largest cell: to compute that, we need to track for each cell how many pixels are in it. So for each cell (seed), count how many pixels have that color. The maximum count would be the area. But counting per pixel might be time-consuming. Alternatively, during the initial pixel loop, track an array of counters for each seed. Let's see: create an array counts initialized to 0. For each pixel, when determining the closest seed (index i), increment counts[i]. Then, after processing all pixels, find the max value in counts. The area can be stored as that value. But with 600x400 pixels, that's 240,000 pixels. For each pixel, we have to loop through 25 seeds, so 25*240,000 = 6,000,000 operations per frame. That might be too slow. Wait, but 60 fps would require that per second, so 6,000,000 * 60 ~ 360 million operations per second. That might be okay for a modern browser, but maybe not. Hmm. Alternatively, can we optimize?
Maybe precompute the squared distances to avoid sqrt? Since sqrt is expensive but when comparing distances, we can just compare squared distances. So for each seed, compute dx = x - seed.x, dy = y - seed.y, then distance squared is dx*dx + dy*dy. We can track the closest squared distance, and find the index of the seed with the smallest squared distance. That way, no sqrt needed. That's better.
But even then, the per-pixel loop might be slow. Let me think. Let's see, for 600x400 pixels, 240,000 pixels. For each pixel, 25 iterations (distance calculations). So, 25 * 240,000 = 6,000,000 operations per frame. Let's see: if each operation is a few instructions, it might be manageable. Let's see, maybe in a for loop with nested loops. Alternatively, using typed arrays could help? Not sure.
Alternatively, maybe using the canvas's getImageData and putImageData would be faster. For example:
- Create an offscreen buffer, process the image data array.
Wait, here's an approach using the canvas's imageData:
- Create a 2D context.
- Create a data array of length 4*width*height (rgba).
But processing all pixels via a loop is still the same.
Alternatively, using the canvas's pixel manipulation. Let me structure the code step by step.
Variables:
- canvas, ctx
- seeds array: each seed has x, y, vx, vy. Plus a color (stored as a 4-element array for rgba?)
Wait, but storing color as a single integer (argb) might be more manageable.