2012-06-13 169 views
1

我正在尝试编写一个Java程序来支持离格外扩散有限的融合仿真。用于模拟移动粒子的基本代码已到位,直到所述粒子碰到静态中心粒子。在这一点上,我试图确保移动的粒子刚刚接触(切线)静态粒子。但是,由于不明原因,它有时会失败(8个粒子中的前2个相交,其他6个很好)。圆圈碰撞

下面是代码:

boolean killed, collide; 
    double xp, yp, dx, dy, theta, xpp, ypp, length; 

    int xc = 200; 
    int yc = 200; 
    int killRadius = 200; 
    int releaseRadius = 150; 
    int partRadius = 14; 
    int partDiam = 2 * partRadius; 

    drawCircle(xc, yc, killRadius); // kill 
    drawCircle(xc, yc, releaseRadius); // release 
    drawCircle(xc, yc, partRadius); // center particle 

    //while (true) { 
     killed = false; 
     collide = false; 

     theta = Math.random() * Math.PI * 2; 
     xp = xc + releaseRadius * Math.cos(theta); 
     yp = yc + releaseRadius * Math.sin(theta); 

     while (true) { 

      theta = Math.random() * Math.PI * 2; 
      length = partDiam; 

      xpp = xp; 
      ypp = yp; 

      xp = xp + length * Math.cos(theta); 
      yp = yp + length * Math.sin(theta); 

      //drawCircle((int) xp, (int) yp, partRadius); 

      // Should it be killed ? (maybe could use a box to fasten 
      // computations... 
      // Would switching the test for kill w test for collision 
      // improve perf ? 
      dx = xp - xc; 
      dy = yp - yc; 
      if ((dx * dx) + (dy * dy) > killRadius * killRadius) { 
       killed = true; 
       break; 
      } 

      // Does it collide with center? replace by any particle... 
      dx = xp - xc; 
      dy = yp - yc; 
      if ((dx * dx) + (dy * dy) < (partDiam) * (partDiam)) { 
       collide = true; 
       break; 
      } 
     } 
// Probably something is wrong here... 
     if (collide) { 
      // no absolute value because particles move at most by diameter 
      double depthPenetration = partDiam 
        - Math.sqrt((dx * dx) + (dy * dy)); 
      dx = xpp - xp; 
      dy = ypp - yp; 
      // shorten distance travelled by penetration length to ensure 
      // that 
      // particle is tangeant 
      length = Math.sqrt((dx * dx) + (dy * dy)) - depthPenetration; 
      xp = xpp + length * Math.cos(theta); 
      yp = ypp + length * Math.sin(theta); 
      drawCircle((int) xp, (int) yp, partRadius); 
     } 
    //} 

当然,我问前检查许多引用,但不能发现任何错误的代码...帮助将不胜感激。

+2

我调试的代码太多。我们先从 - 你得到什么输出,你期望什么? – djechlin

+0

如果我是你,我会创建一系列测试计算的单元测试。 –

+0

我不得不承认我仍然很难理解你正在努力完成什么,而不是你想要解决的问题。什么是有问题的症状,你期望什么,并且你得到了什么? – trumpetlicks

回答

0

我不认为我可以调试你的代码,但是我在很多年前(十多年前)编写了一些基于java.awt.Shape的碰撞例程,这可能会有所帮助...从这里检查ShapeUtils.java :

http://www.cs101.org/psets/breakout/solns/breakout-src.jar

