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

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

● 粒子系统

  1 package package01;
  2 
  3 import java.awt.Color;
  4 import edu.princeton.cs.algs4.StdIn;
  5 import edu.princeton.cs.algs4.StdDraw;
  6 import edu.princeton.cs.algs4.MinPQ;
  7 import edu.princeton.cs.algs4.Particle;
  8 
  9 public class class01
 10 {
 11     private static final double HZ = 0.5;                               // 每个时间步长内重画的次数
 12     private MinPQ<Event> pq;                                            // 优先队列
 13     private double t = 0.0;                                             // 计时器
 14     private Particle[] particles;                                       // 粒子数据列表
 15 
 16     private static class Event implements Comparable<Event>             // 碰撞事件类
 17     {
 18         private final double time;                                      // 预计事件发生时间
 19         private final Particle a, b;                                    // 事件中的粒子
 20         private final int countA, countB;                               // 粒子在加入事件中时的已碰撞的次数
 21 
 22         public Event(double inputT, Particle inputA, Particle inputB)   // 输入当前时间和两个粒子,计算碰撞事件
 23         {
 24             time = inputT;
 25             a = inputA;
 26             b = inputB;
 27             countA = (a != null) ? a.count() : -1;
 28             countB = (b != null) ? b.count() : -1;
 29         }
 30 
 31         public int compareTo(Event that)                                // 比较两个事件哪个先发生
 32         {
 33             return Double.compare(this.time, that.time);
 34         }
 35 
 36         public boolean isValid()                                        // 判断事件是否有效
 37         {
 38             return (a == null || a.count() == countA) && (b == null || b.count() == countB);// 原代码简化版
 39                                                                                             //if (a != null && a.count() != countA || b != null && b.count() != countB)     // 原代码,只要有粒子当前实际碰撞数与事件中记录的 count 不等,说明事件失效
 40                                                                                             //    return false;
 41                                                                                             //return true;
 42         }
 43     }
 44 
 45     public class01(Particle[] inputParticle)
 46     {
 47         particles = inputParticle.clone();          // 输入列表的深拷贝
 48     }
 49 
 50     private void predict(Particle a, double limit)  // 更新优先队列中关于粒子 a 的事件
 51     {
 52         if (a == null)
 53             return;
 54         for (int i = 0; i < particles.length; i++)  // 预测 a 与各粒子碰撞的时间,只要时间小于阈值就将其放入优先队列
 55         {
 56             double targetTime = t + a.timeToHit(particles[i]);
 57             if (targetTime <= limit)
 58                 pq.insert(new Event(targetTime, a, particles[i]));
 59         }
 60         double targetTimeX = t + a.timeToHitVerticalWall(), targetTimeY = t + a.timeToHitHorizontalWall();
 61         if (targetTimeX <= limit)
 62             pq.insert(new Event(targetTimeX, a, null));
 63         if (targetTimeY <= limit)
 64             pq.insert(new Event(targetTimeY, null, a));
 65     }
 66 
 67     private void redraw(double limit)               // 重画所有粒子位置
 68     {
 69         StdDraw.clear();
 70         for (int i = 0; i < particles.length; i++)
 71             particles[i].draw();
 72         StdDraw.show();
 73         StdDraw.pause(20);                          // 暂停 20 ms
 74         if (t < limit)                              // 还没到时限,加入重画事件
 75             pq.insert(new Event(t + 1.0 / HZ, null, null));
 76     }
 77 
 78     public void simulate(double limit)              // 模拟器
 79     {
 80         pq = new MinPQ<Event>();
 81         for (int i = 0; i < particles.length; i++)  // 首次计算所有粒子之间的碰撞
 82             predict(particles[i], limit);
 83         for (pq.insert(new Event(0, null, null)); !pq.isEmpty();) // 多加入一个重画所有粒子位置的事件
 84         {
 85             Event e = pq.delMin();                  // 取出发生时间最近的时间,判断是否有效
 86             if (!e.isValid())
 87                 continue;
 88             Particle a = e.a, b = e.b;
 89             for (int i = 0; i < particles.length; i++)  // 更新所有粒子位置
 90                 particles[i].move(e.time - t);
 91             t = e.time;                             // 更新当前时间
 92 
 93             if (a != null && b != null)             // 粒子 - 粒子碰撞
 94                 a.bounceOff(b);
 95             else if (a != null && b == null)        // 粒子撞竖直墙壁
 96                 a.bounceOffVerticalWall();
 97             else if (a == null && b != null)
 98                 b.bounceOffHorizontalWall();        // 粒子撞水平墙壁
 99             else if (a == null && b == null)
100                 redraw(limit);                      // 仅重画所有粒子位置
101             predict(a, limit);                      // 重新预测 a 与 b相关的事件
102             predict(b, limit);
103         }
104     }
105 
106     public static void main(String[] args)
107     {
108         StdDraw.setCanvasSize(600, 600);            // 窗口大小
109         StdDraw.enableDoubleBuffering();            // double 缓冲区
110         Particle[] particles;                       // 粒子列表
111 
112         if (args.length == 1)                       // 输入一个参数,生成相应个数的个粒子
113         {
114             int n = Integer.parseInt(args[0]);
115             particles = new Particle[n];
116             for (int i = 0; i < n; i++)
117                 particles[i] = new Particle();
118         }
119         else                                        // 否则按标准输入流依次输入每个粒子的信息
120         {
121             int n = StdIn.readInt();
122             particles = new Particle[n];
123             for (int i = 0; i < n; i++)
124             {
125                 double rx = StdIn.readDouble();
126                 double ry = StdIn.readDouble();
127                 double vx = StdIn.readDouble();
128                 double vy = StdIn.readDouble();
129                 double radius = StdIn.readDouble();
130                 double mass = StdIn.readDouble();
131                 int r = StdIn.readInt();
132                 int g = StdIn.readInt();
133                 int b = StdIn.readInt();
134                 Color color = new Color(r, g, b);
135                 particles[i] = new Particle(rx, ry, vx, vy, radius, mass, color);
136             }
137         }
138         class01 system = new class01(particles);    // 模拟和输出
139         system.simulate(10000);                     // 模拟事件 10s
140     }
141 }

● 粒子类

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);
    }
}

猜你喜欢

转载自www.cnblogs.com/cuancuancuanhao/p/9945059.html