3

I was watching this video: Delaunay Triangulation and I want to use this to generate procedural content in the same way. I had a pretty hard time figuring out how to work with the DelaunayTriangulation class supplied by LibGDX but I guess I finally figured it out.

My question however is how do I know which point is connected to which point in a easy way? This is how I currently setup my points for testing, these points eventually need to be supplied by the rooms generated.

private void TriangleTest()
    {
        DelaunayTriangulator triangulator = new DelaunayTriangulator();

        points = new float[8];
        points[0] = 80;
        points[1] = 30;
        points[2] = 40;
        points[3] = 45;
        points[4] = 0;
        points[5] = 10;
        points[6] = -100;
        points[7] = 100;

        indices = triangulator.computeTriangles(points, false);

        System.out.println(indices);
        //Output [1, 0, 2, 1, 2, 3]
    }

And this is how I am drawing the points and lines/triangles, just for visualization and understanding.

private void DrawTriangles()
    {
        //Draw points
        for (int i = 0; i < points.length / 2; i++)
        {
            DebugHelper.DrawDebugPoint(new Vector2(points[i * 2], points[i * 2 + 1]), camera.combined);
        }

        //Draw lines
        for (int i = 0; i < indices.size / 3; i++)
        {
            Vector2 v1 = new Vector2(points[indices.get(i * 3) * 2],
                    points[indices.get(i * 3) * 2 + 1]);
            Vector2 v2 = new Vector2(points[indices.get(i * 3 + 1) * 2],
                    points[indices.get(i * 3 + 1) * 2 + 1]);
            Vector2 v3 = new Vector2(points[indices.get(i * 3 + 2) * 2],
                    points[indices.get(i * 3 + 2) * 2 + 1]);

            DebugHelper.DrawDebugLine(v1, v2, camera.combined);
            DebugHelper.DrawDebugLine(v2, v3, camera.combined);
            DebugHelper.DrawDebugLine(v3, v1, camera.combined);
        }
    }

Assuming I am using this correctly (the points and triangles get drawn correctly) I'm drawing double lines:

[1, 0, 2] //1st indices (2nd point connects to 1st point)
[1, 2, 3] //2nd indices (1st point connects to 2nd point)

So is there someway to filter these out? Since I am only interested in connectivity and not actually drawing triangles side by side to generate meshes.

Also, in the video I mentioned on top you notice there are lines being removed to have some death ends at 50 seconds in the movie. How is this calculated? Some also get colored and it leaves a nice mix of death ends and loops. I am interested to know how this could be achieved.

Madmenyo
  • 8,389
  • 7
  • 52
  • 99

3 Answers3

2

I'm the author of the video linked to in the original question. The source code for the demo in the video can be found here: https://bitbucket.org/NathisGreen/pcgdungeons

This was created as part of my final year at university so I have a detailed report explaining how the entire thing works that can be read here: http://www.nathanmwilliams.com/files/AnInvestigationIntoDungeonGeneration.pdf

Section 2.3.2 and Section 4.1 relate to the Delaunay triangulation being discussed here.

But basically all that is happening to create the final graph, is I find the minimum spanning tree of the graph produced by the triangulation and then randomly add back some of the edges from the original graph into the minimum spanning tree graph.

Nathan
  • 31
  • 1
  • How come the famous Nathan Williams does not have a stackoverflow account? :D – Madmenyo Jun 17 '15 at 16:19
  • Anyway, I could not get that Unity Delaunay dungeon to work. Perhaps it is not compatible with the latest version. But why Unity anyway? I thought that extension was for LibGDX. – Madmenyo Jun 17 '15 at 16:21
  • I just checked and the project still works fine with Unity version 5.1. I am personally not familiar with the DelaunayTriangulator in LibGDX , I wrote my own implementation of the Delaunay triangulation in my Unity project. In my implementation I had a class that represented an Edge on the graph, which made it easy to look up which vertex any given vertex is connected too. Having a look over the documentation for Libgdx, it seems the vertices are returned in triples from the triangulate function. Perhaps you can convert the data returned into something easier to work with? – Nathan Jun 19 '15 at 20:09
  • I will look into it again soon. I did had some interesting reads past days and have some ideas how to approach it. Thanks. – Madmenyo Jun 19 '15 at 21:14
1

It seems like you just want to compute the set of edges of the resulting triangles. Therefore, you can simply create an Edge class, with two special properties:

  • the equals method returns true even if the vertices of the edge to compare with are swapped (so that an edge (2,1) will be considered as "equal" to an edge (1,2))
  • the hashCode method is implemented in a way that is consistent with this equals implementation, as noted in the Object#hashCode documentation:

    If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.

