package ballwar.model; import java.awt.*; import java.awt.geom.Point2D; /** * Superclass for commands involving elastic collisions of round balls with individual masses. * This superclass provides protected utility methods for the calculations. * * Sub-classes must implement the "public void apply(Ball target)" method of IBallCmd. * The apply method must determine if the source and target balls are within collision distance * and then calculate the impulse of the collision. The collisionUpdate method can be used to * change the velocity of the source and target balls by calling it with appropriate source and target * balls and with the appropriate sign on the impulse. * * Copyright (c) 2010 by Stephen Wong * @author Stephen Wong */ public abstract class AElasticMassCollisionCmd implements IBallCmd { // Subclasses need to override apply() to make determination if collision has occurred. /** * Returns the reduced mass of the two balls (m1*m2)/(m1+m2) * Gives correct result if one of the balls has infinite mass. * @param mSource Mass of the source ball * @param mTarget Mass of the target ball */ protected double reducedMass(double mSource, double mTarget){ if(mSource == Double.POSITIVE_INFINITY) return mTarget; if(mTarget == Double.POSITIVE_INFINITY) return mSource; else return (mSource*mTarget)/(mSource+mTarget); //else return mSource/(1.0+(mSource/mTarget)); // check on mTarget not needed in this calc. } /** * The amount to add to the separation distance to insure that the two balls are beyond collision distance */ private double Nudge = 1.1; /** * Calculates the impulse (change in momentum) of the collision in the direction from the source to the target * This method calculates the impulse on the source ball. The impulse on the target ball is the negative of the result. * Also moves source ball out of collision range along normal direction. * The change in velocity of the source ball is the impulse divided by the source's mass * The change in velocity of the target ball is the negative of the impulse divided by the target's mass * * Operational note: Even though theoretically, the difference in velocities of two balls should be co-linear * with the normal line between them, the discrete nature of animations means that the point where collision is * detected may not be at the same point as the theoretical contact point. This method calculates the * rebound directions as if the two balls were the appropriate radii such that they had just contacted * _at_the_point_of_collision_detection_. This may give slightly different rebound direction than one * would calculate if they contacted at the theoretical point given by their actual radii. * * @param lSource Location of the source ball * @param vSource Velocity of the source ball * @param lTarget Location of the target ball * @param vTarget Velocity of the target ball * @param reducedMass Reduced mass of the two balls * @param distance Distance between the two balls. * @param deltaR The difference in minimum minus actual separation. Should be a pos. value. * @return */ protected Point2D.Double impulse(Point lSource, Point vSource, Point lTarget, Point vTarget, double reducedMass, double distance, double deltaR){ // Calculate the normal vector, from source to target double nx = ((double)(lTarget.x - lSource.x))/distance; double ny = ((double)(lTarget.y - lSource.y))/distance; // delta velocity (speed, actually) in normal direction, source to target double dvn = (vTarget.x - vSource.x)*nx + (vTarget.y - vSource.y)*ny; //double dVx = vTarget.x - vSource.x; //double dVy = vTarget.y - vSource.y; //double dV = Math.sqrt(dVx*dVx + dVy+dVy); //double nx = dVx/dV; //double ny = dVy/dV; //double minSeparation = distance + deltaR; //int x = lTarget.x+(int)(nx*Nudge*minSeparation); //int y = lTarget.y +(int)(ny*Nudge*minSeparation); // move the source ball beyond collision range of the target ball, along the normal direction. lSource.translate((int)Math.ceil(-nx*(Nudge+deltaR)), (int)Math.ceil(-ny*(Nudge+deltaR))); //lSource.setLocation(x, y); return new Point2D.Double(2.0*reducedMass*dvn*nx, 2.0*reducedMass*dvn*ny); //return new Point2D.Double(2.0*reducedMass*dVx, 2.0*reducedMass*dVy); } /** * Updates the velocity of the source ball, given an impulse, then calls the source's update strategy's * updateCollision method. * The change in velocity is the impulse divided by the (source) ball's mass. * To change the velocity of the target ball, switch the source and target input parameters and negate the impulse values. * * @param source The ball to update * @param target The ball being collided with * @param impX x-coordinate of the impulse * @param impY y-coordinate of the impulse */ protected void updateCollision(Ball source, Ball target, double impX, double impY){ source.getVelocity().translate((int)Math.round(impX/source.getMass()), (int)Math.round(impY/source.getMass())); source.collide(target); } }