/** Returns randomly selected points on the specified nodes with each point being separated by \a clearanceRadius from each other.
* Selecting points ON the nodes only works for TriangleMeshNode (used by Recast Graph and Navmesh Graph) and GridNode (used by GridGraph).
* For other node types, only the positions of the nodes will be used.
*
* clearanceRadius will be reduced if no valid points can be found.
*/
public static List<Vector3> GetPointsOnNodes (List<GraphNode> nodes, int count, float clearanceRadius = 0) {
if (nodes == null) throw new ArgumentNullException ("nodes");
if (nodes.Count == 0) throw new ArgumentException ("no nodes passed");
var rnd = new System.Random();
var pts = ListPool<Vector3>.Claim(count);
// Square
clearanceRadius *= clearanceRadius;
if (nodes[0] is TriangleMeshNode || nodes[0] is GridNode) {
//Assume all nodes are triangle nodes or grid nodes
var accs = ListPool<float>.Claim(nodes.Count);
float tot = 0;
for (var i=0;i<nodes.Count;i++) {
var tnode = nodes[i] as TriangleMeshNode;
if (tnode != null) {
float a = Math.Abs(Polygon.TriangleArea(tnode.GetVertex(0), tnode.GetVertex(1), tnode.GetVertex(2)));
tot += a;
accs.Add (tot);
}
else {
var gnode = nodes[i] as GridNode;
if (gnode != null) {
var gg = GridNode.GetGridGraph (gnode.GraphIndex);
var a = gg.nodeSize*gg.nodeSize;
tot += a;
accs.Add (tot);
} else {
accs.Add(tot);
}
}
}
for (var i=0;i<count;i++) {
//Pick point
var testCount = 0;
var testLimit = 10;
var worked = false;
while (!worked) {
worked = true;
//If no valid points can be found, progressively lower the clearance radius until such a point is found
if (testCount >= testLimit) {
clearanceRadius *= 0.8f;
testLimit += 10;
if (testLimit > 100) clearanceRadius = 0;
}
var tg = (float)rnd.NextDouble()*tot;
var v = accs.BinarySearch(tg);
if (v < 0) v = ~v;
if (v >= nodes.Count) {
// This shouldn't happen, due to NextDouble being smaller than 1... but I don't trust floating point arithmetic.
worked = false;
continue;
}
var node = nodes[v] as TriangleMeshNode;
Vector3 p;
if (node != null) {
// Find a random point inside the triangle
float v1;
float v2;
do {
v1 = (float)rnd.NextDouble();
v2 = (float)rnd.NextDouble();
} while (v1+v2 > 1);
p = ((Vector3)(node.GetVertex(1)-node.GetVertex(0)))*v1 + ((Vector3)(node.GetVertex(2)-node.GetVertex(0)))*v2 + (Vector3)node.GetVertex(0);
} else {
var gnode = nodes[v] as GridNode;
if (gnode != null) {
var gg = GridNode.GetGridGraph (gnode.GraphIndex);
var v1 = (float)rnd.NextDouble();
var v2 = (float)rnd.NextDouble();
p = (Vector3)gnode.position + new Vector3(v1 - 0.5f, 0, v2 - 0.5f) * gg.nodeSize;
} else
{
//Point nodes have no area, so we break directly instead
pts.Add ((Vector3)nodes[v].position);
break;
}
}
// Test if it is some distance away from the other points
if (clearanceRadius > 0) {
for (var j=0;j<pts.Count;j++) {
if ((pts[j]-p).sqrMagnitude < clearanceRadius) {
worked = false;
break;
}
}
}
if (worked) {
pts.Add (p);
break;
} else {
testCount++;
}
}
}
ListPool<float>.Release(accs);
} else {
for (var i=0;i<count;i++) {
pts.Add ((Vector3)nodes[rnd.Next (nodes.Count)].position);
}
}
return pts;
}