📄 billiards.java
字号:
//Billiards.java
import java.awt.*;
import java.applet.*;
//globals类设置了全局常量
class globals
{
public static int DrawScale = 10;
//设置最小速度
public static double MinVelocity = 0;
//设置速度的范围
public static double VelocityRange = 5;
//最小尺寸
public static double MinSize = 2;
public static double SizeRange = 2;
//球的数目
public static final int BallCount = 10;
//窗口的宽度和长度
public static final int WindowWidth = 500; //applet和表格的大小
public static final int WindowHeight = 500;
public static double MaxV()
{
return MinVelocity + VelocityRange;
}
}
public class Billiards extends Applet implements Runnable
{
//用来在循环中设置移动球体的时间长度
Thread BallTimer;
//采用两块缓冲区互换的方法
Image offImage;
Graphics offGraphics;
//初始化BilliardTable并且采用全局常量的设置
BilliardTable T = new BilliardTable(0,0,globals.WindowWidth / globals.DrawScale, globals.WindowHeight / globals.DrawScale);
//初始化球的个数
Ball B[] = new Ball[globals.BallCount];
//设置颜色
Color TableColor = new Color(0,0,0);
//当时间更新时使用
public void run() {
int i = 0; //For loop counters
int x = 0;
int y = 0;
double pX[] = new double[globals.BallCount];
double pY[] = new double[globals.BallCount];
System.out.println("Run");
Graphics g = getGraphics();
while (true)
{
offGraphics.setColor(TableColor);
offGraphics.fillRect((int)T.Left * globals.DrawScale, (int)T.Top * globals.DrawScale, (int)T.Width * globals.DrawScale, (int)T.Height * globals.DrawScale);
offGraphics.setColor(Color.black);
for(i = 0; i < globals.BallCount; i++)
{
pX[i] = B[i].Px;
pY[i] = B[i].Py;
//移动小球一点
B[i].Move(.05); }
for(i = 0; i < globals.BallCount; i++)
{
//当小球碰壁后的处理
CheckWallBounce(B[i]); }
if (globals.BallCount > 1)
{
for(x = 0; x < globals.BallCount; x++)
{ //检测每个球之间的情况
for(y = x + 1; y < globals.BallCount; y++) {
if (Separation(B[x],B[y]) <= RadSum(B[x],B[y])) {
Collide(B[x],B[y]); //COLLISON!!!!
}
}
}
}
for(i = 0; i < globals.BallCount; i++)
{
//绘制每一个小球
B[i].Draw(offGraphics); }
//绘制边界
T.DrawBorder(offGraphics);
//暂停一段时间
try { Thread.sleep(20); }
catch (InterruptedException e) { }
g.setColor(Color.white);
g.drawImage(offImage, 0, 0, null);
}
}
//返回一个随机颜色
public Color RandColor()
{
int r,g,b;
r = (int)(Math.random() * 256);
g = (int)(Math.random() * 256);
b = (int)(Math.random() * 256);
return new Color(r,g,b);
}
public void init() //开启线程并初始化
{
//设置applet的大小
setSize(globals.WindowWidth, globals.WindowHeight);
offImage = createImage((int)(T.Width + T.Left) * globals.DrawScale,(int) (T.Height + T.Top) * globals.DrawScale);
offGraphics = offImage.getGraphics();
double tmpRad; //存储临时半径
double tmpX, tmpY; //存储随机的位置
boolean overlap; //被用在循环中用来测试碰撞
int x;
double dX, dY, Rads; //计算球体之间的距离
for(int i = 0; i < globals.BallCount; i++)
{ //初始化各个小球并给出随机位置
tmpRad = Math.random() * globals.SizeRange + globals.MinSize;
do
{
overlap = false;
//给出随机的位置
tmpX = Math.random() * T.Width + T.Left;
tmpY = Math.random() * T.Height + T.Top;
for(x = 0; x < i; x++) //测试每一个球是否都获得了合适的位置
{
dX = B[x].Px - tmpX;
dY = B[x].Py - tmpY;
Rads = B[x].Radius + tmpRad;
if (Math.sqrt(dX * dX + dY * dY) < Rads) //是否它们之间的距离已经足够
overlap = true; //它们确实重叠了
}
} while (overlap);
B[i] = new Ball(tmpX, tmpY, tmpRad);
B[i].Vx = (Math.random() * globals.VelocityRange + globals.MinVelocity) * PosNegRand();
B[i].Vy = (Math.random() * globals.VelocityRange + globals.MinVelocity) * PosNegRand();
B[i].BallColor = RandColor();
}
repaint(); //确定已经得到了绘制
BallTimer = new Thread(this); //设置一个新的线程
BallTimer.start(); //启动了run方法
}
public int PosNegRand()
{
double a = Math.random() * 2;
if (a >= 1)
a = 1;
if (a < 1)
a = -1;
return (int)a;
}
public void CheckWallBounce(Ball B)
{
double Xleft = B.Px;
double Ytop = B.Py;
double Ybottom = B.Py + 2 * B.Radius; //记录下左上角的位置
double Xright = B.Px + 2 * B.Radius; //记录了右下角的位置
if (Xleft < T.minX())
{
B.Px = T.minX();
B.WallBounceX();
}
if (Ytop < T.minY())
{
B.Py = T.minY();
B.WallBounceY();
}
if (Xright > T.maxX())
{
B.Px = T.maxX() - 2 * B.Radius;
B.WallBounceX();
}
if (Ybottom > T.maxY())
{
B.Py = T.maxY() - 2 * B.Radius;
B.WallBounceY();
}
}
public void PrintData()//为了调试
{
double TotalM = 0.0;
double TotalK = 0.0;
for(int i = 0; i < globals.BallCount; i++)
{
System.out.println("Ball " + i + ": ");
B[i].PrintData();
System.out.println();
TotalM += B[i].Momentum();
TotalK += B[i].Kinetic();
}
System.out.println("Total Momentum: " + TotalM);
System.out.println("Total Kinetic: " + TotalK);
System.out.println();
}
public void paint(Graphics g) //重绘整张表
{
T.Draw(g, TableColor);
}
public double Separation(Ball B1, Ball B2) //给出两个球之间的距离
{
double X1, X2, Y1, Y2;
X1 = B1.Px + B1.Radius;
X2 = B2.Px + B2.Radius;
Y1 = B1.Py + B1.Radius;
Y2 = B2.Py + B2.Radius;
return Math.sqrt((X2 - X1) * (X2 - X1) + (Y2 - Y1) * (Y2 - Y1));
}
public double RadSum(Ball B1, Ball B2) //计算两个球之间的半径和
{
return (B1.Radius + B2.Radius);
}
public void Collide(Ball B1, Ball B2)
{
/*以下代码绘制了发生碰撞的时候产生的效果*/
double Dx1, Dx2, Dy1, Dy2; //碰撞的距离
double X1, X2, Y1, Y2; //中心点和碰撞点的距离
double DxR, DyR; //实际距离 (碰撞覆盖)
double Dx, Dy; //假设的距离 (假设没有重叠)
double Vp1, Vp2, Vs1, Vs2; //速度设置
double newVs1, newVs2; //在计算过程中记录相应的速度
double distance; //球中心之间的真实距离
X1 = B1.Px + B1.Radius;
X2 = B2.Px + B2.Radius;
Y1 = B1.Py + B1.Radius;
Y2 = B2.Py + B2.Radius;
DxR = (X2 - X1);
DyR = (Y2 - Y1);
distance = Math.sqrt(DxR * DxR + DyR * DyR);
Dx = RadSum(B1,B2) * DxR / distance; //为了获得理想的碰撞所需要的数据
Dy = RadSum(B1,B2) * DyR / distance;
if (B1.Mass < B2.Mass)
{
X1 = (X1 - (Dx - DxR));
Y1 = (Y1 - (Dy - DyR));
B1.Px = X1 - B1.Radius;
B1.Py = Y1 - B1.Radius;
}
else
{
X2 = (X2 + (Dx - DxR));
Y2 = (Y2 + (Dy - DyR));
B2.Px = X2 - B2.Radius;
B2.Py = Y2 - B2.Radius;
}
//找到x和y各自到球中心节点的距离
Dx1 = (B1.Radius / RadSum(B1,B2)) * (X2 - X1);
Dx2 = (B2.Radius / RadSum(B1,B2)) * (X2 - X1);
Dy1 = (B1.Radius / RadSum(B1,B2)) * (Y2 - Y1);
Dy2 = (B2.Radius / RadSum(B1,B2)) * (Y2 - Y1);
Vs1 = StraightVelocity(B1.Vx, B1.Vy, Dx1, Dy1, B1.Radius);
Vp1 = PerpendicularVelocity(B1.Vx, B1.Vy, Dx1, Dy1, B1.Radius);
Vs2 = StraightVelocity(B2.Vx, B2.Vy, Dx2, Dy2, B2.Radius);
Vp2 = PerpendicularVelocity(B2.Vx, B2.Vy, Dx2, Dy2, B2.Radius);
newVs1 = CollisionVelocity(Vs1, Vs2, B1.Mass, B2.Mass);
newVs2 = CollisionVelocity(Vs2, Vs1, B2.Mass, B1.Mass);
B1.Vx = XVelocity(newVs1, Vp1, Dx1, Dy1, B1.Radius);
B1.Vy = YVelocity(newVs1, Vp1, Dx1, Dy1, B1.Radius);
B2.Vx = XVelocity(newVs2, Vp2, Dx2, Dy2, B2.Radius);
B2.Vy = YVelocity(newVs2, Vp2, Dx2, Dy2, B2.Radius);
}
public double StraightVelocity(double Vx, double Vy, double Dx, double Dy, double R)
{
return Vx * Dx / R + Vy * Dy / R;
} //速度值
public double PerpendicularVelocity(double Vx, double Vy, double Dx, double Dy, double R)
{
return Vy * Dx / R - Vx * Dy / R;
}
public double XVelocity(double Vs, double Vp, double Dx, double Dy, double R)
{
return Vs * Dx / R - Vp * Dy / R;
} //x 从S和P的速度值
public double YVelocity(double Vs, double Vp, double Dx, double Dy, double R)
{
return Vs * Dy / R + Vp * Dx / R;
} //y 来自S和P的速度值
public double CollisionVelocity(double V1, double V2, double m1, double m2) //碰撞之后返回速度值
{
return V1 * (m1-m2) / (m1+m2) + V2 * (2 * m2) / (m1 + m2);
}
}
class Ball
{
double Vx = 0; //x方向的速度
double Vy = 0; //y方向速度
double Px = 0; //x轴的位置
double Py = 0; //y轴的位置
double Density = 1; //球的密度
double Radius; //球的半径
double Mass;
Color BallColor = new Color(0,0,0); //球体初始化为白色
public Ball(double x, double y, double radius) //构造函数
{
double Volume = 4.0/3.0 * Math.PI * radius * radius * radius; // v = 4/3 pi r^3
Px = x;
Py = y;
Radius = radius;
Mass = Density * Volume;
}
public void PrintData()//输出全部球的信息(仅用于测试)
{
System.out.println("Px:" + Px);
System.out.println("Py:" + Py);
System.out.println("Mass:" + Mass);
System.out.println("Vx: " + Vx);
System.out.println("Vy: " + Vy);
System.out.println("V: " + Velocity());
System.out.println("Ek: " + Kinetic());
System.out.println("Mom: " + Momentum());
}
public void addVelocity(double x, double y)//增加球的速度
{
Vx+=x;
Vy+=y;
}
public double Velocity() //返回球的当前速度
{
return Math.sqrt(Vx * Vx + Vy * Vy);
}
public double Momentum()
{ //p = mv
return Mass * Vx + Mass * Vy;
}
public double Kinetic() //returns total kinetic energy of ball
{// ke = 1/2 m v^2
return .5 * Mass * Velocity() * Velocity();
}
public void WallBounceX()
{
Vx = -Vx; //ball switches x velocity
}
public void WallBounceY() //球移动到了边界上
{
Vy = -Vy; //ball switches y velocity
}
public void Move(double t) //将球移动一圈
{
Px = Px + Vx * t;
Py = Py + Vy * t;
}
public void Draw(Graphics g)
{
int top, left, size;
left = (int)(Px * globals.DrawScale);
top = (int)(Py * globals.DrawScale);
size = (int)(2 * Radius * globals.DrawScale);
g.setColor(BallColor); //绘制球的内部
g.fillOval(left, top, size, size);
//g.setColor(Color.black); //绘制小球的外边框
//g.drawOval(left, top, size, size);
}
//绘制小球所在的当前位置的颜色
public void Clear(Graphics g, Color TableColor)
{
int top, left, size;
left = (int)(Px * globals.DrawScale);
top = (int)(Py * globals.DrawScale);
size = (int)(2 * Radius * globals.DrawScale);
//覆盖了老的位置
g.setColor(TableColor);
//确保所有的线条都覆盖到了
g.fillOval(left-1, top-1, size+2, size+2);
}
}
class BilliardTable
{
double Left, Top, Width, Height;
public BilliardTable(double L, double T, double W, double H)
{
Left = L;
Top = T;
Width = W;
Height = H;
}
//球的最小的X值
public double minX()
{
return Left;
}
//对于一个球来说最小的值
public double minY()
{
return Top;
}
public double maxX()
{
return Left + Width;
}
public double maxY()
{
return Top + Height;
}
//绘制图表
public void Draw(Graphics g, Color TableColor)
{
//绘制所有的颜色
g.setColor(TableColor);
g.fillRect((int)(Left * globals.DrawScale), (int)(Top * globals.DrawScale), (int)(Width * globals.DrawScale), (int)(Height * globals.DrawScale));
DrawBorder(g);
}
//绘制边界
public void DrawBorder(Graphics g)
{
//采用了白颜色,更改方便
g.setColor(new Color(255,255,255));
g.drawRect((int)(Left * globals.DrawScale), (int)(Top * globals.DrawScale), (int)(Width * globals.DrawScale), (int)(Height * globals.DrawScale));
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -