我有一个简单的六边形网格,我在那里选择一组六边形,然后用一些随机点填充。
让我解释一下产生积分的确切程序:
现在我尝试了这两种方法来确定一个点是否位于特定形状内。
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;
});
},
结果:
答案 0 :(得分:2)
在一个六边形中进行采样。只需制作带有大小(2s,2s cos(30)),样本(x,y)的边界框,如果它在六边形之外则拒绝它。效率应为3/4(检查hexagon area)。将此采样六边形定位在(0,0)处,这样采样非常简单,而且,更重要的是,非常可靠
对于每个区域,保持六边形中心阵列,例如,大小为N
分两步进行示例。首先,采样整数1 ... N并选择什么 该区域的六边形即将被选中。第二,来自你的样品 来自步骤#1的(x,y),来自(0,0)的单个六边形。持续, 通过随机选择的六边形中心移位采样(x,y)
更新
实际上,我相信有一种方法可以在一个六边形中采样点(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,支持他的回答。