This way, these Edge objects can simply be put into a Set, and the Set will take care of the rest: Even if "duplicate" edges are added, the set will eventually contain each edge only once.
import java.util.LinkedHashSet;
import java.util.Set;

public class DelaunayEdges
{
    public static void main(String[] args)
    {
        int triangleIndices[] = new int[] { 1, 0, 2, 1, 2, 3 };
        Set<Edge> edges = computeEdges(triangleIndices);
        System.out.println("Edges: "+edges);
    }

    static class Edge
    {
        private final int vertex0;
        private final int vertex1;
        public Edge(int vertex0, int vertex1)
        {
            this.vertex0 = vertex0;
            this.vertex1 = vertex1;
        }
        @Override
        public String toString()
        {
            return "("+vertex0+","+vertex1+")";
        }
        @Override
        public int hashCode()
        {
            return vertex0 ^ vertex1;
        }
        @Override
        public boolean equals(Object object)
        {
            if (this == object)
            {
                return true;
            }
            if (object == null)
            {
                return false;
            }
            if (getClass() != object.getClass())
            {
                return false;
            }
            Edge that = (Edge) object;
            return 
                (this.vertex0 == that.vertex0 &&
                 this.vertex1 == that.vertex1) ||
                (this.vertex0 == that.vertex1 &&
                 this.vertex1 == that.vertex0);
        }
    }

    private static Set<Edge> computeEdges(int triangleIndices[])
    {
        Set<Edge> edges = new LinkedHashSet<Edge>();
        for (int i=0; i<triangleIndices.length; i+=3)
        {
            int i0 = triangleIndices[i+0];
            int i1 = triangleIndices[i+1];
            int i2 = triangleIndices[i+2];
            edges.add(new Edge(i0, i1));
            edges.add(new Edge(i1, i2));
            edges.add(new Edge(i2, i0));
        }
        return edges;
    }
}

The above program will print

Edges: [(1,0), (0,2), (2,1), (2,3), (3,1)]

Thus, omitting the edge (1,2), because it is considered to be equal to the edge (2,1).

(Of course, one could convert this set back into a plain int[] array of edge indices, but that's not the point of the question)

Marco13
  • 53,703
  • 9
  • 80
  • 159
  • Let's say I want to calculate a minimal spanning tree from this. I already have a method that calculates the weight (or distance between the verts). Then I'm better off with a vertex class that holds all 3 edges right? This way I can start anywhere and just add the lowest weight to the list. Right now I have to traverse the unsorted Set many times. – Madmenyo Jun 23 '15 at 17:11
  • A vertex class? Or a triangle class? (Because you mentioned 3 edges..). In general, one tries to use data structures that allow the algorithm to run in (sometimes so called) "n-free time" - this means that the individual operations should *not* depend on the number of elements, but be done in constant time. So e.g. when you frequently need the "neighbors" of vertices, you should store them so that you can call something like `List neighbors = vertex.getNeighbors();` (or similarly, `List e = vertex.getEdges()`), **without** searching **all** the vertices in the mesh. – Marco13 Jun 23 '15 at 18:44
0

IMO it's impossible to know when there isn't an explanation or example code. But it's possible to use contour lines for a maze. Here is the explanation how to use it with a minimum spanning tree (https://twitter.com/nathanpie/status/435558377964318720). BTW. the delaunay triangulation is the superset of the minimum spanning tree.

Micromega
  • 12,486
  • 7
  • 35
  • 72
  • That at the very least provides a good read. Still there must be some tricks/math I can do on the indices array I get provided by using `DelaunayTriangulation.computeTriangles`. In the end I just want to know which V2 is connected to which V2's. – Madmenyo Jun 13 '15 at 13:48
  • The triangle vertices are my initial points of course. This eventually just corresponds to a room on my map. I have to connect this room to the rooms this vertice connects too. Unfortunately this produces a lot of doubles like I showed. Also if each and every room is connected to an adjacent room it does not make a nice map so I need to find away to remove connectivity while still keeping the whole connected. – Madmenyo Jun 13 '15 at 14:04
  • I am still not finding an answer. Take the twitter reference, I pretty much need to know what happened one step before since it already has removed plenty of triangles after doing a Delaunay triangulation, which connects/triangulates alls points on a plane without crossing each other. I am however trying to implement a minimum spanning tree, this should yield the opposite of the Delaunay triangulations, where it connects the nodes by there shortest path. Using something from both could yield something useful but I am new to graphs and tree's so I'm not that fast. – Madmenyo Jun 13 '15 at 16:26
  • That is not what I'm trying to say, I meant DT connects all and MST connects shortest until all nodes or vertexes have been visited. I could get the result I want from mixing them which means adding some random DT edges to the MST. – Madmenyo Jun 13 '15 at 17:12