▶ 书中第六章部分程序,包括在加上自己补充的代码,粒子碰撞系统及用到的粒子类

● 粒子系统

 package package01;

 import java.awt.Color;
import edu.princeton.cs.algs4.StdIn;
import edu.princeton.cs.algs4.StdDraw;
import edu.princeton.cs.algs4.MinPQ;
import edu.princeton.cs.algs4.Particle; public class class01
{
private static final double HZ = 0.5; // 每个时间步长内重画的次数
private MinPQ<Event> pq; // 优先队列
private double t = 0.0; // 计时器
private Particle[] particles; // 粒子数据列表 private static class Event implements Comparable<Event> // 碰撞事件类
{
private final double time; // 预计事件发生时间
private final Particle a, b; // 事件中的粒子
private final int countA, countB; // 粒子在加入事件中时的已碰撞的次数 public Event(double inputT, Particle inputA, Particle inputB) // 输入当前时间和两个粒子,计算碰撞事件
{
time = inputT;
a = inputA;
b = inputB;
countA = (a != null) ? a.count() : -1;
countB = (b != null) ? b.count() : -1;
} public int compareTo(Event that) // 比较两个事件哪个先发生
{
return Double.compare(this.time, that.time);
} public boolean isValid() // 判断事件是否有效
{
return (a == null || a.count() == countA) && (b == null || b.count() == countB);// 原代码简化版
//if (a != null && a.count() != countA || b != null && b.count() != countB) // 原代码,只要有粒子当前实际碰撞数与事件中记录的 count 不等,说明事件失效
// return false;
//return true;
}
} public class01(Particle[] inputParticle)
{
particles = inputParticle.clone(); // 输入列表的深拷贝
} private void predict(Particle a, double limit) // 更新优先队列中关于粒子 a 的事件
{
if (a == null)
return;
for (int i = 0; i < particles.length; i++) // 预测 a 与各粒子碰撞的时间,只要时间小于阈值就将其放入优先队列
{
double targetTime = t + a.timeToHit(particles[i]);
if (targetTime <= limit)
pq.insert(new Event(targetTime, a, particles[i]));
}
double targetTimeX = t + a.timeToHitVerticalWall(), targetTimeY = t + a.timeToHitHorizontalWall();
if (targetTimeX <= limit)
pq.insert(new Event(targetTimeX, a, null));
if (targetTimeY <= limit)
pq.insert(new Event(targetTimeY, null, a));
} private void redraw(double limit) // 重画所有粒子位置
{
StdDraw.clear();
for (int i = 0; i < particles.length; i++)
particles[i].draw();
StdDraw.show();
StdDraw.pause(20); // 暂停 20 ms
if (t < limit) // 还没到时限,加入重画事件
pq.insert(new Event(t + 1.0 / HZ, null, null));
} public void simulate(double limit) // 模拟器
{
pq = new MinPQ<Event>();
for (int i = 0; i < particles.length; i++) // 首次计算所有粒子之间的碰撞
predict(particles[i], limit);
for (pq.insert(new Event(0, null, null)); !pq.isEmpty();) // 多加入一个重画所有粒子位置的事件
{
Event e = pq.delMin(); // 取出发生时间最近的时间,判断是否有效
if (!e.isValid())
continue;
Particle a = e.a, b = e.b;
for (int i = 0; i < particles.length; i++) // 更新所有粒子位置
particles[i].move(e.time - t);
t = e.time; // 更新当前时间 if (a != null && b != null) // 粒子 - 粒子碰撞
a.bounceOff(b);
else if (a != null && b == null) // 粒子撞竖直墙壁
a.bounceOffVerticalWall();
else if (a == null && b != null)
b.bounceOffHorizontalWall(); // 粒子撞水平墙壁
else if (a == null && b == null)
redraw(limit); // 仅重画所有粒子位置
predict(a, limit); // 重新预测 a 与 b相关的事件
predict(b, limit);
}
} public static void main(String[] args)
{
StdDraw.setCanvasSize(600, 600); // 窗口大小
StdDraw.enableDoubleBuffering(); // double 缓冲区
Particle[] particles; // 粒子列表 if (args.length == 1) // 输入一个参数,生成相应个数的个粒子
{
int n = Integer.parseInt(args[0]);
particles = new Particle[n];
for (int i = 0; i < n; i++)
particles[i] = new Particle();
}
else // 否则按标准输入流依次输入每个粒子的信息
{
int n = StdIn.readInt();
particles = new Particle[n];
for (int i = 0; i < n; i++)
{
double rx = StdIn.readDouble();
double ry = StdIn.readDouble();
double vx = StdIn.readDouble();
double vy = StdIn.readDouble();
double radius = StdIn.readDouble();
double mass = StdIn.readDouble();
int r = StdIn.readInt();
int g = StdIn.readInt();
int b = StdIn.readInt();
Color color = new Color(r, g, b);
particles[i] = new Particle(rx, ry, vx, vy, radius, mass, color);
}
}
class01 system = new class01(particles); // 模拟和输出
system.simulate(10000); // 模拟事件 10s
}
}

