let boundaries = []; let balls = []; let drawingBoundary = false; let boundaryDrawn = false; let w, h; let entropyGraph = []; let startTime; function setup() { w = windowWidth; h = windowHeight; canvas = createCanvas(w, h); startTime = millis(); } function draw() { background(0); // Draw the boundaries stroke(255); strokeWeight(2); if (boundaries.length >= 3) { beginShape(); for (let v of boundaries) { vertex(v.x, v.y); } endShape(CLOSE); } // Update and draw the balls for (let ball of balls) { ball.move(); ball.checkBoundaryCollision(); ball.display(); } // Calculate and display entropy if (boundaryDrawn && balls.length > 0) { let entropy = calculateEntropy(); entropyGraph.push(entropy); displayEntropyGraph(); } // Display instructions displayInstructions(); } function mouseDragged() { if (!drawingBoundary && !boundaryDrawn) { boundaries = []; drawingBoundary = true; } if (drawingBoundary) { boundaries.push({ x: mouseX, y: mouseY }); } } function mouseReleased() { drawingBoundary = false; boundaryDrawn = true; } function mouseClicked() { if (boundaryDrawn) { balls.push(new Ball()); } } class Ball { constructor() { this.x = mouseX; this.y = mouseY; this.vx = random(-2, 2); this.vy = random(-2, 2); this.radius = 2; this.color = color(random(100, 255), random(100, 255), random(100, 255)); } move() { this.x += this.vx; this.y += this.vy; } checkBoundaryCollision() { let isOutside = !this.isPointInPolygon(boundaries, this.x, this.y); if (isOutside) { let closestEdge = this.findClosestEdge(boundaries); let edgeNormal = p5.Vector.fromAngle(closestEdge.angle + HALF_PI); let reflectedVelocity = this.reflectVector(createVector(this.vx, this.vy), edgeNormal); this.vx = reflectedVelocity.x; this.vy = reflectedVelocity.y; // Move the ball back inside the boundary this.x = constrain(this.x, closestEdge.x1, closestEdge.x2); this.y = constrain(this.y, closestEdge.y1, closestEdge.y2); } } // findClosestEdge(polygon) { // // ... (keep the existing implementation) // } // distanceToLine(edge) { // // ... (keep the existing implementation) // } // reflectVector(vector, normal) { // // ... (keep the existing implementation) // } // isPointInPolygon(polygon, px, py) { // // ... (keep the existing implementation) // } findClosestEdge(polygon) { let closestEdge = null; let closestDistance = Infinity; for (let i = 0; i < polygon.length; i++) { let j = (i + 1) % polygon.length; let x1 = polygon[i].x; let y1 = polygon[i].y; let x2 = polygon[j].x; let y2 = polygon[j].y; let edge = { x1, y1, x2, y2 }; let distance = this.distanceToLine(edge); if (distance < closestDistance) { closestDistance = distance; closestEdge = edge; } } let dx = closestEdge.x2 - closestEdge.x1; let dy = closestEdge.y2 - closestEdge.y1; closestEdge.angle = atan2(dy, dx); return closestEdge; } distanceToLine(edge) { let x1 = edge.x1; let y1 = edge.y1; let x2 = edge.x2; let y2 = edge.y2; let dx = x2 - x1; let dy = y2 - y1; let a = dy; let b = -dx; let c = dx * y1 - dy * x1; let dist = Math.abs(a * this.x + b * this.y + c) / Math.sqrt(a * a + b * b); return dist; } reflectVector(vector, normal) { let dotProduct = vector.x * normal.x + vector.y * normal.y; let reflectedVector = p5.Vector.sub(vector, p5.Vector.mult(normal, 2 * dotProduct)); return reflectedVector; } isPointInPolygon(polygon, px, py) { const epsilon = 0.01; // Adjust this value as needed const radius = this.radius; let isInside = false; let j = polygon.length - 1; for (let i = 0; i < polygon.length; i++) { let x1 = polygon[i].x; let y1 = polygon[i].y; let x2 = polygon[j].x; let y2 = polygon[j].y; if ((y1 > py + radius + epsilon) !== (y2 > py + radius + epsilon) && px + radius + epsilon < ((x2 - x1) * (py + radius + epsilon - y1)) / (y2 - y1) + x1) { isInside = !isInside; } j = i; } return isInside; } display() { fill(this.color); noStroke(); ellipse(this.x, this.y, this.radius * 2, this.radius * 2); } } function calculateEntropy() { const gridSize = 10; const grid = {}; const totalBalls = balls.length; for (let ball of balls) { const gridX = Math.floor(ball.x / gridSize); const gridY = Math.floor(ball.y / gridSize); const key = `${gridX},${gridY}`; grid[key] = (grid[key] || 0) + 1; } let entropy = 0; for (let count of Object.values(grid)) { const probability = count / totalBalls; entropy -= probability * Math.log2(probability); } return entropy; } function displayEntropyGraph() { const graphWidth = 200; const graphHeight = 100; const x = width - graphWidth - 10; const y = height - graphHeight - 10; fill(0, 150); rect(x, y, graphWidth, graphHeight); stroke(255); noFill(); beginShape(); for (let i = 0; i < entropyGraph.length; i++) { const px = map(i, 0, entropyGraph.length - 1, x, x + graphWidth); const py = map(entropyGraph[i], 0, 5, y + graphHeight, y); vertex(px, py); } endShape(); fill(255); noStroke(); textAlign(RIGHT); text(`Entropy: ${entropyGraph[entropyGraph.length - 1].toFixed(2)}`, x + graphWidth, y - 5); } function displayInstructions() { fill(255); noStroke(); textAlign(LEFT); textSize(14); text("1. Draw a boundary by dragging the mouse", 10, 20); text("2. Click inside the boundary to add balls", 10, 40); text("3. Observe the entropy graph in the bottom right", 10, 60); } function windowResized() { w = windowWidth; h = windowHeight; resizeCanvas(w, h); }