/* Copyright 2006 by Sean Luke and George Mason University Licensed under the Academic Free License version 3.0 See the file "LICENSE" for more information */ package sim.app.tutorial5; import sim.engine.*; import sim.portrayal.*; import sim.util.*; import sim.field.network.*; import sim.field.continuous.*; import java.awt.*; import java.awt.geom.*; public class Ball extends SimplePortrayal2D implements Steppable { private static final long serialVersionUID = 1; // force on the Ball public double forcex; public double forcey; // Ball mass public double mass; // Current Ball velocity public double velocityx; public double velocityy; // did the Ball collide? public boolean collision; // for drawing: always sqrt of mass public double diameter; public double getVelocityX() { return velocityx; } public void setVelocityX(double val) { velocityx = val; } public double getVelocityY() { return velocityy; } public void setVelocityY(double val) { velocityy = val; } public double getMass() { return mass; } public void setMass(double val) { if (val > 0) { mass = val; diameter = Math.sqrt(val); } } public Ball(double vx, double vy, double m) { velocityx=vx; velocityy=vy; mass = m; diameter = Math.sqrt(m); } public void computeCollision(Tutorial5 tut) { /* // old previous code collision = false; Double2D me = tut.balls.getObjectLocation(this); Bag b = tut.balls.getNeighborsWithinDistance(me,Tutorial5.collisionDistance); for(int x=0;x<b.numObjs;x++) if( this != b.objs[x] ) { Double2D loc = tut.balls.getObjectLocation(b.objs[x]); if ((loc.x-me.x)*(loc.x-me.x) + (loc.y-me.y)*(loc.y-me.y) <= Tutorial5.collisionDistance * Tutorial5.collisionDistance) { collision = true; ((Ball)(b.objs[x])).collision = true; } } */ Double2D me = tut.balls.getObjectLocation(this); Bag b = tut.balls.getNeighborsExactlyWithinDistance(me,Tutorial5.collisionDistance); collision = b.numObjs > 1; // other than myself of course } public void addForce(Double2D otherBallLoc, Double2D myLoc, Band band) { // compute difference final double dx = otherBallLoc.x - myLoc.x; final double dy = otherBallLoc.y - myLoc.y; final double len = Math.sqrt(dx*dx + dy*dy); final double l = band.laxDistance; final double k = band.strength/512.0; // cut-down final double forcemagnitude = (len - l) * k; // add rubber band force if (len - l > 0) { forcex += (dx * forcemagnitude) / len; forcey += (dy * forcemagnitude) / len; } } public void computeForce(SimState state) { Tutorial5 tut = (Tutorial5) state; Network bands = tut.bands; Continuous2D balls = tut.balls; Double2D me = balls.getObjectLocation(this); forcex = 0; forcey = 0; // rubber bands exert a force both ways -- // so our graph is undirected. We need to get edges // both in and out, as they could be located either place Bag in = bands.getEdgesIn(this); Bag out = bands.getEdgesOut(this); if (in!=null) for(int x=0;x<in.numObjs;x++) { Edge e = (Edge)(in.objs[x]); Band b = (Band) (e.info); Ball other = (Ball)(e.from()); // from him to me Double2D him = balls.getObjectLocation(other); addForce(him,me,b); } if (out!=null) for(int x=0;x<out.numObjs;x++) { Edge e = (Edge)(out.objs[x]); Band b = (Band) (e.info); Ball other = (Ball)(e.to()); // from me to him Double2D him = balls.getObjectLocation(other); addForce(him,me,b); } } public void step(SimState state) { Tutorial5 tut = (Tutorial5) state; // acceleration = force / mass final double ax = forcex / mass; final double ay = forcey / mass; // velocity = velocity + acceleration velocityx += ax; velocityy += ay; // position = position + velocity Double2D pos = tut.balls.getObjectLocation(this); Double2D newpos = new Double2D(pos.x+velocityx, pos.y + velocityy); tut.balls.setObjectLocation(this,newpos); // compute collisions computeCollision(tut); } public void draw(Object object, Graphics2D graphics, DrawInfo2D info) { final double width = info.draw.width * diameter; final double height = info.draw.height * diameter; if (collision) graphics.setColor(Color.red); else graphics.setColor(Color.blue); final int x = (int)(info.draw.x - width / 2.0); final int y = (int)(info.draw.y - height / 2.0); final int w = (int)(width); final int h = (int)(height); // draw centered on the origin graphics.fillOval(x,y,w,h); } public boolean hitObject(Object object, DrawInfo2D range) { final double SLOP = 1.0; // need a little extra diameter to hit circles final double width = range.draw.width * diameter; final double height = range.draw.height * diameter; Ellipse2D.Double ellipse = new Ellipse2D.Double( range.draw.x-width/2-SLOP, range.draw.y-height/2-SLOP, width+SLOP*2, height+SLOP*2 ); return ( ellipse.intersects( range.clip.x, range.clip.y, range.clip.width, range.clip.height ) ); } }