001    package breakout;
002    
003    import java.awt.*;
004    import java.awt.geom.*;
005    import java.util.*;
006    import cs101.awt.geom.*;
007    
008    /** Basic implementation of a Ball, with velocity arbitrarily picked at 
009      *  magnitude 3 and direction pi/3.
010    **/
011    public class BasicBall implements Ball {
012        /** The diameter of a BasicBall. **/
013        public static final int DEFAULT_DIAM = 10;
014        
015        /** The location of this BasicBall.  Uses Point2D.Double to increase the
016          * accuracy of velocity calculations. **/
017        protected Point2D.Double location;
018        /** The size of this BasicBall. **/
019        protected Dimension size;
020        
021        /** The velocity vector of this BasicBall.  **/
022        protected Line2D.Float velvect;
023        
024        /** Whether or not we died during the last resolveBounces phase of the tick. **/
025        protected boolean diedrebounding;
026    
027        /** Creates a default ball with speed 3, direction pi/3, and diameter 10 **/
028        public BasicBall() {
029            this.size = new Dimension(BasicBall.DEFAULT_DIAM, BasicBall.DEFAULT_DIAM);
030            float dx = (float) (3*Math.cos(Math.PI/3)),
031                  dy = (float) (-3*Math.sin(Math.PI/3));
032            this.velvect = new Line2D.Float(0f, 0f, dx, dy);
033            this.diedrebounding=false;
034        }
035        
036        /** Sets the location of this ball to (p.x, p.y). 
037          * 
038          * @param p A Point object representing the ball's intended location.
039        **/
040        public void setLocation(Point p) {
041            this.location = new Point2D.Double(p.x, p.y);
042        }
043        /** Gets the current coordinates of the ball, cast to an integer grid.
044          *
045          * @return a new Point object representing the approximate location of the ball.
046        **/
047        public Point getLocation() {
048            return new Point((int)this.location.x, (int)this.location.y);
049        }
050        
051        /** Gets the current size of the ball. 
052          * 
053          * @return a new Dimension object representing the size of the ball.
054        **/
055        public Dimension getSize() {
056            return new Dimension(this.size.width, this.size.height);
057        }
058        
059        /** Called by World every tick; sets the new location of the ball, as affected by velocity. **/
060        public void update() {
061            this.location = new Point2D.Double(this.location.x+this.velvect.getX2(), 
062                                               this.location.y+this.velvect.getY2());
063        }
064        
065        /** Called by an object implementing the Rebounding interface when it has detected
066          * an intersection with this ball.
067          * If this method is overridden in a subclass, it should be sure to call super(bc) to maintain the
068          * mechanism that manages the death of balls when they hit the floor.
069          *
070          * @param bc The BreakoutComponent/Rebounding object that detected the hit.
071          * @return <code>false</code>.  Invalid to return <code>true</code>, since Rebounding objects may not be 
072          *  removed during the phase of the tick where hits are calculated.
073          * @see breakout.BreakoutComponent
074          * @see breakout.Rebounding
075        **/
076        public boolean hit(BreakoutComponent bc) { 
077            if(bc instanceof Wall && ((Wall)bc).killing()) {
078                this.diedrebounding=true;
079            }
080            return false;    
081        }
082        
083        /** Gets the java.awt.Shape of this ball.
084          *
085          * @return a new Shape label at the same location and of the same size/shape as this ball.
086        **/
087        public Shape getShape() {
088            return new Ellipse2D.Double(this.location.x, this.location.y, this.size.width, this.size.height);
089        }
090        
091        /** Paints the ball.
092          *
093          * @param g Graphics object for this ball's coordinate frame(ie, already located where we are).
094        **/
095        public void paint(Graphics g) {
096            g.setColor(Color.blue);
097            g.fillOval(0,0,this.size.width, this.size.height);
098        }
099        
100        /** Checks all the BreakoutComponents in an iterator to see if it intersects them,
101          * hit()s them, and recalculates its velocity vector.
102          * 
103          * @param it Iterator handed to it by World of all the BreakoutComponents on the current Board.
104        **/
105        public void resolveBounces(Iterator it) {
106            /* Note to browsers: reusing this code is not recommended, as it uses stuff out
107             * of the cs101 library, you won't find it elsewhere and won't be able to use it
108             * much in the future, etc, etc */ 
109            while(it.hasNext()) {
110                BreakoutComponent bc = (BreakoutComponent)it.next();
111                if(!bc.equals(this)) { //skip ourself
112                    Shape theirShape = bc.getShape(),
113                          myShape = this.getShape();
114                    // ShapeUtils magic:
115                    if(myShape.intersects(theirShape.getBounds2D()) && 
116                       ShapeUtils.isOverlapping(ShapeUtils.getPreciseShape(myShape, 2),
117                                                ShapeUtils.getPreciseShape(theirShape, 2))) {
118                        this.velvect = ShapeUtils.doReflect(myShape, theirShape, this.velvect);
119                        
120                        // this is why BreakoutComponent.hit(bc) returns a boolean:
121                        if(bc.hit(this)) {
122                            it.remove();
123                        }
124                    }
125                }
126            }
127        }
128        
129        /** Tells whether we hit the floor the last time we resolved bounces.
130          * 
131          * This provides a way to allow balls to die without the component iterators
132          *  complaining about being modified in the middle of iterating.
133          *
134          * @return <code>true</code> if we died while rebounding; <code>false</code> otherwise.
135        **/
136        public boolean diedWhileRebounding() {
137            if(this.diedrebounding == false) {
138                return false;
139            } else {
140                this.diedrebounding = false;
141                return true;
142            }
143        }
144    }