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 }