BounceArea.javaimport 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