簡體   English   中英

六邊形/形狀中的隨機點

[英]random points in a hexarea / shape

我有一個簡單的六邊形網格,我在其中選擇一組六邊形,然后將填充一些隨機點。

讓我解釋一下生成點的確切過程:

  • 使用六邊形坐標列表我選擇六邊形。
  • 將六邊形分組為區域。
  • 單獨存儲每個區域的所有邊緣點。
  • 循環區域:
    • 計算區域邊界(定義隨機點生成器的范圍需要)
    • 繪制區域(邊界)正方形。 (看計算是否正確)
    • 基於邊界平方最小值,最大值生成隨機點
    • 測試點是否在區域形狀內。 (形狀由區域邊緣點定義)
    • 如果點通過了上述測試,則​​將其推入數組。
  • 遍歷點數組並在屏幕上繪制它們。

現在我嘗試了這兩種方法來確定一個點是否位於特定形狀內。

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

結果:

在此處輸入圖片說明

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

結果:

在此處輸入圖片說明

我想第一種方法需要按特定順序排列所有點,這就是它無法正常工作的原因。 但是除了某些部分外,第二個似乎幾乎正確工作。 知道我做錯了什么嗎?

更新:

事實證明,對於大多數算法來說,需要按特定順序排列點,所以我計算了每個點相對於平均中心點的角度。 然后按角度對所有點進行排序。 這兩種算法現在返回相似的結果。 盡管有一些點通過區域形狀泄漏。

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

結果:

在此處輸入圖片說明

坦率地說,我會讓它更簡單。

  1. 在一個單一的六邊形中進行采樣。 只需制作大小為 (2s,2s cos(30))、樣本 (x,y) 的邊界框,如果它在六邊形之外,則拒絕它。 效率應為 3/4(檢查六邊形面積)。 將此采樣六邊形定位在 (0,0) 處,這樣采樣非常容易,而且更重要的是,非常可測試

  2. 對於每個區域,保持六邊形中心陣列,例如大小為 N

  3. 分兩步取樣。 首先,對整數 1...N 進行采樣,然后選擇該區域中將要選擇的六邊形。 其次,從步驟 #1 的 (x,y) 中采樣,從 (0,0) 處的單個六邊形中采樣。 最后,通過隨機選擇的六邊形中心移位采樣 (x,y)

更新

在此處輸入圖片說明

實際上,我相信有一種方法可以在沒有任何拒絕/接受的情況下以 100% 的效率在單個六邊形中對點 (x,y) 進行采樣。 看上面的圖片。 紅色是整個六邊形,矩形藍色區域是您采樣點的地方。 如果采樣點在紅色區域內,則將其取下並繼續。 如果它不在紅色和藍色A三角形內,則將其映射到黑色A'三角形中。 如果點不在紅色而是在藍色B三角形中,則將其重新映射為黑色B'三角形

重映射是非常簡單的線性變換。

最后,您使用三個隨機數作為輸入進行采樣(一個用於選擇目標六邊形,兩個用於采樣隨機點),它將保證在所需區域的某處返回隨機點

更新二

正如 Denis Sheremet 所指出的,更好的映射是A->B'B->A' 假設六邊形中心在 (0,0),總體上只有兩個反射 - 一個在中心上方,另一個在三角形的中間

我在 C++ 中實現了Severin Pappadeux 的精彩答案(但應該可以輕松轉換為任何其他語言):

    // 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,支持他的回答。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM