hexarea / shape

时间:2016-08-31 08:55:48

标签: javascript random shape point hexagonal-tiles

我有一个简单的六边形网格,我在那里选择一组六边形,然后用一些随机点填充。

让我解释一下产生积分的确切程序:

  • 使用十六进制坐标列表我正在选择六边形。
  • 将六边形分组到区域。
  • 分别为每个区域存储所有边缘点。
  • 循环通过区域:
    • 计算区域边界(定义随机点生成器的范围所需)
    • 绘制区域(边界)正方形。 (看计算是否正确)
    • 根据边界平方min,最大值生成随机点
    • 测试点是否在区域形状内。 (形状由区域边缘点定义)
    • 如果该点通过了上述测试,则​​将其推入数组。
  • 循环遍历点阵并在屏幕上绘制。

现在我尝试了这两种方法来确定一个点是否位于特定形状内。

cn_PnPoly: function( P, V, n ){ ///// Point , array of vertices , array size ////
        var cn = 0, vt = 0.0;
        for (var i=0; i< (n-1); i++) {    // edge from V[i]  to V[i+1]
           if (((V[i].y <= P.y) && (V[i+1].y > P.y))     // an upward crossing
            || ((V[i].y > P.y) && (V[i+1].y <=  P.y))) { // a downward crossing
                // compute  the actual edge-ray intersect x-coordinate
                vt = (P.y  - V[i].y) / (V[i+1].y - V[i].y);
                if (P.x <  V[i].x + vt * (V[i+1].x - V[i].x)) // P.x < intersect
                     ++cn;   // a valid crossing of y=P.y right of P.x
            }
        }
        return (cn&1);    // 0 if even (out), and 1 if  odd (in)
    },

结果:

enter image description here

isInHexBounary: function( p, points, l ){ ///// Point , array of vertices , array size ////
        var result = false;
          for (i = 0, j = l - 1; i < l; j = i++) {
            if ((points[i].y > p.y) != (points[j].y > p.y) && (p.x < (points[j].x - points[i].x) * (p.y - points[i].y) / (points[j].y-points[i].y) + points[i].x)) {
                result = !result;
             }
          }
          return result;
    },

结果:

enter image description here

我认为第一种方法需要按特定顺序排列所有点,这就是它无法正常工作的原因。 但除了某些部分之外,第二个似乎几乎正确。知道我做错了吗?

更新

事实证明,对于算法来说,需要按特定顺序获得点,所以我计算了每个点相对于平均中心点的角度。然后按角度对所有点进行排序。两种算法现在返回类似的结果。虽然有一些点通过区域形状泄漏。

findCenter: function( points ){
        var x = 0, y = 0, i, len = points.length;
        for (i = 0; i < len; i++) {
            x += points[i].x;
            y += points[i].y;
        }
        return {x: x / len, y: y / len};
    },

    findAngles: function(c, points){
        var i, len = points.length, p, dx, dy;
        for (i = 0; i < len; i++) {
            p = points[i];
            dx = p.x - c.x;
            dy = p.y - c.y;
            p.angle = Math.atan2(dy, dx);
        }
    },

    sortByAngle: function( points ){
        points.sort(function(a, b) {
          if (a.angle > b.angle) return 1;
          else if (a.angle < b.angle) return -1;
          return 0;
        });
    },

结果:

enter image description here

2 个答案:

答案 0 :(得分:2)

