【问题标题】:Distributing shapes from a centre point从中心点分布形状
【发布时间】:2015-05-19 17:24:12
【问题描述】:

我正在尝试改进我的方法,即从中心点在网格表面上以相等的足迹布置数千个形状的位置(它具有更有机的外观)。网格表面意味着邻居之间的间距相等,但我不想通过选择随机的 x 和 y 坐标(或在这种情况下为 x 和 z,因为我在 3 维中工作)来创建类似拼凑的图案)。我现在拥有的东西可以工作,但是当我从 2,000 个对象开始向上移动时,速度非常慢。有没有更快的方法?就像我说的那样,我正在避免拼凑效果,以及均匀的圆形传播。现在的分布非常像城市和完美,但太慢了。

我一直在评论这个函数,希望它能彻底解释一切:

ArrayList buildingCoords; // stores the co-ordinates of occupied spaces
int w = 50 // Footprint dimensions. Width and depth.

PVector generateNewBuildingPosition(PVector coord) {
    float sW = 30; // gap between shapes
    // Starting at a coordinate of 0,0 if this is our 1st go
    // (PVector coord initially feeds 0,0)
    // or the last coordinate that was already taken
    // (this loops with the last coordinate if it fails)
    // we check one of the four spaces next to us with
    // the roll of a dice (in a way...)
    float randomX = random(0,15);
    float randomZ = random(0,15);
    if (randomX >= 10) { 
      randomX = w + sW; 
    } else if (randomX >= 5) { 
      randomX = (w + sW) * -1;
    } else {
      randomX = 0;
    }
      if (randomZ >= 10) { 
      randomZ = w + sW; 
    } else if (randomX >= 5) { 
      randomZ = (w + sW) * -1;
    } else {
      randomZ = 0;
    }

    // We've picked our direction.
    // We have a PVector that acts as a marker for where we're
    // placing. Adding or subtracting the movement of each
    // attempt, one at a time, means the shapes spreads out 
    // more organically from the centre rather than trying
    // to distribute each shape as a random patch.
    PVector newDirection = new PVector(randomX, 0, randomZ);
    coord.add(newDirection);
    // Our marker moves to the new spot, we check if it exists.
    // If it doesn't, we confirm it as this shape's anchor spot.
    // If it does, we loop this function again, feeding it where our
    // marker is.
    if(buildingCoords.contains(coord)) {
      generateNewBuildingPosition(coord);
    } else {
      // add this PVector to the arrayList
      buildingCoords.add(coord);
    }
    // Return the coordinates that just succeeded.
    return coord;
  }

【问题讨论】:

  • 抱歉,我错过了有一个外部变量。我现在已经将它包含在原始文件中,但它是每个形状的足迹的宽度和深度。
  • @Micheal Hobbs:这只是检查 ArrayList 是否包含某个对象的默认代码。列表越大,速度越慢。
  • 有不同的方法可以做到这一点。如果您希望您的结构从中心向外扩展,您可以使用边界形状(边界框最简单),但实际上它归结为保留 non-taken 空间的列表,每次占用空间时都会更新。正如我所提到的,为了防止该列表在内存中爆炸,您可以动态增长它,这意味着您从一些空白空间开始,并在您占用空间时添加新空间(例如某个半径内的邻居)。
  • 这里还有一个可能会有所帮助的想法:一种类似生活游戏的设置,根据网格位置的邻居数量来选择位置。零邻居不好,邻居太多也不好,我们总是试图在中间挑选一些东西。您可以跟踪至少有一个邻居的每个空位置(为那些有 1 个邻居、有 2 个邻居等的人保留一个单独的列表可能会更好),当您放置一个新元素时更新这个列表。跨度>
  • 通过调整与n 邻居选择某物的概率权重,您可以调整生成的形状:如果您的“租户”非常隐居并且更喜欢一个邻居而不是其他所有事物,您将得到一个类似蕨类植物的结构,如果他们更喜欢更多的邻居,你更有可能得到一个斑点。您甚至可以在生成期间调整权重(当村庄较小时,房屋往往会挤在一起,并开始分散到一定大小以上)。

标签: java random processing


【解决方案1】:

