I have a simple hexagonal grid, where I'm selecting a group of hexagons, which will then be filled with some random points.
Let me explain the exact procedure of generating the points:
Now I tried these two methods to determine whether a point lies inside a specific 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)
},
result:

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

I suppose the first method requires to have all the points in specific order, and that's why it's not working properly. But the second one seems to be working almost correct , apart from some parts. Any idea what I'm doing wrong?
Update:
It's turning out that for mos tof the algorhytms it's required to have the points in a specific order, so I calculated angle of each point relative to the average center point. And then sorted all the points by angle. Both algorithms now return similar results. Although there is sitll some points leaking through the area shape.
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;
});
},
result:

Frankly, I would make it simpler.
Make sampling in one single hexagon. Just make bounding box with size (2s,2s cos(30)), sample (x,y) and reject it if it is outside the hexagon. Efficiency should be 3/4 (check hexagon area). Position this sampling hexagon at (0,0) such that sampling is very easy, and, what's more important, very TESTABLE
For each area maintain array of hexagon centers, say, with size N
Sample in two steps. First, sample integer 1...N and select what hexagon in the area is about to be selected. Second, sample from your (x,y) from step #1, from your single hexagon at (0,0). Last, shift sampled (x,y) by randomly selected hexagon center
UPDATE

Actually, I believe there is a way to sample point (x,y) in a single hexagon with 100% efficiency without any rejection/acceptance. Look at picture above. Red is whole hexagon, and rectangular blue area is where you sample points. If sampled point is within red area you take it and move on. If it is not in the red and inside blue A triangle, you map it into black A' triangle. If point is not in the red but in the blue B triangle, you remap it into black B' triangle
Remapping is quite simple linear transformation.
Finally you have sampling with three random numbers as input (one used to select destination hexagon, and two used to sample random point) and it will guaranteed to return random point somewhere in the desired area
UPDATE II
As noted by Denis Sheremet, better mapping would be A->B' and B->A'. Assuming hexagon center at (0,0), overall just two reflections - one over center and another over middle of the triangle
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With