一个你可能有很可能的问题是Java界是连得4分样条曲线,因此如果你检查是否所有定义圆点是另一种形状的内部,你仍然会错过样条曲线凸出到其他形状的情况,但没有任何点实际在物体内部。我的解决方案(这可能不是不够好,你的情况是重拍的形状,直到沿着各自的边缘点之间的距离比最大可接受误差较小:

/** 
    * Convert any AWT shape into a shape with a specified precision. 
    * The specified precision indicates the maximum tolerable distance 
    * between knot points. If the shape is already precise enough then 
    * it is returned unmodified. 
    * 
    * @param aShape the shape to be converted if necessary. 
    * @param precision the maximum tolerable distance between knot points. 
    * @return A more precise version of the shape, or the same shape if 
    *   the precision is already satisfied. 
    */ 
    public static Shape getPreciseShape(Shape aShape, float precision) { 

这种精密与模拟的速度沿对象,告诉我在模拟中我必须检查碰撞(最大增量时间)的频率......如果您不缩放时间间隔,快速移动的模拟对象可以在一个时间间隔内通过固体对象拉链少于所需精度的时间

基本上这样就成了计算成本和结果精度之间的直接折衷。我只是希望游戏看起来不错,所以我只需要非常精确以确保物体不会重叠超过半个像素左右。

编辑:让我总结一下我的建议......在重新阅读我知道我得到了在细节丢失,从来没有设法指出关键的概念... Java有很多,对于工作得非常好例程测试Shape的各种实现(包括圆圈)。我的建议是尽可能使用java库,并获得可以验证的结果是否正确。一旦你有了用于检查代码工作的代码和方法,如果你需要更多的速度,开始优化它的一部分,最好在分析正在运行的程序(产生正确的结果)之后,看看哪些部分会限制你的性能。

1

我通过代码做了一些简单的重构,只是为了感受它的作用,发生了什么。

让我在开始时提到一件事:这是Sphaghetti怪兽的复兴,不是吗?你喜欢全局变量吗? Long,superpotent 让我们现在就做,现在这里 methoods?

如果您尽可能晚地引入变量,那么您的代码的读者不需要向上搜索,此变量之前的内容 - 例如,如果重写了某个变量,例如,是否存在重复使用。

如果你的变量不改变:使它们最终。这简化了对他们的推理。final int killRadius = 200;表示获得类型信息和价值,并希望在第一次使用之前不久,并且永远不会改变。仅在源代码中配置。可能不是一个太复杂的候选人。相较于双DX - 未初始化,因为它得到的内环路初始化,

static void foo() { 

    final int xc = 200; 
    final int yc = 200; 
    final int killRadius = 200; 
    final int releaseRadius = 150; 
    final int partRadius = 14; 

    drawCircle (xc, yc, killRadius); // kill 
    drawCircle (xc, yc, releaseRadius); // release 
    drawCircle (xc, yc, partRadius); // center particle 

    //while (true) { 
    boolean killed = false; 
    boolean collide = false; 

    double theta = Math.random() * Math.PI * 2;  
    double xp = xc + releaseRadius * Math.cos (theta); 
    double yp = yc + releaseRadius * Math.sin (theta); 
    double dx, dy, xpp, ypp; 

    while (true) { 

     theta = Math.random() * Math.PI * 2; 
     final int partDiam = 2 * partRadius; 
     final double length = partDiam; 

     xpp = xp; 
     ypp = yp; 

     xp += length * Math.cos (theta); 
     yp += length * Math.sin (theta); 

     dx = xp - xc; 
     dy = yp - yc; 
     if ((dx * dx) + (dy * dy) > killRadius * killRadius) { 
      killed = true; 
      break; 
     } 

     // Why again assign dx = xp -xc? Did any of these values change meanwhile? 
     // I don't think so. 
     // dx = xp - xc; 
     // dy = yp - yc; 
     if ((dx * dx) + (dy * dy) < (partDiam) * (partDiam)) { 
      collide = true; 
      break; 
     } 
    } 
    if (collide) { 
     // no absolute value because particles move at most by diameter 
     double depthPenetration = partDiam - Math.sqrt((dx * dx) + (dy * dy)); 
     dx = xpp - xp; 
     dy = ypp - yp; 
     // shorten distance travelled by penetration length to ensure 
     // that 
     // particle is tangeant 
     final double length = Math.sqrt((dx * dx) + (dy * dy)) - depthPenetration; 
     xp = xpp + length * Math.cos (theta); 
     yp = ypp + length * Math.sin (theta); 
     drawCircle ((int) xp, (int) yp, partRadius); 
    } 

如果你组织你这样的代码,你不仅可以看到,像XC一定的价值是200和从未改变 - 在while循环的头部,你会发现theta没有在循环中声明,所以无论是稍后在循环外使用,还是在循环内部依次修改。做一个x + = 4;您无法在循环传球中初始化x。

在大而结束,你有两个类似的块:

dx = xp - xc; 
    dy = yp - yc; 
    if ((dx * dx) + (dy * dy) (OP) a OP b) { 
     c = true; 
     break; 
    } 

但XP,XC和DX不同时改变 - 也不在y当量。这是一个错误,还是为什么你再次分配它们?

然后,你可以通过这种方式摆脱你的无尽:既然这两个条件终止了while,把条件放到while - test中,并且调用第二个block(不重复赋值),只有当第一个wasn'牛逼进入 - 关键字这样做是else

while (!killed && !collide) { 

     // ... 

     dx = xp - xc; 
     dy = yp - yc; 
     if ((dx * dx) + (dy * dy) > killRadius * killRadius) { 
      killed = true; 
     } 
     else if ((dx * dx) + (dy * dy) < (partDiam) * (partDiam)) { 
      collide = true; 
     } 
    } 

是什么在发现错误帮助吗?没那么多。如果两个圈子被错误地放置,其余的都很好,屏幕截图将会很好,并且值会导致不良的圈子,并且这些值是正确的。