坦率地说,我会更简单。

  1. 在一个六边形中进行采样。只需制作带有大小(2s,2s cos(30)),样本(x,y)的边界框,如果它在六边形之外则拒绝它。效率应为3/4(检查hexagon area)。将此采样六边形定位在(0,0)处,这样采样非常简单,而且,更重要的是,非常可靠

  2. 对于每个区域,保持六边形中心阵列,例如,大小为N

  3. 分两步进行示例。首先,采样整数1 ... N并选择什么 该区域的六边形即将被选中。第二,来自你的样品 来自步骤#1的(x,y),来自(0,0)的单个六边形。持续, 通过随机选择的六边形中心移位采样(x,y)

  4. 更新

    enter image description here

    实际上,我相信有一种方法可以在一个六边形中采样点(x,y),效率为100%而没有任何拒绝/接受。看看上面的图片。红色是整个六边形,矩形蓝色区域是您采样点的位置。如果采样点位于红色区域内,则将其取下并继续前进。如果它不在红色和内部蓝色A三角形中,则将其映射为黑色A'三角形。如果点不在红色但在蓝色B三角形中,则将其重新映射为黑色B'三角形

    重新映射是一种非常简单的线性变换。

    最后你有三个随机数作为输入的采样(一个用于选择 目标六边形,两个用于采样随机点),它将保证在所需区域的某处返回随机点

    更新II

    正如Denis Sheremet所指出的,更好的映射是A->B'B->A'。假设六角形中心位于(0,0),总体上只有两个反射 - 一个在中心,另一个在三角形的中间

答案 1 :(得分:0)

我用C ++实现了Severin Pappadeux's awesome answer(但应该可以轻松转换为任何其他语言):

    // randA and randB must be in the range [0, 1]
    static glm::vec2 SampleHexagon(float randA, float randB) {
        // Algorithm based on https://stackoverflow.com/a/39262805/3841944 by user Severin Pappadeux
        //
        // Hexagon map:
        //  ___________________
        // |   /|         |\   |
        // |  / |         | \b'|
        // | / a|         |A \ |
        // |/___|    C    |___\|
        // |\   |         |   /|
        // | \ b|         |B / |
        // |  \ |         | /a'|
        // |   \___________/___|
        // |    |         | <- "startA"
        // |    |         |<-->| <- "widthA"
        // |    | <- "startC"
        // |<----------------->| "width"
        //
        // C = center part
        // a, b = left (upper and lower) part
        // A, B = right (upper and lower) part
        // a' -> area that will be remapped to a
        // b' -> area that will be remapped to b

        // The algorithm is to:
        // 1. Generate random points in the rectangle spanning C, A, B, b' and a'
        // 2. Move all points in a' and b' into a and b
        // 3. (Optional) Make hexagon regular
        // 4. (Optional) Remap coordinates to range [-1, 1]

        // Coordinates are in the range [0, 1]
        const float width  = 1;
        const float widthC = width / 2;
        const float widthA = (width - widthC) / 2;
        const float startC = widthA;
        const float startA = startC + widthC;
        const float slope  = .5f / widthA;


        // 1. Remap x into the rectangle spanning C, A, B, b' and a'
        float x = startC + randA * .75f;
        float y = randB;


        // 2. Move all points in a' and b' into a and b

        // If we are in the last third (A, b', B and a')
        if (startA < x) {
            float localX = x - startA;

            if (y > .5f) {  // And the upper half of it (in A or b')
                float localY = y - .5f;
                if (localY > .5f - localX * slope) { // And above the diagonal
                    // Move the point so it is in the triangle to 'b'
                    x -= startA;
                    y -= .5f;
                }
            } else { // And the lower half of it (in B or a')
                float localY = y;
                if (localY < localX * slope) {  // And we are below the diagonal
                    // Move the point so it is in the triangle to 'a'
                    x -= startA;
                    y += .5f;
                }
            }
        }

        // 3. Make to regular hexagon (currently the hexagon is too high, because we assumed width == height)

        // Divide the hexagon into 6 regular triangles and use the Pythagorean theorem, and this calculation should be fairly obvious
        float regularHexagonWidthToHeightRatio = std::sqrt(1.f*1.f - .5f*.5f);
        y = (y - .5f) * regularHexagonWidthToHeightRatio + .5f; // Center around 0, scale, center to .5

        // 4. Remap coordinates to -1 to 1 (optional)
        x = x * 2 - 1;
        y = y * 2 - 1;

        return { x, y };
    }

再次感谢Severin Pappadeux,支持他的回答。