xxxxxxxxxx
344
let rectangles = [];
let gravity = 1;
let friction = 0.85;
let numRectangles = 30;
let timeInterval = 5000; // 5 seconds
let lastTime = 0;
let groundHeight = 720;
let screenWidth = 720;
let screenHeight = 720;
function setup() {
createCanvas(screenWidth, screenHeight);
noStroke();
rectangles.push(new Rectangle(random(screenWidth), random(screenHeight / 3)));
}
function draw() {
background(0);
let currentTimeA = millis();
if (currentTimeA - lastTime > timeInterval && rectangles.length < 10) {
lastTime = currentTimeA;
rectangles.push(
new Rectangle(random(screenWidth), random(80,screenHeight / 3))
);
}
for (let i = 0; i < rectangles.length; i++) {
rectangles[i].update();
rectangles[i].draw();
}
}
class Rectangle {
constructor(x, y) {
this.width = 80;
this.height = 80;
this.position = createVector(x, y);
this.velocity = createVector(.001, 0.001);
this.angle = random(TWO_PI);
this.angularVelocity = (0, 0);
this.mass = 1;
this.restitution = 0.5;
this.collisionAngle = 0;
this.isColliding = false;
this.momentOfInertia =
(1 / 12) * this.mass * (this.width ** 2 + this.height ** 2);
this.angularDamping = 0.9999;
this.airResistance = 0.5;
this.torque = 0;
}
draw() {
push();
translate(this.position.x, this.position.y);
rotate(this.angle);
rectMode(CENTER);
rect(0, 0, this.width, this.height);
pop();
}
update() {
// Check if object should be asleep
if (this.getMagnitudeOfVector(this.velocity) < 0.001) {
this.velocity = createVector(0, 0);
this.angularVelocity = 0;
return;
}
// Apply forces
let forces = createVector(0, this.mass * gravity);
forces.rotate(this.angle);
this.velocity.add(forces.div(this.mass));
this.velocity.mult(1 - this.airResistance);
this.torque = -this.angularVelocity * this.angularDamping;
// Apply torques
this.angularVelocity += this.torque / this.momentOfInertia;
this.angle += this.angularVelocity;
// Apply friction
this.velocity.mult(this.isColliding ? friction : 1);
// Update position and check boundaries
this.position.add(this.velocity);
let left = this.position.x - this.width / 2;
let right = this.position.x + this.width / 2;
let top = this.position.y - this.height / 2;
let bottom = this.position.y + this.height / 2;
// Check if any of the corners are outside the boundary
let vertices = this.getVertices();
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
for (let i = 0; i < vertices.length; i++) {
let vertex = vertices[i];
if (vertex.x < minX) {
minX = vertex.x;
}
if (vertex.x > maxX) {
maxX = vertex.x;
}
if (vertex.y < minY) {
minY = vertex.y;
}
if (vertex.y > maxY) {
maxY = vertex.y;
}
}
if (minX < 0) {
this.position.x += -minX;
this.velocity.x *= -1;
this.angularVelocity *= -1;
this.angle = -this.angle;
} else if (maxX > screenWidth) {
this.position.x -= maxX - screenWidth;
this.velocity.x *= -1;
this.angularVelocity *= -1;
this.angle = -this.angle;
}
if (minY < 0) {
this.position.y += -minY;
this.velocity.y *= -1;
this.angularVelocity *= -1;
this.angle = PI - this.angle;
} else if (maxY > groundHeight) {
this.position.y -= maxY - groundHeight;
this.velocity.y *= -1;
this.angularVelocity *= -1;
this.angle = PI - this.angle;
}
// Check collisions with other rectangles
for (let i = 0; i < rectangles.length; i++) {
let otherRectangle = rectangles[i];
if (this !== otherRectangle) {
let collisionInfo = this.checkCollisionWithRectangle(otherRectangle);
if (collisionInfo !== null) {
let collisionAxis = collisionInfo[0];
let overlap= collisionInfo[1];
this.resolveCollisionWithRectangle(
otherRectangle,
collisionAxis,
overlap
);
}
}
}
}
getVertices() {
let halfWidth = this.width / 2;
let halfHeight = this.height / 2;
let cosAngle = cos(this.angle);
let sinAngle = sin(this.angle);
let positionX = this.position.x;
let positionY = this.position.y;
let topLeft = createVector(
positionX + halfWidth * cosAngle - halfHeight * sinAngle,
positionY + halfWidth * sinAngle + halfHeight * cosAngle
);
let topRight = createVector(
positionX - halfWidth * cosAngle - halfHeight * sinAngle,
positionY - halfWidth * sinAngle + halfHeight * cosAngle
);
let bottomLeft = createVector(
positionX + halfWidth * cosAngle + halfHeight * sinAngle,
positionY + halfWidth * sinAngle - halfHeight * cosAngle
);
let bottomRight = createVector(
positionX - halfWidth * cosAngle + halfHeight * sinAngle,
positionY - halfWidth * sinAngle - halfHeight * cosAngle
);
return [topLeft, topRight, bottomRight, bottomLeft];
}
checkCollisionWithRectangle(otherRectangle) {
let vertices1 = this.getVertices();
let vertices2 = otherRectangle.getVertices();
let overlap = Infinity;
let smallestAxis = null;
for (let i = 0; i < vertices1.length; i++) {
let j = i + 1 < vertices1.length ? i + 1 : 0;
let edge = p5.Vector.sub(vertices1[j], vertices1[i]);
let axis = createVector(-edge.y, edge.x).normalize();
let projection1 = this.projectRectangleOntoAxis(vertices1, axis);
let projection2 = this.projectRectangleOntoAxis(vertices2, axis);
let currentOverlap = this.getOverlap(projection1, projection2);
if (currentOverlap === 0) {
return null;
} else if (abs(currentOverlap) < overlap) {
overlap = abs(currentOverlap);
smallestAxis = axis.copy();
}
}
for (let i = 0; i < vertices2.length; i++) {
let j = i + 1 < vertices2.length ? i + 1 : 0;
let edge = p5.Vector.sub(vertices2[j], vertices2[i]);
let axis = createVector(-edge.y, edge.x).normalize();
let projection1 = this.projectRectangleOntoAxis(vertices1, axis);
let projection2 = this.projectRectangleOntoAxis(vertices2, axis);
let currentOverlap = this.getOverlap(projection1, projection2);
if (currentOverlap === 0) {
return null;
} else if (abs(currentOverlap) < overlap) {
overlap = abs(currentOverlap);
smallestAxis = axis.copy();
}
}
let direction = p5.Vector.sub(otherRectangle.position, this.position);
if (direction.dot(smallestAxis) > 0) {
smallestAxis.mult(-1);
}
return [smallestAxis, overlap];
}
projectRectangleOntoAxis(vertices, axis) {
let min = axis.dot(vertices[0]);
let max = min;
for (let i = 1; i < vertices.length; i++) {
let projection = axis.dot(vertices[i]);
if (projection < min) {
min = projection;
}
if (projection > max) {
max = projection;
}
}
return [min, max];
}
getOverlap(projection1, projection2) {
let min1 = projection1[0];
let max1 = projection1[1];
let min2 = projection2[0];
let max2 = projection2[1];
if (max1 < min2 || max2 < min1) {
return 0;
} else {
return max1 < max2 ? max1 - min2 : max2 - min1;
}
}
resolveCollisionWithRectangle(otherRectangle, collisionAxis, overlap) {
let totalMass = this.mass + otherRectangle.mass;
let weight1 = this.mass / totalMass;
let weight2 = otherRectangle.mass / totalMass;
let velocity1 = this.velocity.copy();
let velocity2 = otherRectangle.velocity.copy();
let relativeVelocity = velocity1.sub(velocity2);
let velAlongNormal = relativeVelocity.dot(collisionAxis);
if (velAlongNormal > 0) {
return;
}
let e = Math.min(this.restitution, otherRectangle.restitution);
let j = -(1 + e) * velAlongNormal;
j /= 1 / this.mass + 1 / otherRectangle.mass;
let impulse = collisionAxis.copy().mult(j);
this.velocity.add(impulse.copy().mult(weight1));
otherRectangle.velocity.sub(impulse.copy().mult(weight2));
let penetration = collisionAxis.copy().mult(overlap);
this.position.add(penetration.copy().mult(weight1));
otherRectangle.position.sub(penetration.copy().mult(weight2));
// Update collision angle
let combinedMass = this.mass + otherRectangle.mass;
let collisionNormal = collisionAxis.copy().rotate(HALF_PI);
let relativeVelocityAlongNormal = collisionNormal.dot(
velocity1.sub(velocity2)
);
this.collisionAngle = atan2(
relativeVelocityAlongNormal * ((2 * otherRectangle.mass) / combinedMass),
this.velocity.mag() -
otherRectangle.velocity.mag() * ((2 * this.mass) / combinedMass)
);
let otherCollisionNormal = collisionAxis.copy().rotate(PI);
let otherRelativeVelocityAlongNormal = otherCollisionNormal.dot(
velocity2.sub(velocity1)
);
otherRectangle.collisionAngle = atan2(
otherRelativeVelocityAlongNormal * ((2 * this.mass) / combinedMass),
otherRectangle.velocity.mag() -
this.velocity.mag() * ((2 * otherRectangle.mass) / combinedMass)
);
}
applyFrictionBetweenRectangles(otherRectangle, collisionAxis, overlap) {
// Determine the tangent axis
let tangentAxis = createVector(collisionAxis.y, -collisionAxis.x);
// Project velocities onto the tangent axis
let velocity1 = this.velocity.copy().project(tangentAxis);
let velocity2 = otherRectangle.velocity.copy().project(tangentAxis);
// Calculate the total kinetic energy
let totalKineticEnergy =
0.5 *
(this.mass * this.velocity.magSq() +
otherRectangle.mass * otherRectangle.velocity.magSq());
// Calculate the relative velocity
let relativeVelocity = velocity1.sub(velocity2);
// Calculate the impulse
let j =
-relativeVelocity.dot(tangentAxis) /
(1 / this.mass + 1 / otherRectangle.mass);
// Clamp the impulse
let maxImpulse = (friction * overlap) / totalKineticEnergy;
j = Math.min(j, maxImpulse);
j = Math.max(j, -maxImpulse);
// Apply the impulse to the velocities
let impulse = tangentAxis.copy().mult(j);
this.velocity.sub(impulse.copy().mult(this.mass));
otherRectangle.velocity.add(impulse.copy().mult(otherRectangle.mass));
}
getMagnitudeOfVector(vec) {
return Math.sqrt(vec.x ** 2 + vec.y ** 2);
}
}