Triangulating a point set then walking over edges is a solution that avoids the problem with "circular-like" paths that polygon-based walks have.
You probably don't want a random point set that has points too close together because it may lead to a path looks like it's intersecting itself at certain points. For this reason, using something like a poisson disk sampling method to generate the point set is advisable — the triangulation of such a point set will provide a random path with segments of more-or-less equal length, segment-segment angles of roughly 60° and no apparent points of intersection.
Algorithm
With a Delaunay triangulation library at hand, the algorithm looks like this:
To initialise,
- Generate point set
- Triangulate point set
- Choose a random vertex from the triangulation (the
lastVisitedVertex
)
Then loop:
- Select a random edge connected to the last visited vertex, only if the other vertex connected to the edge has not yet been visited (this edge is the next segment)
- Mark the vertex we just came from as visited
lastVisitedVertex
= selected edge's other vertex
- Loop again, or exit if the path length exceeds our desired length (i.e. > 25)
Illustrations
Random paths on a triangulated poisson disc point set
[
Random paths on a triangulated random point set
Code
Here's an example in Java using the Tinfour library for triangulation. When using a library for triangulation, I advise you to pick one that provides easy access to connected edges for a given vertex, and the vertices making up a given edge.
In this example I pick a random edge (rather than vertex) as the starting point.
ArrayList<Vertex> randomPath(List<Vertex> randomPoints, int pathLength) {
IncrementalTin tin = new IncrementalTin(10);
tin.add(randomPoints, null); // insert point set; points are triangulated upon insertion
HashSet<Vertex> visitedVertices = new HashSet<>();
ArrayList<Vertex> pathVertices = new ArrayList<>();
IQuadEdge lastEdge = tin.getStartingEdge(); // get random edge
int n = 0;
while (n <= pathLength) {
List<IQuadEdge> list = new ArrayList<>();
lastEdge.pinwheel().forEach(list::add); // get edges connected to A; add to array
IQuadEdge nextEdge;
int attempts = 0;
do {
nextEdge = list.get(random.nextInt(list.size())); // randomly select connected edge
if (attempts++ > list.size() * 4) {
return pathVertices; // path ended prematurely (no possible edges to take)
}
} while (visitedVertices.contains(nextEdge.getB()) || nextEdge.getB() == null);
lastEdge = nextEdge.getDual(); // A and B flip around, so A is next vertex
pathVertices.add(lastEdge.getB()); // add the point we just came from to path
visitedVertices.add(lastEdge.getB()); // add the point we just came from to visited vertices
n++;
}
return pathVertices;
}