[英]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;
});
},
結果:
坦率地說,我會讓它更簡單。
在一個單一的六邊形中進行采樣。 只需制作大小為 (2s,2s cos(30))、樣本 (x,y) 的邊界框,如果它在六邊形之外,則拒絕它。 效率應為 3/4(檢查六邊形面積)。 將此采樣六邊形定位在 (0,0) 處,這樣采樣非常容易,而且更重要的是,非常可測試
對於每個區域,保持六邊形中心陣列,例如大小為 N
分兩步取樣。 首先,對整數 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.