● 粒子类

package package01;

import java.awt.Color;
import edu.princeton.cs.algs4.StdDraw;
import edu.princeton.cs.algs4.StdRandom; public class class01
{
private static final double INFINITY = Double.POSITIVE_INFINITY; private double rx, ry;
private double vx, vy;
private int count; // 粒子已经碰撞的次数
private final double radius;
private final double mass;
private final Color color; public class01(double inputRx, double inputRy, double inputVx, double inputVy, double inputRadius, double inputMass, Color inputColor)
{
rx = inputRx;
ry = inputRy;
vx = inputVx;
vy = inputVy;
radius = inputRadius;
mass = inputMass;
color = inputColor;
} public class01()
{
rx = StdRandom.uniform(0.0, 1.0);
ry = StdRandom.uniform(0.0, 1.0);
vx = StdRandom.uniform(-0.005, 0.005);
vy = StdRandom.uniform(-0.005, 0.005);
radius = 0.02;
mass = 0.5;
color = Color.BLACK;
} public void move(double dt)
{
rx += vx * dt;
ry += vy * dt;
} public void draw() // 绘制粒子
{
StdDraw.setPenColor(color);
StdDraw.filledCircle(rx, ry, radius);
} public int count()
{
return count;
} public double timeToHit(class01 that)
{
if (this == that)
return INFINITY;
double dx = that.rx - rx, dy = that.ry - ry, dvx = that.vx - vx, dvy = that.vy - vy;
double dvdr = dx * dvx + dy * dvy;
if (dvdr > 0) // Δx 与 Δv 同号,不会撞
return INFINITY;
double dvdv = dvx * dvx + dvy * dvy;
if (dvdv == 0) // 速度完全相等,不会撞
return INFINITY;
double drdr = dx * dx + dy * dy;
double dist = radius + that.radius;
double d = (dvdr*dvdr) - dvdv * (drdr - dist * dist);
return (d > 0) ? -(dvdr + Math.sqrt(d)) / dvdv : INFINITY;
} public double timeToHitVerticalWall()
{
return (vx > 0) ? (1.0 - rx - radius) / vx : ((vx < 0) ? (radius - rx) / vx : INFINITY);
} public double timeToHitHorizontalWall()
{
return (vy > 0) ? (1.0 - ry - radius) / vy : ((vy < 0) ? (radius - ry) / vy : INFINITY);
} public void bounceOff(class01 that)
{
double dx = that.rx - rx, dy = that.ry - ry;
double dvx = that.vx - vx, dvy = that.vy - vy;
double dvdr = dx * dvx + dy * dvy;
double dist = radius + that.radius;
double magnitude = 2 * mass * that.mass * dvdr / ((mass + that.mass) * dist);
double fx = magnitude * dx / dist, fy = magnitude * dy / dist;
vx += fx / mass;
vy += fy / mass;
that.vx -= fx / that.mass;
that.vy -= fy / that.mass;
count++;
that.count++;
} public void bounceOffVerticalWall()
{
vx = -vx;
count++;
} public void bounceOffHorizontalWall()
{
vy = -vy;
count++;
} public double kineticEnergy()
{
return 0.5 * mass * (vx*vx + vy * vy);
}
}

