BounceArea.java
import javafx.scene.Scene; import javafx.scene.Node; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.shape.*; import javafx.util.Duration; import java.util.*; import javafx.animation.KeyFrame; import javafx.animation.Timeline; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.collections.*; public class BounceArea extends Pane { Timer myTimer; double wd, ht; ArrayList<Ball> balls = new ArrayList<Ball>(); ArrayList<Ball> fixedPts = new ArrayList<Ball>(); ArrayList<WallSeg> segs = new ArrayList<WallSeg>(); ArrayList<Collision> collisions = new ArrayList<Collision>(); boolean done = false; Timeline animation; double simtime = 0.0; BounceArea() { Ball b; wd = 740; ht = 500; setStyle("-fx-background-color: honeydew;"); make_area(); Color [] colors = {Color.RED, Color.GREEN, Color.BLUE, Color.MAGENTA, Color.YELLOW}; String [] names = {"red","green","blue","magneta","yellow"}; for (int k=0; k<5; k++) { b = new Ball(); b.randomize(20.0,wd,ht); while (intersect_world(b)) b.randomize(10.0,wd,ht); b.setColor(colors[k]); b.set_name(names[k]); balls.add(b); getChildren().add(b.circle); } // Create a handler for animation EventHandler<ActionEvent> eventHandler = e -> { update2(); }; // Create an animation animation = new Timeline( new KeyFrame(Duration.millis(50), eventHandler)); animation.setCycleCount(Timeline.INDEFINITE); animation.play(); // Start animation } void make_area() { double sw = 2.0; // stroke width ObservableList<Node> ol = getChildren(); // draw grid int n; double w = 80; Path path1 = new Path(); ObservableList<PathElement> ob = path1.getElements(); for (n=0; n<7; n++) { ob.add(new MoveTo(10,10+n*w)); ob.add(new LineTo(10+9*w,10+n*w)); } for (n=0; n<10; n++) { ob.add(new MoveTo(10+n*w,10)); ob.add(new LineTo(10+n*w,10+6*w)); } path1.setStroke(Color.rgb(200,200,255)); // room boundaries Ball pt1, pt2, pt3, pt4, pt5; pt1 = new Ball(10, 10); pt2 = new Ball(730, 10); pt3 = new Ball(730, 490); pt4 = new Ball(10, 490); segs.add(new WallSeg(pt1,pt2)); segs.add(new WallSeg(pt2,pt3)); segs.add(new WallSeg(pt3,pt4)); segs.add(new WallSeg(pt4,pt1)); // draw room boundaries Path path2 = new Path(); ob = path2.getElements(); ob.add(new MoveTo(pt1.px,pt1.py)); ob.add(new LineTo(pt2.px,pt2.py)); ob.add(new LineTo(pt3.px,pt3.py)); ob.add(new LineTo(pt4.px,pt4.py)); ob.add(new ClosePath()); path2.setStroke(Color.BLACK); path2.setStrokeWidth(sw); // obstruction1 Path path3 = new Path(); ob = path3.getElements(); pt1 = new Ball(540, 300); fixedPts.add(pt1); pt2 = new Ball(540, 160); fixedPts.add(pt2); pt3 = new Ball(300, 160); fixedPts.add(pt3); pt4 = new Ball(300, 390); fixedPts.add(pt4); pt5 = new Ball(540, 390); fixedPts.add(pt5); ob.add(new MoveTo(pt1.px,pt1.py)); ob.add(new LineTo(pt2.px,pt2.py)); ob.add(new LineTo(pt3.px,pt3.py)); ob.add(new LineTo(pt4.px,pt4.py)); ob.add(new LineTo(pt5.px,pt5.py)); path3.setStroke(Color.BLACK); path3.setStrokeWidth(sw); segs.add(new WallSeg(pt1,pt2)); segs.add(new WallSeg(pt2,pt3)); segs.add(new WallSeg(pt3,pt4)); segs.add(new WallSeg(pt4,pt5)); // obstruction 2 Path path4 = new Path(); ob = path4.getElements(); pt1 = new Ball(200, 490); pt2 = new Ball(200, 200); pt3 = new Ball(100, 200); segs.add(new WallSeg(pt1,pt2)); segs.add(new WallSeg(pt2,pt3)); fixedPts.add(pt2); fixedPts.add(pt3); ob.add(new MoveTo(pt1.px,pt1.py)); ob.add(new LineTo(pt2.px,pt2.py)); ob.add(new LineTo(pt3.px,pt3.py)); path4.setStroke(Color.BLACK); path4.setStrokeWidth(sw); ol.addAll(path1,path2,path3,path4); } public boolean intersect_world(Ball bt) { for (Ball b: balls) { if (b.dist(bt)<50) return true; } for (WallSeg seg: segs) { if (seg.dist(bt)<0) return true; } return false; } public boolean check_distance() { double dist; boolean flag = false; int nballs = balls.size(); for (int i=0; i<nballs-1; i++) { for (int j=i+1; j<nballs; j++) { Ball b1 = balls.get(i); Ball b2 = balls.get(j); dist = b1.dist(b2); if (dist<-0.01) { System.out.println("dist " + dist + " " + b1 + b2); double dx = b2.px-b1.px; double dy = b2.py-b1.py; System.out.println( b1.info()); System.out.println( b2.info()); System.out.println("dx " + dx + " dy " + dy); System.out.println("sep " + Math.hypot(dx,dy) + " radius " + (b1.radius+b2.radius)); flag = true; } } } return flag; } double wd1, ht1; public void update2() { wd1 = getWidth(); ht1 = getHeight(); double tstep, tmore; tstep = 1.0; tmore = tstep; simtime += tstep; while (tmore>0.0) { update_collision_list(tmore); if (collisions.size()>0) { java.util.List<Collision> list = collisions; Collections.<Collision>sort(list); Collision c = collisions.get(0); tstep = c.timestep; tmore = tmore - tstep; for (Ball ball: balls) ball.move(tstep); c.update_velocity(); } else { tstep = tmore; tmore = 0.0; for (Ball ball: balls) ball.move(tstep); } } } public void update_collision_list(double tstep) { Collision c; collisions.clear(); double t; for (Ball ball: balls) { t = ball.intersect_window_vertical(wd1); if (t>0.0 && t<tstep) collisions.add(new Collision(t,ball,1)); t = ball.intersect_window_horizontal(ht1); if (t>0.0 && t<tstep) collisions.add(new Collision(t,ball,2)); for (WallSeg seg: segs) { t = seg.intersect(ball); if (t<-1.5) System.out.println(seg.str); if (t>0.0 && t<tstep) collisions.add(new Collision(t,ball,seg)); } for (Ball pt: fixedPts) { t = ball.intersect(pt); if (t>0.0 && t<tstep) { c = new Collision(t,ball,pt); collisions.add(c); c.ndx = -2; } } } int nballs = balls.size(); for (int i=0; i<nballs-1; i++) { for (int j=i+1; j<nballs; j++) { Ball b1 = balls.get(i); Ball b2 = balls.get(j); t = b1.intersect(b2); if (t>0.0 && t<tstep) collisions.add(new Collision(t,b1,b2)); } } } }
Maintained by John Loomis, updated Sat Mar 03 12:46:03 2018