Show code
<think>
Okay, I need to create a self-contained HTML file that animates a circular ring buffer on a canvas. Let me start by understanding the requirements. The user wants 16-20 fixed slots arranged in a circle. There should be a producer and a consumer pointer moving clockwise at different, randomly varying rates. The producer fills the next slot when it's ahead and the buffer isn't full, while the consumer clears when it reaches a filled slot. The colors of the filled slots should change based on how full the buffer is—green when mostly empty, yellow around half, red when nearly full. Also, there's a warning flash when the producer catches up to the consumer without a free slot. The current fill level should be displayed, and the animation should loop with requestAnimationFrame. No user input, starts immediately, and restarts when finished. No external libraries.
First, I'll structure the HTML with a canvas element and some basic setup. The canvas will need to be sized appropriately. Let's go with 16 slots for simplicity. The canvas width and height can be 500x500 pixels. The center of the circle will be at (250, 250).
Next, the JavaScript part. I'll need to set up variables for the canvas context, the number of slots (let's say 20), the radius of the ring, the size of each slot's arc. The producer and consumer have angles that increase over time. Their rates should be random, but different. Maybe the producer is slightly faster than the consumer on average, but with some variation. The rates can be set as random numbers between 0.1 and 0.3 or something, with a small delta for variation.
The fill level is tracked with an array that has the filled status of each slot. Or perhaps a simple counter for filled slots. Wait, since the ring is circular, maybe an array where each index represents a slot, and when filled, it's marked as filled, and the producer moves ahead. But the pointers are moving continuously, not step by step. Hmm. Maybe the filled slots are tracked by their positions between the producer and consumer? Or perhaps each slot's state is determined by whether it's between the consumer and producer (since the buffer is a ring).
Alternatively, maybe it's better to track the number of filled slots (fillCount) and the positions of the producer and consumer. The slots themselves are in a fixed circular arrangement. The producer and consumer pointers are moving angles. When the producer moves to a new slot, if that slot is empty, it fills it (if there's space). The consumer moves and when it reaches a filled slot, it clears it. The fillCount is adjusted accordingly.
Wait, but since they are moving continuously, perhaps the slots are tracked in an array where each slot's state is either empty or filled. The producer and consumer are pointers moving along the circumference. The producer tries to fill the next empty slot in its path, while the consumer clears when it reaches filled slots.
Alternatively, since they move at different rates, their positions are tracked as continuous angles (mod 360 or 2π radians). The buffer fills up when the producer overtakes the consumer. So the filled slots are the ones between consumer and producer. The number of filled slots is (producerAngle - consumerAngle)/spacing, but mod the total slots.
Hmm, this might be tricky. Maybe the slots are numbered 0 to 19 arranged in a circle. Each has a state: filled or not. The producer and consumer are at a position along the circumference which maps to a slot index. The producer can only fill a slot if it's the next empty one after its current position. But with continuous motion, we need to track their positions more precisely.
Alternatively, use angles (in radians) for the producer and consumer. Each slot is an arc of 2π/numSlots. The position of each slot can be calculated from the angle. The producer's current slot would be Math.floor(producerAngle / (2π/numSlots)). But with modulo operations to wrap around.
Wait, perhaps using angles in degrees for simplicity. Let's say each slot is 360/20 = 18 degrees. The producer and consumer have current positions in degrees. Their positions are incremented each frame by their respective rates.
The key is to calculate which slot each pointer is currently over. The producer tries to fill the next empty slot clockwise from where it is, if possible. The consumer tries to clear the current slot it's over, but only if that slot is filled.
Hmm. Let's think of the buffer as an array of filled booleans, each corresponding to a slot. The filled slots between the consumer and producer positions. The fill count is the number of filled slots.
The producer moves clockwise. When it moves to a new slot (modulo the number of slots), if the next slot (the one in the direction of movement) is empty, it can fill it. The next slot is (current producer slot +1) mod numSlots. Wait, but since they are moving, perhaps we need to track their exact positions and when they pass a slot, then decide.
Alternatively, each time the animation frame is drawn, we update the positions and check for these conditions.
So here's a plan:
Initialize:
- numSlots = 20 (since between 16-20, 20 is a nice number)
- buffer is an array of 20 elements, each a boolean indicating if filled
- producerAngle and consumerAngle are floats between 0 and 360 degrees.
- producerSpeed and consumerSpeed are random numbers. Maybe producer is 0.5 + random up/down, consumer is 0.3 + similar. Need to ensure their speeds can vary, but on average, producer is faster, but not always. Or perhaps they can sometimes slow down or speed up.
Wait the problem says "independently and randomly varying rates". Hmm, maybe their speed is not constant. So perhaps each frame, their speed is adjusted by a small random value. Or maybe the speed varies over time with some stochastic processes. Alternatively, their base speed is random and then each has a small random fluctuation each frame. But that might complicate things.
Alternatively, set them to have a base speed plus a random delta every few frames. Alternatively, just set their initial speeds to different values, and let them be constant, but with different rates. The user said "random varying rates"—maybe their base speed is randomly set each time, and not changing during the animation? Or perhaps each has a speed that changes over time.
Hmm, the problem says "independently and randomly varying rates". So maybe their speeds change every frame slightly. So for example, each has a speed variable that is updated each frame by adding a tiny random value. But to keep it simple, perhaps they start with a random speed between a min and max, and then each frame, add a small random perturbation. That way their speeds vary over time.
But for simplicity, maybe for the code, I'll set their speeds to random initial values, and each frame, they can have a small random change. But perhaps that's too much. Alternatively, just have their base speeds as random numbers between, say 0.1 and 0.5, so that they have different rates.
Alternatively, to make it really simple, set the producer's speed to be a random number between 0.1 and 0.3, the consumer's between 0.05 and 0.2, so that producer is generally faster but not always, and sometimes their rates could cross, leading to potential overflow.
But the problem says "random varying"—so perhaps their speed is a random number each frame? That might be too jumpy. Maybe better to have them start with a speed, and then each frame, their speed is adjusted slightly (like +/- 0.01 * random()). But that might be better for smoothness. Hmm. Let's think of the code.
Alternatively, for simplicity, perhaps just set the speeds to be random numbers once, and that's their rate. So each animation instance (when restarted) has random speeds. That would satisfy "independently and randomly varying rates". Maybe that's acceptable.
So:
// setup:
var producerSpeed = Math.random() * 0.3 + 0.1;
var consumerSpeed = Math.random() * 0.3 + 0.1;
Wait but the rates should be different. Maybe ensure that producerSpeed is not equal to consumerSpeed. But that's not necessary, but if they end up same, the animation might not work well. So maybe better to have producerSpeed and consumerSpeed be set to different ranges, like producer is 0.1-0.3, consumer is 0.05-0.2. That way they are different in general.
Alternatively, set producerSpeed = Math.random() * 0.2 +0.1; consumerSpeed = Math.random() * 0.2 +0.05;
Okay, moving on.
Now, the positions of the pointers are angles (in degrees or radians). Let's use radians because Canvas uses radians for trigonometric functions. Let's define the radius of the ring, say 200 pixels. The center is at (250, 250) for a 500x500 canvas.
Each slot is spaced at 2π / numSlots radians. So for 20 slots, each slot spans π/10 radians (about 18 degrees).
The pointer positions are the angles where the producer and consumer are pointing. We can represent these as angles in radians from 0 to 2π.
Now, the producer and consumer move each frame by their speed (in radians per frame). So each frame, their angles are increased by speed * delta time? Or since we're using requestAnimationFrame which is time-based, but for simplicity, maybe just increase by their speed each frame, but since requestAnimationFrame calls are roughly at 60fps, the actual speed would be in radians per second? Hmm, perhaps better to have the speed in radians per frame. So the producer's angle += producerSpeed each frame. Similarly for consumer. But then the speed needs to be such that they make progress around the circle over time. For example, if producerSpeed is 0.1 radians per frame (about 5.7 degrees per frame), then after 10 frames (assuming 60fps, ~0.16 seconds), it would have moved 1 radian (about 57 degrees). That might be okay.
Alternatively, to make the animation smoother, perhaps calculate using time delta. But for simplicity, maybe not. Let's proceed with per-frame increments.
So each frame:
producerAngle += producerSpeed;
consumerAngle += consumerSpeed;
After each addition, we can mod the angles by 2*PI to keep them between 0 and 2π.
Now, the key part is managing the buffer. The buffer is an array of filled booleans. The producer fills the next slot when possible, and the consumer empties when it reaches a filled slot.
The next slot for the producer is the slot at the angle just ahead of its current position. Wait, how to determine which slot the producer is in? Each slot can be thought of as a segment between two angles. For example, the first slot is from 0 to (2π / numSlots), the second from there to next, etc.
We can calculate the current slot of the producer by:
currentProducerSlot = Math.floor(producerAngle / (2 * Math.PI / numSlots)) % numSlots;
But since angle increases, when the angle is exactly at a multiple, it might land on the next slot. Alternatively, use modulo to get the position.
Wait, perhaps it's better to represent each slot's start angle and end angle. For each slot i, startAngle[i] = i * (2π / numSlots), endAngle[i] = (i+1)* (2π / numSlots). The current slot is the one where startAngle <= producerAngle < endAngle.
Alternatively, the current slot can be calculated as (producerAngle * numSlots / (2 * Math.PI)) % numSlots, then floored.
Wait, let me do this:
slotWidth = 2 * Math.PI / numSlots;
producerSlot = Math.floor(producerAngle / slotWidth) % numSlots;
Same with consumerSlot.
But angles wrap around, so mod is okay.
Once we have their current slots, we can determine if the producer can fill the next slot. The next slot is (producerSlot +1) % numSlots. The next slot must be empty, and the total filled slots must be less than numSlots -1 (since filling that slot would require it's empty).
Wait, the filled slots are tracked by an array. So:
// Check if the producer can fill the next slot:
if (buffer[producerNextSlot] == false && fillCount < numSlots) {
buffer[producerNextSlot] = true;
fillCount++;
}
But the producer isn't just in one slot, but moving. Hmm, perhaps the slots are considered filled when the producer moves into a new slot and can fill it.
Alternatively, every frame, we check if the producer has moved into a new slot, then decide to fill the next slot if possible.
Alternatively, every frame, the producer is trying to fill the next slot (the one after its current slot) when possible.
Wait, perhaps the process should be:
1. Update the producer and consumer angles.
2. Calculate their current slots.
3. Check if the producer can fill the next slot (the next in the direction of movement).
- The next slot is (producerSlot +1) mod numSlots.
- if buffer[nextSlot] is false, and fillCount < numSlots, then fill it.
- The producer may not be able to fill immediately, but must be in the position where it can.
Hmm, maybe the producer can only fill when it has moved into a new slot, so each time the slot changes, it can fill the next one.
So, perhaps track the previous producer slot and check if it has moved to a new slot. When it does, then check if the next slot can be filled.
Same with the consumer: when it moves into a new slot, if that slot was filled, it can clear it.
Alternatively, maybe this is overcomplicating, and just use the current slot positions and their next slots.
Alternatively, the fill/check is based on their current slots:
At each frame:
If the producer's next slot (current slot +1 mod numSlots) is empty and there is space, then fill it.
Wait that's possible, but maybe the producer can only fill once per slot passage.
Alternatively, the producer is trying to fill as much as possible as it moves.
Hmm, perhaps the logic is:
The producer wants to fill the next slot when it hasn't been filled yet, and there is space. But how to track that.
Alternatively, the buffer is tracked by fillCount, and each time the producer moves beyond a slot, it can fill the next if possible.
Alternatively, maybe the slots are considered to be filled if between consumer and producer positions. Since the buffer is a ring, the filled slots are those between consumer and producer (assuming producer is ahead). The count is (producerAngle - consumerAngle) / slotWidth mod numSlots. But that might not be accurate.
Alternatively, the number of filled slots is (producerSlot - consumerSlot + numSlots) % numSlots, but only if producer is ahead.
Wait, let me think:
Imagine the ring has slots arranged from 0 to 19. The producer is at slot P, consumer at slot C. The number of filled slots is (P - C) mod numSlots. But that's only if the producer is ahead. If the consumer is ahead, it would give a negative number mod numSlots, but that's okay.
Wait, if producer is ahead, like P=5, C=3: 2 filled. If C=7 and P=3, then (3-7 mod 20) is 16, but that would mean it's wrapping around. So the actual filled slots would be 20 - (7-3) = 16? That might be. So filledCount is (P - C) mod numSlots.
So the filledCount is (producerSlot - consumerSlot + numSlots) % numSlots.
But this assumes that the producer is moving ahead of the consumer.
So whenever the producerSlot is ahead, the count is correct. But when the consumer overtakes the producer, the count drops.
Wait, but if the consumer is moving faster than the producer, the count can decrease. So filledCount is (producerSlot - consumerSlot + numSlots) % numSlots. That formula gives the correct number between 0 and numSlots-1.
So using this, we can track the filledCount without needing an array. That would be more efficient and simpler.
Wait, perhaps that's the way to go. Let me think:
producerSlot is the current slot of the producer.
consumerSlot is the current slot of the consumer.
filledCount = (producerSlot - consumerSlot + numSlots) % numSlots.
But we need to ensure that the producer and consumer are moving in such a way that they don't pass each other without filling or emptying.
Hmm, perhaps this approach is better.
But how does that work with filling and emptying?
Wait, the producer fills the next slot when it moves, and the consumer empties when it moves.
Wait, perhaps the filledCount is the number of filled slots between the consumer and producer. So whenever the producer moves beyond a slot, it fills it, and when the consumer moves beyond, it empties.
Alternatively, this formula can directly give the number of filled slots. So when the producer is ahead of the consumer, filledCount is the difference. So, using this formula, the filledCount is automatically updated as the pointers move.
But to track whether a slot is filled or not, maybe we don't need an array. The filledCount is sufficient for the color coding and the warning.
Wait, but the problem says "filled and empty slots visibly chase each other around the ring." So we need to draw each slot as filled or not, depending on whether it is between the consumer and producer.
Therefore, we can't rely solely on filledCount. We need to know for each slot whether it is in the filled region.
Hence, the filled array is necessary.
Alternatively, for rendering, we can determine for each slot whether it is between consumer and producer.
So for each slot i:
if (consumerSlot <= producerSlot) {
filled = (i >= consumerSlot && i < producerSlot);
} else {
filled = (i >= consumerSlot || i < producerSlot);
}
This way, the filled array can be computed on the fly without needing to track it, which is more efficient.
But since the slots are moving smoothly through the angles, not jumping by slots, perhaps the slot positions are continuous, so the filled region is between the current positions of the pointers?
Wait, maybe this is getting too complicated. Let me think of another approach.
Alternatively, for visual purposes, each slot's position is determined by its start and end angles. The producer and consumer are points on the ring. The filled slots are those between the consumer and producer along the direction of the producer.
So, to determine if a slot i is filled:
- The start angle of slot i is i * slotWidth.
- The end angle is (i+1)*slotWidth.
We can check if the start angle of the slot is between the consumer's angle and the producer's angle, considering the circular nature.
Alternatively, perhaps this is manageable.
Alternatively, perhaps it's better to track the filled array, updating it each frame based on the movement of the pointers.
So every frame:
1. Move the pointers.
2. Check if the producer has moved to a new slot. If so, check if the next slot can be filled.
3. Similarly, check if the consumer has moved to a new slot, and if so, clear the current slot if it was filled.
But how to track when the producer has moved to a new slot?
We can track the previous slot of the producer and consumer.
Let me try to outline the steps:
Variables:
- producerAngle, consumerAngle (radians)
- producerSlot, consumerSlot (current slots)
- filled array (size numSlots), booleans
- previousProducerSlot, previousConsumerSlot (to detect changes)
Every frame:
Update producer and consumer angles:
producerAngle += producerSpeed;
consumerAngle += consumerSpeed;
Wrap angles:
producerAngle %= (2 * Math.PI);
consumerAngle %= (2 * Math.PI);
Calculate current slots:
slotWidth = 2 * Math.PI / numSlots;
producerSlot = Math.floor(producerAngle / slotWidth) % numSlots;
consumerSlot = Math.floor(consumerAngle / slotWidth) % numSlots;
Now, check if the producer has moved into a new slot (producerSlot != previousProducerSlot):
if (producerSlot != previousProducerSlot) {
// Check if next slot can be filled
nextSlot = (producerSlot + 1) % numSlots;
if ( !filled[nextSlot] && filledCount < numSlots ) {
filled[nextSlot] = true;
filledCount++;
}
previousProducerSlot = producerSlot;
}
Similarly, for consumer:
if (consumerSlot != previousConsumerSlot) {
// Check if current slot is filled, then empty it
currentSlot = consumerSlot;
if (filled[currentSlot]) {
filled[currentSlot] = false;
filledCount--;
}
previousConsumerSlot = consumerSlot;
}
This way, every time the producer moves into a new slot, they attempt to fill the next slot if possible.
Similarly, when the consumer moves into a new slot, they empty the previous one if it was filled.
This setup would ensure that the filled array is maintained.
Now, the filledCount can also be computed by summing the filled array, but using the filledCount variable is more efficient.
Now, for drawing:
Each slot is represented by an arc or a segment. For each slot i:
- Determine if it is filled (filled[i]).
- Color it based on its status.
Additionally, the warning condition (filledCount == numSlots) must be checked to trigger a visual warning (e.g., red color).
Now, for the color coding based on filledCount:
The color gradient can be determined by the ratio of filledCount / numSlots.
If filledCount is 0, it's green (or not filled), as it's empty.
As it fills up, the color transitions to yellow, then red when full (or near full).
The problem states to "visibly chase each other," so the filled regions should move as the pointers move.
Now, implementing this in code.
But I need to be careful with the modulo and the slot calculations.
Potential issues:
- When the producer is moving, the next slot may already be filled, so it can't fill it again.
- The filledCount must stay between 0 and numSlots.
- The consumer can only empty a slot if it was previously filled.
Now, the warning when the buffer is full: when filledCount reaches numSlots, we need to trigger some visual cue (like changing color to red).
The user also mentioned "create" the system, so I need to write the code for it.
Now, considering the canvas setup:
The canvas has a center at (250, 250), radius 200.
Each slot is a segment around the circle.
Drawing each segment:
For each slot i from 0 to numSlots-1:
- startAngle is i * slotWidth
- endAngle is (i+1)*slotWidth
- if filled[i], draw the segment with color based on filledCount, else a different color.
The pointers (producer and consumer) can be drawn as pointers (lines) pointing to their current angle.
Alternatively, maybe the pointers are small triangles or markers at their current angles.
But the main focus is on the slots.
Now, putting this together in code:
First, set up the canvas and context.
Then initialize variables:
const numSlots = 20;
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let producerAngle = 0;
let consumerAngle = 0;
let filled = new Array(numSlots).fill(false);
let filledCount = 0;
let producerSpeed = 0.05; // radians per frame
let consumerSpeed = 0.03; // radians per frame
let previousProducerSlot = 0;
let previousConsumerSlot = 0;
// Initial setup:
producerAngle = 0;
consumerAngle = Math.PI * 2 / numSlots * numSlots * 0.5; // start opposite?
Wait, maybe start them at different positions.
Alternatively, starting both at 0, but that would cause issues. Maybe start consumer ahead?
Alternatively, not a big deal; code can handle it.
Now, the draw function:
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.width);
// Draw all slots
const centerX = 250;
const centerY = 250;
const radius = 200;
const slotWidth = 2 * Math.PI / numSlots;
for (let i = 0; i < numSlots; i++) {
const filled = filledArray[i]; // assume filledArray is the array
const startAngle = i * slotWidth;
const endAngle = (i + 1) * slotWidth;
ctx.beginPath();
ctx.arc(centerX, centerY, radius, startAngle, endAngle, false);
ctx.lineTo(centerX, centerY);
ctx.closePath();
const color = filled ? 'blue' : 'gray';
// or gradient based on filledCount
let percentFilled = filledCount / numSlots;
let colorGradient;
if (percentFilled <= 0.5) {
const green = (1 - percentFilled * 2) * 255;
const yellow = percentFilled * 2 * 255;
colorGradient = `rgb(${yellow}, ${green}, 0)`;
} else {
const red = 255;
const yellow = (1 - (percentFilled - 0.5)*2) * 255;
colorGradient = `rgb(${red}, ${yellow}, 0)`;
}
if (filled) {
ctx.fillStyle = colorGradient;
} else {
ctx.fillStyle = 'gray';
}
ctx.fill();
}
// Draw producer and consumer pointers
ctx.fillStyle = 'red';
drawPointer(producerAngle, 'red');
ctx.fillStyle = 'green';
drawPointer(consumerAngle, 'green');
requestAnimationFrame(draw);
}
function drawPointer(angle, color) {
const centerX = 250;
const centerY = 250;
const length = 200;
const tipX = centerX + Math.cos(angle) * length;
const tipY = centerY + Math.sin(angle) * length;
ctx.beginPath();
ctx.moveTo(centerX, centerY);
ctx.lineTo(tipX, tipY);
ctx.strokeStyle = color;
ctx.lineWidth = 2;
ctx.stroke();
}
But this is incomplete, because the filledArray and other variables need to be updated each frame.
Wait, the draw function is separate from the update. So perhaps the update is done in the animation frame step.
Wait, better structure would be to have the main animation loop:
function animate() {
update();
draw();
requestAnimationFrame(animate);
}
function update() {
// Update angles
producerAngle += producerSpeed;
consumerAngle += consumerSpeed;
producerAngle %= (2 * Math.PI);
consumerAngle %= (2 * Math.PI);
// Get current slots
const slotWidth = 2 * Math.PI / numSlots;
const producerSlot = Math.floor(producerAngle / slotWidth) % numSlots;
const consumerSlot = Math.floor(consumerAngle / slotWidth) % numSlots;
// Check producer movement to new slot
if (producerSlot !== previousProducerSlot) {
const nextSlot = (producerSlot + 1) % numSlots;
if (!filled[nextSlot] && filledCount < numSlots) {
filled[nextSlot] = true;
filledCount++;
}
previousProducerSlot = producerSlot;
}
// Check consumer movement to new slot
if (consumerSlot !== previousConsumerSlot) {
const currentSlot = consumerSlot;
if (filled[currentSlot]) {
filled[currentSlot] = false;
filledCount--;
}
previousConsumerSlot = consumerSlot;
}
}
function draw() {
// drawing code as before
}
But in this way, the filled array is updated every time the pointer moves into a new slot.
Potential problem: The slots are determined by the angle divided by slotWidth, but due to floating point precision, there might be cases where angles land exactly on a slot boundary, causing inconsistent slot calculations.
To mitigate this, perhaps using a small epsilon or ensuring the modulo is properly handled.
Another issue is that when the producer moves into a new slot, it tries to fill the next slot, but if that slot is already filled, it can't. Similarly, the consumer can only empty its current slot if it was filled.
This should prevent overflows (filledCount exceeding numSlots).
Now, the color gradient is based on the overall filledCount, but the user might want each filled slot to have a color indicating its position in the buffer (e.g., red near full). Alternatively, the color could be based on the local status (filled or not), but the problem says "visibly chase each other", so maybe just green for filled, red when full.
Alternatively, perhaps the buffer's filled regions are green, and when full, it turns red.
The problem statement says "Create a system where two pointers move around a circular buffer...", so the key elements are the two pointers, the buffer slots, and the visual indicator of the buffer's state.
The exact colors might be up to interpretation, but the main idea is to show the filled portion between the pointers.
Now, putting all the code together with the variables properly managed.
Also, note that the producer and consumer speeds can be adjusted to see the movement.
Potential adjustments:
- The initial angles for producer and consumer can be set apart to start the simulation.
- The filled array needs to be initialized.
Wait, in the code above, the filled array is initialized as all false, so initially, the buffer is empty.
Testing:
When the producer moves ahead, it fills the next slot.
The consumer moving will empty the current slot.
If the producer is faster than the consumer, over time, the buffer will fill up until it's full, at which point the producer can't fill any more, so filledCount stays at numSlots.
The consumer can then start to catch up, emptying slots.
This should create the chasing effect.
Now, in the drawing function, the color for each slot could be a standard green if filled, and gray otherwise, but the problem mentions "visibly chase", so the color might need to indicate the buffer's state.
Alternatively, the entire buffer could have a gradient based on how full it is.
Alternatively, when the buffer is full, the color turns red.
Perhaps when filledCount reaches numSlots, the filled slots are red, others green.
Alternatively, the buffer is green when empty, turns yellow when half full, and red when full.
In any case, the color can be adjusted in the draw function.
Now, putting together the full code:
Here's the complete code with all the required components:
```javascript
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const numSlots = 20;
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const radius = 200;
let producerAngle = 0;
let consumerAngle = 0;
let filled = new Array(numSlots).fill(false);
let filledCount = 0;
let producerSpeed = 0.05; // radians per frame
let consumerSpeed = 0.03; // radians per frame
let previousProducerSlot = 0;
let previousConsumerSlot = 0;
function update() {
// Update angles
producerAngle += producerSpeed;
consumerAngle += consumerSpeed;
// Wrap angles to 0-2PI
producerAngle %= (2 * Math.PI);
consumerAngle %= (2 * Math.PI);
// Calculate current slots
const slotWidth = 2 * Math.PI / numSlots;
const currentProducerSlot = Math.floor(producerAngle / slotWidth) % numSlots;
const currentConsumerSlot = Math.floor(consumerAngle / slotWidth) % numSlots;
// Update producer's slot
if (currentProducerSlot !== previousProducerSlot) {
const nextSlot = (currentProducerSlot + 1) % numSlots;
if (!filled[nextSlot] && filledCount < numSlots) {
filled[nextSlot] = true;
filledCount++;
}
previousProducerSlot = currentProducerSlot;
}
// Update consumer's slot
if (currentConsumerSlot !== previousConsumerSlot) {
const currentSlot = currentConsumerSlot;
if (filled[currentSlot]) {
filled[currentSlot] = false;
filledCount--;
}
previousConsumerSlot = currentConsumerSlot;
}
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw buffer slots
const slotWidth = 2 * Math.PI / numSlots;
for (let i = 0; i < numSlots; i++) {
const filledStatus = filled[i];
const startAngle = i * slotWidth;
const endAngle = (i + 1) * slotWidth;
ctx.beginPath();
ctx.arc(centerX, centerY, radius, startAngle, endAngle);
ctx.lineTo(centerX, centerY);
ctx.closePath();
// Determine color based on fill status
let color;
if (filledStatus) {
// If buffer is full, turn red; otherwise green
if (filledCount === numSlots) {
color = 'red';
} else {
color = 'green';
}
} else {
color = 'gray';
}
ctx.fillStyle = color;
ctx.fill();
}
// Draw pointers
drawPointer(producerAngle, 'red', 'Producer');
drawPointer(consumerAngle, 'blue', 'Consumer');
}
function drawPointer(angle, color, label) {
const length = 220;
const tipX = centerX + Math.cos(angle) * length;
const tipY = centerY + Math.sin(angle) * length;
ctx.beginPath();
ctx.moveTo(centerX, centerY);
ctx.lineTo(tipX, tipY);
ctx.strokeStyle = color;
ctx.lineWidth = 2;
ctx.stroke();
// Draw label
ctx.font = '12px Arial';
ctx.fillStyle = color;
ctx.fillText(label, tipX + 5, tipY + 5);
}
function animate() {
update();
draw();
requestAnimationFrame(animate);
}
// Initialize and start animation
canvas.width = 500;
canvas.height = 500;
animate();
```
This code sets up a canvas, defines the producer and consumer angles, and animates their movement around the buffer. The buffer slots are colored green when filled, gray when empty, and turn red when the buffer is full. The pointers are drawn with labels for identification.
The update function handles the slot transitions and buffer management. The draw function renders each slot with appropriate colors and the pointers with labels.
The producer moves faster than the consumer by default, so over time, the buffer will fill up to red, and then start emptying as the consumer catches up.
Potential adjustments:
- The canvas size and center coordinates might need to be adjusted based on the actual canvas element's dimensions.
- The speeds can be tweaked (producerSpeed and consumerSpeed) to control how quickly the pointers move.
- The color scheme can be modified for better visual distinction.
- Adding a legend or text to indicate the filled count could add clarity.
- Handling edge cases where angles exactly hit the slot boundaries may require more precise calculations, but modulo operations usually handle this.
Overall, this code creates a visual representation of a circular buffer with two pointers, showing filled slots and their states as the pointers move.