《算法》第六章部分程序 part 1的更多相关文章

  1. 《算法》第六章部分程序 part 7

    ▶ 书中第六章部分程序,加上自己补充的代码,包括全局最小切分 Stoer-Wagner 算法,最小权值二分图匹配 ● 全局最小切分 Stoer-Wagner 算法 package package01; ...

  2. 《算法》第六章部分程序 part 6

    ▶ 书中第六章部分程序,包括在加上自己补充的代码,包括二分图最大匹配(最小顶点覆盖)的交替路径算法和 HopcroftKarp 算法 ● 二分图最大匹配(最小顶点覆盖)的交替路径算法 package ...

  3. 《算法》第六章部分程序 part 5

    ▶ 书中第六章部分程序,包括在加上自己补充的代码,网络最大流 Ford - Fulkerson 算法,以及用到的流量边类和剩余流量网络类 ● 网络最大流 Ford - Fulkerson 算法 pac ...

  4. 《算法》第六章部分程序 part 8

    ▶ 书中第六章部分程序,加上自己补充的代码,包括单纯形法求解线性规划问题 ● 单纯形法求解线性规划问题 // 表上作业法,I 为单位阵,y 为对偶变量,z 为目标函数值 // n m 1 // ┌── ...

  5. 《算法》第六章部分程序 part 4

    ▶ 书中第六章部分程序,包括在加上自己补充的代码,利用后缀树查找最长重复子串.查找最大重复子串并输出其上下文(Key word in context,KWIC).求两字符串的最长公共子串 ● 利用后缀 ...

  6. 《算法》第六章部分程序 part 3

    ▶ 书中第六章部分程序,包括在加上自己补充的代码,后缀树的两种实现 ● 后缀树实现一 package package01; import java.util.Arrays; import edu.pr ...

  7. 《算法》第六章部分程序 part 2

    ▶ 书中第六章部分程序,包括在加上自己补充的代码,B-树 ● B-树 package package01; import edu.princeton.cs.algs4.StdOut; public c ...

  8. 《算法》第一章部分程序 part 1

    ▶ 书中第一章部分程序,加上自己补充的代码,包括若干种二分搜索,寻找图上连通分量数的两种算法 ● 代码,二分搜索 package package01; import java.util.Arrays; ...

  9. 《算法》第二章部分程序 part 5

    ▶ 书中第二章部分程序,加上自己补充的代码,包括利用优先队列进行多路归并和堆排序 ● 利用优先队列进行多路归并 package package01; import edu.princeton.cs.a ...

随机推荐

  1. linux新手非常有用的20个命令

    引用:http://www.oschina.net/translate/useful-linux-commands-for-newbies 1. ls命令 ls命令是列出目录内容(List Direc ...

  2. Docker-compose ports和expose的区别

    docker-compose中有两种方式可以暴露容器的端口:ports和expose. 1 ports ports暴露容器端口到主机的任意端口或指定端口,用法: ports: - "80:8 ...

  3. 修改docker容器的端口映射

    大家都知道docker run可以指定端口映射,但是容器一旦生成,就没有一个命令可以直接修改.通常间接的办法是,保存镜像,再创建一个新的容器,在创建时指定新的端口映射. 有没有办法不保存镜像而直接修改 ...

  4. Quartz不用配置文件配置启动

    StdSchedulerFactory schedulerFactory = null; try { schedulerFactory = new StdSchedulerFactory(); Pro ...

  5. 2.C++语言特性

    一.普遍编程语言的特征 任何常用的编程语言都具备一组公共的语法特征,不同的语言仅在特征的细节上有所区别.所以,要想掌握一门语言,需要理解其语法特征的实现细节是第一步.  最基本的特征包括:       ...

  6. 史上最全Spring面试71题与答案

    1.什么是spring? Spring是个java企业级应用的开源开发框架.Spring主要用来开发Java应用,但是有些扩展是针对构建J2EE平台的web应用.Spring框架目标是简化Java企业 ...

  7. linux采集CPU温度并上传数据到云平台判断是否需要beep

    如果要beep肯定要apt install beep的 但光安装好还不够,需要执行模块加载 /sbin/modprobe pcspkr 再写一个bash脚本 data=$(/usr/bin/senso ...

  8. sqlite之多线程总结

    12.android 多线程数据库读写分析与优化 11.多线程操作Sqlite? ==== 11.android 多线程数据库读写分析与优化 最新需要给软件做数据库读写方面的优化,之前无论读写,都是用 ...

  9. [UE4]Is Server判断是否在服务器端

  10. Laravel 5.5 FormRequest 自定义错误消息 postman调试时X-Requested-With设为XMLHttpRequest

    Laravel 5.5 FormRequest 自定义错误消息 使用FormRequest进行表单验证,就不用让验证逻辑和控制器里面的逻辑都混在一起.但在使用的时候呢,发现json错误返回的数据,与我 ...