这段代码基本上可以立即处理 200,000 个。我不得不猜测一些细节,但它应该与您要查找的内容接近。

public class Test {

    static Map<Integer, Vector3> buildingCoords;
    public static void main(String[] args) {
        buildingCoords = new HashMap();

        Vector3 start = new Vector3(0,0,0);

        for (int i = 0; i < 200000; i++)
            start = generateNewBuildingPosition(start);

        System.out.print("Done");

    }

    static Vector3 generateNewBuildingPosition(Vector3 coord) {
        int w = 50; // Footprint dimensions. Width and depth.
        float sW = 30; // gap between shapes
        // Starting at a coordinate of 0,0 if this is our 1st go
        // (PVector coord initially feeds 0,0)
        // or the last coordinate that was already taken
        // (this loops with the last coordinate if it fails)
        // we check one of the four spaces next to us with
        // the roll of a dice (in a way...)
        float randomX = (float)random() * 15;
        float randomZ = (float)random() * 15;
        if (randomX >= 10) randomX = w + sW;
        else if (randomX >= 5) randomX = (w + sW) * -1;
        else randomX = 0;

        if (randomZ >= 10) randomZ = w + sW;
        else if (randomX >= 5) randomZ = (w + sW) * -1;
        else randomZ = 0;


        // We've picked our direction.
        // We have a PVector that acts as a marker for where we're
        // placing. Adding or subtracting the movement of each
        // attempt, one at a time, means the shapes spreads out
        // more organically from the centre rather than trying
        // to distribute each shape as a random patch.
        Vector3 newDirection = new Vector3(randomX, 0, randomZ);
        coord.add(newDirection);
        // Our marker moves to the new spot, we check if it exists.
        // If it doesn't, we confirm it as this shape's anchor spot.
        // If it does, we loop this function again, feeding it where our
        // marker is.
        if(buildingCoords.containsKey(coord.hashCode())) {
            generateNewBuildingPosition(coord);
        } else {
            // add this PVector to the arrayList
            buildingCoords.put(coord.hashCode(), coord);
        }
        // Return the coordinates that just succeeded.
        return coord;
    }
}

class Vector3 {

    float x, y, z; // package-private variables; nice encapsulation if you place this in a maths package of something

    Vector3(float x, float y, float z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public Vector3 add(Vector3 vector) {
        x += vector.x;
        y += vector.y;
        z += vector.z;
        return this; // method chaining would be very useful
    }

    @Override
    public int hashCode(){
        return Float.hashCode(x + y + z);
    }

}

编辑:如图所示的 hashCode 不是很健全,可能会导致问题。你应该阅读:Hashing 2D, 3D and nD vectors

【讨论】:

  • 谢谢你,迈克尔,看起来棒极了。请与我裸露,我在这方面不是特别有经验,需要适应您所写的内容。特别是“地图”,我还没有。
  • Np,如果您需要帮助,请告诉我。我会再待 30 分钟左右。在旁注中,我担心我提供的散列函数。吃完后,我会对其进行一些测试。
  • 我猜你之前对java框架'Processing'不太熟悉(从你关于'contains'的问题来看)?它使诸如“地图”之类的事情变得棘手,因为它不是框架的一部分。可以对 java 进行较低级别的调用,但这些调用有点超出我的理解。
  • 你的原始代码没有列出ArrayList buildingCoords;我的想法是,当您在 3d 空间中工作时,“包含”是某种碰撞检测。不,我直到现在才知道它的存在。
  • TLDR:ArrayList.contains 太慢了,O(n)。换句话说,当您添加 1000 个建筑物时,它必须检查 1000 个项目,然后检查 1001 个项目,因为 HashMap 只需要检查一个项目。对于您正在做的事情,您正在查看 1/2n(n+1) list.contain 调用,其中 n 是您想要的建筑物数量。如果您尝试了 n=200,000 并且每 10000 次调用需要 1 秒,那么您大约需要两年时间才能完成运行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-09-19
  • 1970-01-01
  • 2010-10-16
  • 1970-01-01
  • 1970-01-01
  • 2017-06-22
  • 1970-01-01
相关资源
最近更新 更多