package sim.physics2D.constraint; import sim.util.matrix.*; import sim.physics2D.physicalObject.MobileObject2D; import sim.physics2D.physicalObject.PhysicalObject2D; //import sim.physics2D.util.Double2D; import sim.util.Double2D; /** * Created when a collision is detected between two objects and * used to solve for the collision impulses. */ public class Collision implements ImpulseConstraint { public PhysicalObject2D obj1; public PhysicalObject2D obj2; private Double2D r1; private Double2D r2; private DenseMatrix subCollisionRowsMatrix1; private DenseMatrix subCollisionColsMatrix1; private DenseMatrix subCollisionRowsMatrix2; private DenseMatrix subCollisionColsMatrix2; private DenseMatrix subCollisionIntersectionMatrix; private sim.util.matrix.Vector subCollisionAnswersVector; Double2D colNormal; double relVel; private boolean sticky; public Collision() { sticky = false; subCollisionRowsMatrix1 = new DenseMatrix(2, 3); subCollisionColsMatrix1 = new DenseMatrix(3, 2); subCollisionRowsMatrix2 = new DenseMatrix(2, 3); subCollisionColsMatrix2 = new DenseMatrix(3, 2); subCollisionIntersectionMatrix = new DenseMatrix(2, 2); subCollisionAnswersVector = new sim.util.matrix.Vector(2); } /** Set this collision to be perfectly inelastic */ public void setSticky() { sticky = true; } /** Returns the number of rows in the collision response matrix */ public int GetCollisionResponseRows() { return 2; } public void setColNormal(Double2D colNormal) { this.colNormal = colNormal; } /** Sets the relative velocity along the collision normal */ public void setRelVel(double relVel) { this.relVel = relVel; } public void AddPhysicalObject(PhysicalObject2D mobjRigidBody) { if (obj1 == null) obj1 = mobjRigidBody; else obj2 = mobjRigidBody; } /** Add a physical object to the collision and specify the vector pointing * from the center of the object to the collision point. */ public void AddPhysicalObject(PhysicalObject2D mobjRigidBody, Double2D connectionPoint) { if (obj1 == null) { obj1 = mobjRigidBody; r1 = connectionPoint; } else { obj2 = mobjRigidBody; r2 = connectionPoint; } } /** Sets the blocks of the global matrices represented by this collision */ public void setCollisionMatrices(int curConstraintRow, BorderedDiagonalIdentityMatrix collisionMatrix, sim.util.matrix.Vector answerVector) { int constraintRows = 2; // Prepare the local constraint state matrix setCollisionRowsMatrix(); setCollisionColsMatrix(); setCollisionAnswersVector(); setCollisionIntersectionMatrix(); // Answer and intersection matrices have all zeros, so do not need to be // set int globalPosStart1 = obj1.index * 3; collisionMatrix.setBlock(curConstraintRow, globalPosStart1, subCollisionRowsMatrix1.vals); collisionMatrix.setBlock(globalPosStart1, curConstraintRow, subCollisionColsMatrix1.vals); int globalPosStart2 = obj2.index * 3; collisionMatrix.setBlock(curConstraintRow, globalPosStart2, subCollisionRowsMatrix2.vals); collisionMatrix.setBlock(globalPosStart2, curConstraintRow, subCollisionColsMatrix2.vals); collisionMatrix.setBlock(curConstraintRow, curConstraintRow, subCollisionIntersectionMatrix.vals); answerVector.vals[curConstraintRow] = subCollisionAnswersVector.vals[0]; answerVector.vals[curConstraintRow + 1] = subCollisionAnswersVector.vals[1]; } private void setCollisionRowsMatrix() { Double2D r1 = this.r1; Double2D r2 = this.r2; double colPoint1PDot = r1.perpDot(colNormal); double colPoint2PDot = r2.perpDot(colNormal); subCollisionRowsMatrix1.vals[0][0] = colNormal.x; subCollisionRowsMatrix1.vals[0][1] = colNormal.y; subCollisionRowsMatrix1.vals[0][2] = colPoint1PDot; subCollisionRowsMatrix1.vals[1][0] = 0; subCollisionRowsMatrix1.vals[1][1] = 0; subCollisionRowsMatrix1.vals[1][2] = 0; subCollisionRowsMatrix2.vals[0][0] = -colNormal.x; subCollisionRowsMatrix2.vals[0][1] = -colNormal.y; subCollisionRowsMatrix2.vals[0][2] = -colPoint2PDot; subCollisionRowsMatrix2.vals[1][0] = 0; subCollisionRowsMatrix2.vals[1][1] = 0; subCollisionRowsMatrix2.vals[1][2] = 0; } private void setCollisionColsMatrix() { PhysicalObject2D mobj1 = this.obj1; PhysicalObject2D mobj2 = this.obj2; Double2D r1 = this.r1; Double2D r2 = this.r2; subCollisionColsMatrix1.vals[0][0] = -mobj1.getMassInverse(); subCollisionColsMatrix1.vals[0][1] = 0; subCollisionColsMatrix1.vals[1][0] = 0; subCollisionColsMatrix1.vals[1][1] = -mobj1.getMassInverse(); subCollisionColsMatrix1.vals[2][0] = mobj1.getMassMomentOfInertiaInverse() * r1.y; subCollisionColsMatrix1.vals[2][1] = -mobj1.getMassMomentOfInertiaInverse() * r1.x; subCollisionColsMatrix2.vals[0][0] = mobj2.getMassInverse(); subCollisionColsMatrix2.vals[0][1] = 0; subCollisionColsMatrix2.vals[1][0] = 0; subCollisionColsMatrix2.vals[1][1] = mobj2.getMassInverse(); subCollisionColsMatrix2.vals[2][0] = -mobj2.getMassMomentOfInertiaInverse() * r2.y; subCollisionColsMatrix2.vals[2][1] = mobj2.getMassMomentOfInertiaInverse() * r2.x; } private void setCollisionIntersectionMatrix() { subCollisionIntersectionMatrix.vals[0][0] = 0; subCollisionIntersectionMatrix.vals[0][1] = 0; subCollisionIntersectionMatrix.vals[1][0] = -colNormal.y; subCollisionIntersectionMatrix.vals[1][1] = colNormal.x; } private void setCollisionAnswersVector() { PhysicalObject2D mobj1 = this.obj1; PhysicalObject2D mobj2 = this.obj2; //relVel = colNormal.multiply(relVel.dotProduct(colNormal)); // Make the combined coefficient of restitution the product of the two // CFs. If sticky is set, make the coefficient 0 to reduce the relative // velocity of the two bodies to 0 (for resting contact) double CF; if (this.sticky) CF = 0; else CF = mobj1.getCoefficientOfRestitution() * mobj2.getCoefficientOfRestitution(); subCollisionAnswersVector.vals[0] = -relVel * CF; subCollisionAnswersVector.vals[1] = 0; } /** Applies the calculated impulses to the objects involved in the collision */ public void applyImpulses(int curAnswerRow, sim.util.matrix.Vector answers) { Double2D R = new Double2D(answers.vals[curAnswerRow], answers.vals[curAnswerRow + 1]); if (obj1 instanceof MobileObject2D) { MobileObject2D mobj1 = (MobileObject2D)obj1; mobj1.setVelocity(mobj1.getVelocity().add(R.multiply(mobj1.getMassInverse()))); mobj1.setAngularVelocity(mobj1.getAngularVelocity() + r1.perpDot(R.multiply(mobj1.getMassMomentOfInertiaInverse()))); } if (obj2 instanceof MobileObject2D) { MobileObject2D mobj2 = (MobileObject2D)obj2; mobj2.setVelocity(mobj2.getVelocity().subtract(R.multiply(mobj2.getMassInverse()))); mobj2.setAngularVelocity(mobj2.getAngularVelocity() - r2.perpDot(R.multiply(mobj2.getMassMomentOfInertiaInverse()))); } } }