Пример #1
0
        internal void OnDelaunayTriangleCheck(Adjacency adjacency, int triangle)
        {
            Bitmap img    = new Bitmap(800, 800);
            Random random = new Random();
            float  scale  = 1000.0f;

            for (int j = 0; j < adjacency.vertices.Count; j++)
            {
                adjacency.vertices[j].x /= scale;
                adjacency.vertices[j].y /= scale;
            }
            adjacency.ToImage(img);

            Graphics g = Graphics.FromImage(img);

            PointF[] points = new PointF[3];
            for (int i = 0; i < 3; i++)
            {
                Vertex v = adjacency.vertices[adjacency.triangles[triangle].vertices[i]];
                points[i] = new PointF(v.x * img.Width, v.y * img.Height);
            }

            g.FillPolygon(new SolidBrush(Color.FromArgb(random.Next(255), random.Next(255), random.Next(255))), points);


            for (int j = 0; j < adjacency.vertices.Count; j++)
            {
                adjacency.vertices[j].x *= scale;
                adjacency.vertices[j].y *= scale;
            }

            QueueDebugImage(img);
        }
Пример #2
0
        public void ToImage(Image img, Adjacency adjacency)
        {
            SolidBrush vertexColor   = new SolidBrush(Color.Green);
            Pen        boundaryColor = new Pen(Color.Blue);
            Pen        cellColor     = new Pen(Color.Gray);

            Graphics g = Graphics.FromImage(img);

            g.Clear(Color.White);
            for (int e = 0; e < edges.Count; e++)
            {
                Vertex v0 = vertices[edges[e].v[0]];
                Vertex v1 = vertices[edges[e].v[1]];
                g.DrawLine(boundaryColor, v0.x * img.Width, v0.y * img.Height, v1.x * img.Width, v1.y * img.Height);

                Vertex cellA   = adjacency.vertices[edges[e].cell[0]];
                Vertex cellB   = adjacency.vertices[edges[e].cell[1]];
                Vertex centerV = new Vertex((v0.x + v1.x) * 0.5f, (v0.y + v1.y) * 0.5f);
                g.DrawLine(cellColor, centerV.x * img.Width, centerV.y * img.Height, cellA.x * img.Width, cellA.y * img.Height);
                g.DrawLine(cellColor, centerV.x * img.Width, centerV.y * img.Height, cellB.x * img.Width, cellB.y * img.Height);
            }
            for (int v = 0; v < vertices.Count; v++)
            {
                const float r = 3;
                g.FillEllipse(vertexColor, new RectangleF(vertices[v].x * img.Width - r, vertices[v].y * img.Height - r, 2 * r, 2 * r));
            }
        }
Пример #3
0
        public static Voronoi FromDelaunay(Adjacency adjacency)
        {
            Voronoi v = new Voronoi();

            int[] triangleRemapping = new int[adjacency.triangles.Count];

            for (int i = 0; i < adjacency.triangles.Count; i++)
            {
                Adjacency.Triangle t = adjacency.triangles[i];
                if (!t.valid)
                {
                    triangleRemapping[i] = -1;
                    continue;
                }
                Vertex c; float r;
                t.CircumCircle(adjacency.vertices, out c, out r);

                triangleRemapping[i] = v.vertices.Count;
                v.vertices.Add(c);
            }
            for (int i = 0; i < adjacency.triangles.Count; i++)
            {
                Adjacency.Triangle t = adjacency.triangles[i];
                if (!t.valid)
                {
                    continue;
                }
                for (int e = 0; e < 3; e++)
                {
                    int adjT = adjacency.AdjacentTriangle(i, e);
                    if (adjT < 0 || adjacency.triangles[adjT].valid == false)
                    {
                        continue;
                    }
                    if (triangleRemapping[adjT] < 0)
                    {
                        continue;                              // already processed
                    }
                    int voronoiV0 = triangleRemapping[i];
                    int voronoiV1 = triangleRemapping[adjT];
                    Debug.Assert(voronoiV0 >= 0 && voronoiV1 >= 0);

                    // the 2 vertices shared by the adjacent triangles will become
                    // the voronoi cells divided by the edge we're creating
                    int cellA = adjacency.edges[Math.Abs(t.edges[e]) - 1].vertices[0];
                    int cellB = adjacency.edges[Math.Abs(t.edges[e]) - 1].vertices[1];

                    v.edges.Add(new Edge(voronoiV0, voronoiV1, cellA, cellB));
                }
                triangleRemapping[i] = -1;
            }
            return(v);
        }
Пример #4
0
        public static Voronoi FromDelaunay(Adjacency adjacency)
        {
            Voronoi v = new Voronoi();

            int[] triangleRemapping = new int[adjacency.triangles.Count];

            for (int i = 0; i < adjacency.triangles.Count; i++)
            {
                Adjacency.Triangle t = adjacency.triangles[i];
                if (!t.valid)
                {
                    triangleRemapping[i] = -1;
                    continue;
                }
                Vertex c; float r;
                t.CircumCircle( adjacency.vertices, out c, out r);

                triangleRemapping[i] = v.vertices.Count;
                v.vertices.Add(c);
            }
            for (int i = 0; i < adjacency.triangles.Count; i++)
            {
                Adjacency.Triangle t = adjacency.triangles[i];
                if (!t.valid)
                {
                    continue;
                }
                for (int e = 0; e < 3; e++)
                {
                    int adjT = adjacency.AdjacentTriangle(i, e);
                    if (adjT < 0 || adjacency.triangles[adjT].valid == false) continue;
                    if (triangleRemapping[adjT] < 0) continue; // already processed
                    int voronoiV0 = triangleRemapping[i];
                    int voronoiV1 = triangleRemapping[adjT];
                    Debug.Assert(voronoiV0 >= 0 && voronoiV1 >= 0);

                    // the 2 vertices shared by the adjacent triangles will become
                    // the voronoi cells divided by the edge we're creating
                    int cellA = adjacency.edges[Math.Abs(t.edges[e]) - 1].vertices[0];
                    int cellB = adjacency.edges[Math.Abs(t.edges[e]) - 1].vertices[1];

                    v.edges.Add(new Edge(voronoiV0, voronoiV1, cellA, cellB));
                }
                triangleRemapping[i] = -1;

            }
            return v;
        }
Пример #5
0
        internal void OnDelaunayStep(Adjacency adjacency)
        {
            Bitmap bmp   = new Bitmap(800, 800);
            float  scale = 1000.0f;

            for (int j = 0; j < adjacency.vertices.Count; j++)
            {
                adjacency.vertices[j].x /= scale;
                adjacency.vertices[j].y /= scale;
            }
            adjacency.ToImage(bmp);
            for (int j = 0; j < adjacency.vertices.Count; j++)
            {
                adjacency.vertices[j].x *= scale;
                adjacency.vertices[j].y *= scale;
            }
            QueueDebugImage(bmp);
        }
Пример #6
0
        private static void Delaunay2DCreateSuperTriangle(List <Vertex> vertices, Adjacency adjacency,
                                                          out int stIdx1, out int stIdx2, out int stIdx3)
        {
            // Calculate super triangle

            Bounds2D bounds = new Bounds2D();

            for (int i = 0; i < vertices.Count; i++)
            {
                bounds.AddPoint(vertices[i]);
            }

            Vertex center;
            float  radius;

            bounds.BoundingCircunference(out center, out radius);

            Vertex st1 = new Vertex();
            Vertex st2 = new Vertex();
            Vertex st3 = new Vertex();

            const float DEG2RAD = (float)Math.PI / 180.0f;

            st1.x = center.x + 2.0f * radius * (float)Math.Cos(0.0f * DEG2RAD);
            st1.y = center.y + 2.0f * radius * (float)Math.Sin(0.0f * DEG2RAD);
            st2.x = center.x + 2.0f * radius * (float)Math.Cos(120.0f * DEG2RAD);
            st2.y = center.y + 2.0f * radius * (float)Math.Sin(120.0f * DEG2RAD);
            st3.x = center.x + 2.0f * radius * (float)Math.Cos(240.0f * DEG2RAD);
            st3.y = center.y + 2.0f * radius * (float)Math.Sin(240.0f * DEG2RAD);

            adjacency.vertices.Add(st1);
            adjacency.vertices.Add(st2);
            adjacency.vertices.Add(st3);

            stIdx1 = adjacency.vertices.Count - 3;
            stIdx2 = adjacency.vertices.Count - 2;
            stIdx3 = adjacency.vertices.Count - 1;

            adjacency.CreateTriangle(stIdx1, stIdx2, stIdx3);
        }
Пример #7
0
        public Graph(Voronoi voronoi, Adjacency adjacency, List <WangTile.MergeCandidate> candidates)
        {
            vertices        = voronoi.vertices;
            this.candidates = candidates;
            edges           = new List <Edge>();
            float maxCellDist = float.MinValue;

            for (int i = 0; i < voronoi.edges.Count; i++)
            {
                Voronoi.Edge edge  = voronoi.edges[i];
                bool         found = false;
                foreach (Edge e in edges)
                {
                    if (e.v[0] == edge.v[0] && e.v[1] == edge.v[1] ||
                        e.v[0] == edge.v[1] && e.v[1] == edge.v[0])
                    {
                        found = true;
                        break;
                    }
                }
                if (!found)
                {
                    Vertex cellA    = edge.cell[0] < adjacency.vertices.Count ? adjacency.vertices[edge.cell[0]] : null;
                    Vertex cellB    = edge.cell[1] < adjacency.vertices.Count ? adjacency.vertices[edge.cell[1]] : null;
                    float  cellDist = (cellA != null & cellB != null) ? (cellA - cellB).Length() : float.MaxValue;
                    if (cellDist < float.MaxValue)
                    {
                        maxCellDist = Math.Max(maxCellDist, cellDist);
                    }
                    Graph.Edge gEdge = new Graph.Edge(edge.v[0], edge.v[1], cellDist);

                    edges.Add(gEdge);
                }
            }
            for (int i = 0; i < edges.Count; i++)
            {
                float dist = edges[i].cost;
                edges[i].cost = dist < float.MaxValue ? (float)Math.Pow(1.0f - dist / maxCellDist, 100.0f) : INFINITY_COST; // the exponent value is suggested in the paper
            }
        }
Пример #8
0
        private static bool Delaunay2DInsertPoints(List <Vertex> vertices, Adjacency adjacency)
        {
            // Insert points

            HashSet <int> toCheck = new HashSet <int>();

            for (int i = 0; i < vertices.Count; i++)
            {
                // Insert Vi
                Vertex Vi = vertices[i];

                bool skip = false;
                for (int j = 0; j < adjacency.vertices.Count; j++)
                {
                    if ((Vi - adjacency.vertices[j]).Length() <= COINCIDENT_POINTS_DISTANCE_EPSILON)
                    {
                        // the point has already been inserted. Skip it
                        skip = true;
                        break;
                    }
                }
                if (skip)
                {
                    continue;
                }

                if (OnDelaunayInsertPoint != null)
                {
                    OnDelaunayInsertPoint(adjacency, Vi);
                }

                int tri = adjacency.PointInTriangle(Vi);

                if (tri < 0)
                {
                    Debug.Assert(false);
                    return(false);
                }

                // check whether the point lies exactly on one edge of the triangle
                int edgeIdx = adjacency.PointInTriangleEdge(Vi, tri);
                if (edgeIdx >= 0)
                {
                    // split the edge by Vi
                    int[] result;
                    adjacency.SplitEdge(edgeIdx, Vi, out result);
                    for (int j = 0; j < 4; j++)
                    {
                        if (result[j] >= 0)
                        {
                            toCheck.Add(result[j]);
                        }
                    }
                }
                else
                {
                    // split the triangle in 3
                    int[] result;
                    adjacency.SplitTriangle(tri, Vi, out result);
                    for (int j = 0; j < 3; j++)
                    {
                        toCheck.Add(result[j]);
                    }
                }

                while (toCheck.Count > 0)
                {
                    int t = toCheck.Last <int>();
                    toCheck.Remove(t);

                    Adjacency.Triangle triangle = adjacency.triangles[t];
                    if (!triangle.valid)
                    {
                        continue;
                    }

                    if (OnDelaunayTriangleCheck != null)
                    {
                        OnDelaunayTriangleCheck(adjacency, t);
                    }

                    // check Delaunay condition
                    for (int e = 0; e < 3; e++)
                    {
                        if (!adjacency.triangles[t].valid)
                        {
                            continue;
                        }

                        int adjacentIdx = adjacency.AdjacentTriangle(t, e);
                        if (adjacentIdx < 0)
                        {
                            continue;
                        }
                        int globalEdgeIndex         = Math.Abs(triangle.edges[e]) - 1;
                        Adjacency.Triangle adjacent = adjacency.triangles[adjacentIdx];
                        if (!adjacent.valid)
                        {
                            continue;
                        }
                        Debug.Assert(adjacent.valid);
                        int edgeFromAdjacent = adjacent.LocalEdgeIndex(globalEdgeIndex);
                        Debug.Assert(edgeFromAdjacent >= 0);
                        int v = adjacency.VertexOutOfTriEdge(adjacentIdx, edgeFromAdjacent);
                        Debug.Assert(v >= 0);
                        Debug.Assert(!triangle.Contains(v));

                        if (triangle.InsideCircumcircle(adjacency.vertices[v], adjacency.vertices) > INSIDE_CIRCUMCIRCLE_EPSILON)
                        {
                            int[] result;
                            if (adjacency.FlipTriangles(t, adjacentIdx, out result))
                            {
                                toCheck.Add(result[0]);
                                toCheck.Add(result[1]);
                                //break;
                            }
                        }
                    }
                }

                if (OnDelaunayStep != null)
                {
                    OnDelaunayStep(adjacency);
                }
            }
            return(true);
        }
Пример #9
0
        public static bool Delaunay2DTriangulate(List <Vertex> vertices, bool removeSurroundingTriangle, out List <int> outTriangles, ref Adjacency adjacencyInfo)
        {
            /* Implements Lawson algorithm:
             * http://www.henrikzimmer.com/VoronoiDelaunay.pdf
             *
             * Unlike the Watson algorithm the Lawson algorithm is not based finding circumcircles and deleting
             * triangles to obtain an insertion polygon. Lawson's incremental insertion algorithm utilizes edge
             * flippings to achieve the same result and thus avoids a possibly faulty, degenerate mesh which can
             * occur using Watsons method.
             * In the same way as before, if we are not given a start triangulation we use a super triangle
             * enclosing all vertices in V . In every insertion step a vertex p belonging to V is inserted.
             * A simple retriangulation is made where the edges are inserted between p and the corner vertices of
             * the triangle containing p (fig.11). For all new triangles formed the circumcircles are checked, if they contain
             * any neighbouring vertex the edge between them is flipped. This process is executed recursively
             * until there are no more faulty triangles and no more flips are required (fig.12). The algorithm then
             * moves on, inserting another vertex from V until they have all been triangulated into a mesh, then,
             * like before the super triangle and its edge are removed.
             */

            if (vertices.Count < 3)
            {
                outTriangles = new List <int>();
                return(false);
            }

            Adjacency adjacency = adjacencyInfo;

            if (adjacencyInfo == null)
            {
                adjacency = new Adjacency();
            }
            ;

            int stIdx1, stIdx2, stIdx3;

            Delaunay2DCreateSuperTriangle(vertices, adjacency, out stIdx1, out stIdx2, out stIdx3);

            if (!Delaunay2DInsertPoints(vertices, adjacency))
            {
                outTriangles = new List <int>();
                return(false);
            }

            if (removeSurroundingTriangle)
            {
                Delaunay2DRemoveSuperTriangle(adjacency, stIdx1, stIdx2, stIdx3, out outTriangles, vertices);
            }
            else
            {
                // move the 3 vertices from the supertriangle right to the end of
                // the array instead of the beginning, so that the N first vertices
                // match with the point insertion order
                foreach (Adjacency.Edge e in adjacency.edges)
                {
                    for (int i = 0; i < 2; i++)
                    {
                        e.vertices[i] -= 3;
                        if (e.vertices[i] < 0)
                        {
                            e.vertices[i] += adjacency.vertices.Count;
                        }
                    }
                }

                foreach (Adjacency.Triangle t in adjacency.triangles)
                {
                    for (int i = 0; i < 3; i++)
                    {
                        t.vertices[i] -= 3;
                        if (t.vertices[i] < 0)
                        {
                            t.vertices[i] += adjacency.vertices.Count;
                        }
                    }
                }

                adjacency.vertices.Add(adjacency.vertices[0]);
                adjacency.vertices.Add(adjacency.vertices[1]);
                adjacency.vertices.Add(adjacency.vertices[2]);
                adjacency.vertices.RemoveAt(0);
                adjacency.vertices.RemoveAt(0);
                adjacency.vertices.RemoveAt(0);

                //
                outTriangles = new List <int>();
                for (int i = 0; i < adjacency.triangles.Count; i++)
                {
                    if (adjacency.triangles[i].valid)
                    {
                        outTriangles.Add(adjacency.triangles[i].vertices[0]);
                        outTriangles.Add(adjacency.triangles[i].vertices[1]);
                        outTriangles.Add(adjacency.triangles[i].vertices[2]);
                    }
                }
            }

            return(true);
        }
Пример #10
0
        private static void Delaunay2DCreateSuperTriangle(List<Vertex> vertices, Adjacency adjacency,
                                            out int stIdx1, out int stIdx2, out int stIdx3)
        {
            // Calculate super triangle

            Bounds2D bounds = new Bounds2D();
            for (int i = 0; i < vertices.Count; i++)
            {
                bounds.AddPoint(vertices[i]);
            }

            Vertex center;
            float radius;
            bounds.BoundingCircunference(out center, out radius);

            Vertex st1 = new Vertex();
            Vertex st2 = new Vertex();
            Vertex st3 = new Vertex();

            const float DEG2RAD = (float)Math.PI / 180.0f;

            st1.x = center.x + 2.0f * radius * (float)Math.Cos(0.0f * DEG2RAD);
            st1.y = center.y + 2.0f * radius * (float)Math.Sin(0.0f * DEG2RAD);
            st2.x = center.x + 2.0f * radius * (float)Math.Cos(120.0f * DEG2RAD);
            st2.y = center.y + 2.0f * radius * (float)Math.Sin(120.0f * DEG2RAD);
            st3.x = center.x + 2.0f * radius * (float)Math.Cos(240.0f * DEG2RAD);
            st3.y = center.y + 2.0f * radius * (float)Math.Sin(240.0f * DEG2RAD);

            adjacency.vertices.Add(st1);
            adjacency.vertices.Add(st2);
            adjacency.vertices.Add(st3);

            stIdx1 = adjacency.vertices.Count - 3;
            stIdx2 = adjacency.vertices.Count - 2;
            stIdx3 = adjacency.vertices.Count - 1;

            adjacency.CreateTriangle(stIdx1, stIdx2, stIdx3);
        }
Пример #11
0
        public void ToImage(Image img, Adjacency adjacency)
        {
            SolidBrush vertexColor = new SolidBrush(Color.Green);
            Pen boundaryColor = new Pen(Color.Blue);
            Pen cellColor = new Pen(Color.Gray);

            Graphics g = Graphics.FromImage(img);
            g.Clear(Color.White);
            for (int e = 0; e < edges.Count; e++)
            {
                Vertex v0 = vertices[ edges[e].v[0] ];
                Vertex v1 = vertices[ edges[e].v[1] ];
                g.DrawLine(boundaryColor, v0.x * img.Width, v0.y * img.Height, v1.x * img.Width, v1.y * img.Height);

                Vertex cellA = adjacency.vertices[edges[e].cell[0]];
                Vertex cellB = adjacency.vertices[edges[e].cell[1]];
                Vertex centerV = new Vertex((v0.x + v1.x) * 0.5f, (v0.y + v1.y) * 0.5f);
                g.DrawLine(cellColor, centerV.x * img.Width, centerV.y * img.Height, cellA.x * img.Width, cellA.y * img.Height);
                g.DrawLine(cellColor, centerV.x * img.Width, centerV.y * img.Height, cellB.x * img.Width, cellB.y * img.Height);
            }
            for (int v = 0; v < vertices.Count; v++)
            {
                const float r = 3;
                g.FillEllipse(vertexColor, new RectangleF(vertices[v].x * img.Width - r, vertices[v].y * img.Height - r, 2 * r, 2 * r));
            }
        }
Пример #12
0
        private static void MergeTiles(List <PoissonSample> source, List <PoissonSample> toMerge,
                                       neighbour_e side,
                                       out List <PoissonSample> result)
        {
            List <MergeCandidate> candidates = new List <MergeCandidate>();

            foreach (PoissonSample s in source)
            {
                candidates.Add(new MergeCandidate(s, 0));
            }
            foreach (PoissonSample s in toMerge)
            {
                candidates.Add(new MergeCandidate(s, 1));
            }

            const float scale = 1000.0f; // apply a temporary scaling to the Poisson points (in 0-1 range)
            // before Delaunay triangulation (we'll undo this later) to avoid
            // requiring very small epsilons for point snapping etc which
            // may lead to numeric issues.

            List <Vertex> vertices = new List <Vertex>();

            for (int i = 0; i < candidates.Count; i++)
            {
                vertices.Add(new Vertex(candidates[i].sample.x * scale, candidates[i].sample.y * scale));
            }

            List <int> outTriangles;
            Adjacency  adjacency = new Adjacency();

            Delaunay2D.Delaunay2DTriangulate(vertices, false, out outTriangles, ref adjacency);

            for (int j = 0; j < adjacency.vertices.Count; j++)
            {
                adjacency.vertices[j].x /= scale;
                adjacency.vertices[j].y /= scale;
            }

            Image debugImage = null;

            if (OnDebugStep != null)
            {
                debugImage = new Bitmap(800, 800);
            }


            Voronoi v = Voronoi.FromDelaunay(adjacency);

#if DEBUG
            if (debugImage != null)
            {
                v.ToImage(debugImage, adjacency);
                OnDebugStep(debugImage, "Voronoi diagram generated from Delaunay Triangulation");
            }
#endif
            Graph g = new Graph(v, adjacency, candidates);
            if (debugImage != null)
            {
                g.ToImage(debugImage);
                OnDebugStep(debugImage, "Merging " + NeighbourName(side) + " tile: Convert voronoi diagram into a graph");
            }

            int v0, v1;
            for (int i = 0; i < 4; i++)
            {
                if (i != (int)side)
                {
                    g.Clip((WangTile.neighbour_e)i, out v0, out v1);
                }
            }
            g.Clip(side, out v0, out v1);
#if DEBUG
            if (debugImage != null)
            {
                g.ToImage(debugImage);
                OnDebugStep(debugImage, "Graph generated from Voronoi Diagram");
            }
#endif
            List <int> path;
            g.ShortestPath(v0, v1, out path);
            List <Vertex> shape;
            g.GenerateShape(path, out shape);

            for (int i = 0; i < shape.Count - 1; i++) // skip last one as it is the same as the first element (they're references)
            {
                shape[i].x *= scale;
                shape[i].y *= scale;
            }

            if (debugImage != null)
            {
                Graphics grph = Graphics.FromImage(debugImage);
                Pen      sp   = new Pen(Color.Orange, 2);
                for (int i = 0; i < shape.Count - 1; i++)
                {
                    grph.DrawLine(sp,
                                  shape[i].x / scale * debugImage.Width,
                                  shape[i].y / scale * debugImage.Height,
                                  shape[i + 1].x / scale * debugImage.Width,
                                  shape[i + 1].y / scale * debugImage.Height);
                }
                OnDebugStep(debugImage, "Merging " + NeighbourName(side) + " tile: Orange line displays the lowest cost seam");
            }

            result = new List <PoissonSample>();

            for (int i = 0; i < candidates.Count; i++)
            {
                bool insideSeam = GeomUtils.PointInPolygon(new Vertex(candidates[i].sample.x * scale, candidates[i].sample.y * scale), shape);
                if ((insideSeam && candidates[i].sourceDist == 1) ||
                    (!insideSeam && candidates[i].sourceDist == 0))
                {
                    result.Add(candidates[i].sample);
                }
            }

            if (debugImage != null)
            {
                Graphics grph = Graphics.FromImage(debugImage);
                grph.Clear(Color.White);
                SolidBrush brushInside  = new SolidBrush(Color.Red);
                SolidBrush brushOutside = new SolidBrush(Color.Gray);
                for (int i = 0; i < result.Count; i++)
                {
                    bool       insideSeam = GeomUtils.PointInPolygon(new Vertex(result[i].x * scale, result[i].y * scale), shape);
                    float      r          = (float)debugImage.Width * 0.01f;
                    RectangleF rect       = new RectangleF(result[i].x * debugImage.Width - r, result[i].y * debugImage.Height - r, 2.0f * r, 2.0f * r);
                    if (insideSeam)
                    {
                        grph.FillEllipse(brushInside, rect);
                    }
                    else
                    {
                        grph.FillEllipse(brushOutside, rect);
                    }
                }
                OnDebugStep(debugImage, "Merging " + NeighbourName(side) + " tile: gray dots = base tile, red dots = merged points");
            }
        }
Пример #13
0
 internal void OnDelaunayStep(Adjacency adjacency)
 {
     Bitmap bmp = new Bitmap(800, 800);
     float scale = 1000.0f;
     for (int j = 0; j < adjacency.vertices.Count; j++)
     {
         adjacency.vertices[j].x /= scale;
         adjacency.vertices[j].y /= scale;
     }
     adjacency.ToImage(bmp);
     for (int j = 0; j < adjacency.vertices.Count; j++)
     {
         adjacency.vertices[j].x *= scale;
         adjacency.vertices[j].y *= scale;
     }
     QueueDebugImage(bmp);
 }
Пример #14
0
        internal void OnDelaunayTriangleCheck(Adjacency adjacency, int triangle)
        {
            Bitmap img = new Bitmap(800,800);
            Random random = new Random();
            float scale = 1000.0f;
            for (int j = 0; j < adjacency.vertices.Count; j++)
            {
                adjacency.vertices[j].x /= scale;
                adjacency.vertices[j].y /= scale;
            }
            adjacency.ToImage(img);

            Graphics g = Graphics.FromImage(img);
            PointF[] points = new PointF[3];
            for (int i = 0; i < 3; i++)
            {
                Vertex v = adjacency.vertices[adjacency.triangles[triangle].vertices[i]];
                points[i] = new PointF(v.x * img.Width, v.y * img.Height);
            }

            g.FillPolygon(new SolidBrush(Color.FromArgb(random.Next(255), random.Next(255), random.Next(255))), points);

            for (int j = 0; j < adjacency.vertices.Count; j++)
            {
                adjacency.vertices[j].x *= scale;
                adjacency.vertices[j].y *= scale;
            }

            QueueDebugImage(img);
        }
Пример #15
0
        private static void Delaunay2DRemoveSuperTriangle(Adjacency adjacency, int stIdx1, int stIdx2, int stIdx3, out List<int> outTriangles, List<Vertex> vertices)
        {
            outTriangles = new List<int>();
            // remove triangles containing vertices from the supertriangle
            for (int i = 0; i < adjacency.triangles.Count; i++)
            {
                Adjacency.Triangle triangle = adjacency.triangles[i];
                if (!triangle.valid)
                {
                    continue;
                }
                if (triangle.Contains(stIdx1) || triangle.Contains(stIdx2) || triangle.Contains(stIdx3))
                {
                    adjacency.RemoveTriangle(i);
                }
                else
                {
                    for (int j = 0; j < 3; j++)
                    {
                        Debug.Assert(triangle.vertices[j] >= 0 && triangle.vertices[j] < adjacency.vertices.Count);
                        outTriangles.Add(triangle.vertices[j]);
                    }
                }
            }
            adjacency.invalidTriangles.Clear();

            // remove first 3 vertices (belonging to the supertriangle) and remap all remaining tris

            for (int i = 0; i < outTriangles.Count; i++)
            {
                outTriangles[i] -= 3;
            }

            int numTris = adjacency.triangles.Count;
            for (int i = 0; i < numTris; i++)
            {
                if (!adjacency.triangles[i].valid)
                {
                    int remappedTriangle = numTris - 1;
                    if (remappedTriangle > i)
                    {
                        adjacency.triangles[i] = adjacency.triangles[remappedTriangle];
                        numTris--;

                        // remap edges pointing to this triangle
                        for (int e = 0; e < 3; e++)
                        {
                            int edgeIndex = Math.Abs(adjacency.triangles[i].edges[e]) - 1;
                            if (adjacency.edges[edgeIndex].triangles[0] == remappedTriangle)
                                adjacency.edges[edgeIndex].triangles[0] = i;
                            if (adjacency.edges[edgeIndex].triangles[1] == remappedTriangle)
                                adjacency.edges[edgeIndex].triangles[1] = i;
                        }
            #if  false
                        for (int e = 0; e < adjacency.edges.Count; e++)
                        {
                            Adjacency.Edge edge = adjacency.edges[e];
                            for (int et = 0; et < 2; et++)
                            {
                                Debug.Assert(edge.triangles[et] != i);
                                if (edge.triangles[et] == remappedTriangle)
                                {
                                    edge.triangles[et] = i;
                                }
                            }
                        }
            #endif

                        i--;
                    }
                }
                else
                {
                    Adjacency.Triangle t = adjacency.triangles[i];
                    t.vertices[0] -= 3;
                    t.vertices[1] -= 3;
                    t.vertices[2] -= 3;
                }
            }
            adjacency.triangles.RemoveRange(numTris, adjacency.triangles.Count - numTris);
            adjacency.vertices = vertices;
            for (int i = 0; i < adjacency.edges.Count; i++)
            {
                adjacency.edges[i].vertices[0] -= 3;
                adjacency.edges[i].vertices[1] -= 3;
            }
        }
Пример #16
0
        public static bool Delaunay2DTriangulate( List<Vertex> vertices, bool removeSurroundingTriangle, out List<int> outTriangles, ref Adjacency adjacencyInfo )
        {
            /* Implements Lawson algorithm:
            http://www.henrikzimmer.com/VoronoiDelaunay.pdf

            Unlike the Watson algorithm the Lawson algorithm is not based finding circumcircles and deleting
            triangles to obtain an insertion polygon. Lawson's incremental insertion algorithm utilizes edge
            flippings to achieve the same result and thus avoids a possibly faulty, degenerate mesh which can
            occur using Watsons method.
            In the same way as before, if we are not given a start triangulation we use a super triangle
            enclosing all vertices in V . In every insertion step a vertex p belonging to V is inserted.
            A simple retriangulation is made where the edges are inserted between p and the corner vertices of
            the triangle containing p (fig.11). For all new triangles formed the circumcircles are checked, if they contain
            any neighbouring vertex the edge between them is flipped. This process is executed recursively
            until there are no more faulty triangles and no more flips are required (fig.12). The algorithm then
            moves on, inserting another vertex from V until they have all been triangulated into a mesh, then,
            like before the super triangle and its edge are removed.
            */

            if ( vertices.Count < 3 )
            {
                outTriangles = new List<int>();
                return false;
            }

            Adjacency adjacency = adjacencyInfo;
            if ( adjacencyInfo == null ) {
                adjacency = new Adjacency();
            };

            int stIdx1, stIdx2, stIdx3;
            Delaunay2DCreateSuperTriangle(vertices, adjacency, out stIdx1, out stIdx2, out stIdx3 );

            if (!Delaunay2DInsertPoints(vertices, adjacency))
            {
                outTriangles = new List<int>();
                return false;
            }

            if (removeSurroundingTriangle)
            {
                Delaunay2DRemoveSuperTriangle(adjacency, stIdx1, stIdx2, stIdx3, out outTriangles, vertices);
            }
            else
            {
                // move the 3 vertices from the supertriangle right to the end of
                // the array instead of the beginning, so that the N first vertices
                // match with the point insertion order
                foreach (Adjacency.Edge e in adjacency.edges)
                {
                    for (int i = 0; i < 2; i++)
                    {
                        e.vertices[i] -= 3;
                        if (e.vertices[i] < 0) e.vertices[i] += adjacency.vertices.Count;
                    }
                }

                foreach (Adjacency.Triangle t in adjacency.triangles)
                {
                    for (int i = 0; i < 3; i++)
                    {
                        t.vertices[i] -= 3;
                        if (t.vertices[i] < 0) t.vertices[i] += adjacency.vertices.Count;
                    }
                }

                adjacency.vertices.Add(adjacency.vertices[0]);
                adjacency.vertices.Add(adjacency.vertices[1]);
                adjacency.vertices.Add(adjacency.vertices[2]);
                adjacency.vertices.RemoveAt(0);
                adjacency.vertices.RemoveAt(0);
                adjacency.vertices.RemoveAt(0);

                //
                outTriangles = new List<int>();
                for (int i = 0; i < adjacency.triangles.Count; i++)
                {
                    if (adjacency.triangles[i].valid)
                    {
                        outTriangles.Add(adjacency.triangles[i].vertices[0]);
                        outTriangles.Add(adjacency.triangles[i].vertices[1]);
                        outTriangles.Add(adjacency.triangles[i].vertices[2]);
                    }
                }

            }

            return true;
        }
Пример #17
0
        private static void MergeTiles(List<PoissonSample> source, List<PoissonSample> toMerge,
                                         neighbour_e side,
                                         out List<PoissonSample> result)
        {
            List<MergeCandidate> candidates = new List<MergeCandidate>();
            foreach (PoissonSample s in source) candidates.Add(new MergeCandidate(s, 0));
            foreach (PoissonSample s in toMerge) candidates.Add(new MergeCandidate(s, 1));

            const float scale = 1000.0f; // apply a temporary scaling to the Poisson points (in 0-1 range)
            // before Delaunay triangulation (we'll undo this later) to avoid
            // requiring very small epsilons for point snapping etc which
            // may lead to numeric issues.

            List<Vertex> vertices = new List<Vertex>();
            for (int i = 0; i < candidates.Count; i++)
            {
                vertices.Add(new Vertex(candidates[i].sample.x * scale, candidates[i].sample.y * scale));
            }

            List<int> outTriangles;
            Adjacency adjacency = new Adjacency();
            Delaunay2D.Delaunay2DTriangulate(vertices, false, out outTriangles, ref adjacency);

            for (int j = 0; j < adjacency.vertices.Count; j++)
            {
                adjacency.vertices[j].x /= scale;
                adjacency.vertices[j].y /= scale;
            }

            Image debugImage = null;
            if (OnDebugStep != null) debugImage = new Bitmap(800, 800);

            Voronoi v = Voronoi.FromDelaunay(adjacency);

            #if DEBUG
            if (debugImage != null)
            {
                v.ToImage(debugImage, adjacency);
                OnDebugStep(debugImage, "Voronoi diagram generated from Delaunay Triangulation");
            }
            #endif
            Graph g = new Graph(v, adjacency, candidates);
            if (debugImage != null)
            {
                g.ToImage(debugImage);
                OnDebugStep(debugImage, "Merging " + NeighbourName(side) + " tile: Convert voronoi diagram into a graph");
            }

            int v0, v1;
            for (int i = 0; i < 4; i++)
                if (i != (int)side) g.Clip((WangTile.neighbour_e)i, out v0, out v1);
            g.Clip(side, out v0, out v1);
            #if DEBUG
            if (debugImage != null)
            {
                g.ToImage(debugImage);
                OnDebugStep(debugImage, "Graph generated from Voronoi Diagram");
            }
            #endif
            List<int> path;
            g.ShortestPath(v0, v1, out path);
            List<Vertex> shape;
            g.GenerateShape(path, out shape);

            for (int i = 0; i < shape.Count - 1; i++) // skip last one as it is the same as the first element (they're references)
            {
                shape[i].x *= scale;
                shape[i].y *= scale;
            }

            if (debugImage != null)
            {
                Graphics grph = Graphics.FromImage(debugImage);
                Pen sp = new Pen(Color.Orange, 2);
                for (int i = 0; i < shape.Count - 1; i++)
                {
                    grph.DrawLine(sp,
                                  shape[i].x / scale * debugImage.Width,
                                  shape[i].y / scale * debugImage.Height,
                                  shape[i + 1].x / scale * debugImage.Width,
                                  shape[i + 1].y / scale * debugImage.Height);
                }
                OnDebugStep(debugImage, "Merging " + NeighbourName(side) + " tile: Orange line displays the lowest cost seam");
            }

            result = new List<PoissonSample>();

            for (int i = 0; i < candidates.Count; i++)
            {
                bool insideSeam = GeomUtils.PointInPolygon(new Vertex(candidates[i].sample.x * scale, candidates[i].sample.y * scale), shape);
                if ((insideSeam && candidates[i].sourceDist == 1) ||
                     (!insideSeam && candidates[i].sourceDist == 0))
                    result.Add(candidates[i].sample);
            }

            if (debugImage != null)
            {
                Graphics grph = Graphics.FromImage(debugImage);
                grph.Clear(Color.White);
                SolidBrush brushInside = new SolidBrush(Color.Red);
                SolidBrush brushOutside = new SolidBrush(Color.Gray);
                for (int i = 0; i < result.Count; i++)
                {
                    bool insideSeam = GeomUtils.PointInPolygon(new Vertex(result[i].x * scale, result[i].y * scale), shape);
                    float r = (float)debugImage.Width * 0.01f;
                    RectangleF rect = new RectangleF(result[i].x * debugImage.Width - r, result[i].y * debugImage.Height - r, 2.0f * r, 2.0f * r);
                    if (insideSeam)
                        grph.FillEllipse(brushInside, rect);
                    else
                        grph.FillEllipse(brushOutside, rect);
                }
                OnDebugStep(debugImage, "Merging " + NeighbourName(side) + " tile: gray dots = base tile, red dots = merged points");
            }
        }
Пример #18
0
        private static bool Delaunay2DInsertPoints(List<Vertex> vertices, Adjacency adjacency)
        {
            // Insert points

            HashSet<int> toCheck = new HashSet<int>();

            for (int i = 0; i < vertices.Count; i++)
            {

                // Insert Vi
                Vertex Vi = vertices[i];

                bool skip = false;
                for (int j = 0; j < adjacency.vertices.Count; j++)
                {
                    if ((Vi - adjacency.vertices[j]).Length() <= COINCIDENT_POINTS_DISTANCE_EPSILON)
                    {
                        // the point has already been inserted. Skip it
                        skip = true;
                        break;
                    }
                }
                if (skip)
                {
                    continue;
                }

                if (OnDelaunayInsertPoint != null)
                    OnDelaunayInsertPoint(adjacency, Vi);

                int tri = adjacency.PointInTriangle(Vi);

                if (tri < 0)
                {
                    Debug.Assert(false);
                    return false;
                }

                // check whether the point lies exactly on one edge of the triangle
                int edgeIdx = adjacency.PointInTriangleEdge(Vi, tri);
                if (edgeIdx >= 0)
                {
                    // split the edge by Vi
                    int[] result;
                    adjacency.SplitEdge(edgeIdx, Vi, out result);
                    for (int j = 0; j < 4; j++)
                    {
                        if (result[j] >= 0)
                        {
                            toCheck.Add(result[j]);
                        }
                    }
                }
                else
                {
                    // split the triangle in 3
                    int[] result;
                    adjacency.SplitTriangle(tri, Vi, out result);
                    for (int j = 0; j < 3; j++)
                    {
                        toCheck.Add(result[j]);
                    }
                }

                while (toCheck.Count > 0)
                {
                    int t = toCheck.Last<int>();
                    toCheck.Remove(t);

                    Adjacency.Triangle triangle = adjacency.triangles[t];
                    if (!triangle.valid)
                    {
                        continue;
                    }

                    if (OnDelaunayTriangleCheck != null)
                        OnDelaunayTriangleCheck(adjacency, t);

                    // check Delaunay condition
                    for (int e = 0; e < 3; e++)
                    {
                        if (!adjacency.triangles[t].valid) continue;

                        int adjacentIdx = adjacency.AdjacentTriangle(t, e);
                        if (adjacentIdx < 0)
                        {
                            continue;
                        }
                        int globalEdgeIndex = Math.Abs(triangle.edges[e]) - 1;
                        Adjacency.Triangle adjacent = adjacency.triangles[adjacentIdx];
                        if (!adjacent.valid)
                        {
                            continue;
                        }
                        Debug.Assert(adjacent.valid);
                        int edgeFromAdjacent = adjacent.LocalEdgeIndex(globalEdgeIndex);
                        Debug.Assert(edgeFromAdjacent >= 0);
                        int v = adjacency.VertexOutOfTriEdge(adjacentIdx, edgeFromAdjacent);
                        Debug.Assert(v >= 0);
                        Debug.Assert(!triangle.Contains(v));

                        if (triangle.InsideCircumcircle(adjacency.vertices[v], adjacency.vertices) > INSIDE_CIRCUMCIRCLE_EPSILON)
                        {
                            int[] result;
                            if (adjacency.FlipTriangles(t, adjacentIdx, out result))
                            {
                                toCheck.Add(result[0]);
                                toCheck.Add(result[1]);
                                //break;
                            }
                        }
                    }
                }

                if (OnDelaunayStep != null)
                    OnDelaunayStep(adjacency);
            }
            return true;
        }
Пример #19
0
        private static void Delaunay2DRemoveSuperTriangle(Adjacency adjacency, int stIdx1, int stIdx2, int stIdx3, out List <int> outTriangles, List <Vertex> vertices)
        {
            outTriangles = new List <int>();
            // remove triangles containing vertices from the supertriangle
            for (int i = 0; i < adjacency.triangles.Count; i++)
            {
                Adjacency.Triangle triangle = adjacency.triangles[i];
                if (!triangle.valid)
                {
                    continue;
                }
                if (triangle.Contains(stIdx1) || triangle.Contains(stIdx2) || triangle.Contains(stIdx3))
                {
                    adjacency.RemoveTriangle(i);
                }
                else
                {
                    for (int j = 0; j < 3; j++)
                    {
                        Debug.Assert(triangle.vertices[j] >= 0 && triangle.vertices[j] < adjacency.vertices.Count);
                        outTriangles.Add(triangle.vertices[j]);
                    }
                }
            }
            adjacency.invalidTriangles.Clear();

            // remove first 3 vertices (belonging to the supertriangle) and remap all remaining tris

            for (int i = 0; i < outTriangles.Count; i++)
            {
                outTriangles[i] -= 3;
            }

            int numTris = adjacency.triangles.Count;

            for (int i = 0; i < numTris; i++)
            {
                if (!adjacency.triangles[i].valid)
                {
                    int remappedTriangle = numTris - 1;
                    if (remappedTriangle > i)
                    {
                        adjacency.triangles[i] = adjacency.triangles[remappedTriangle];
                        numTris--;

                        // remap edges pointing to this triangle
                        for (int e = 0; e < 3; e++)
                        {
                            int edgeIndex = Math.Abs(adjacency.triangles[i].edges[e]) - 1;
                            if (adjacency.edges[edgeIndex].triangles[0] == remappedTriangle)
                            {
                                adjacency.edges[edgeIndex].triangles[0] = i;
                            }
                            if (adjacency.edges[edgeIndex].triangles[1] == remappedTriangle)
                            {
                                adjacency.edges[edgeIndex].triangles[1] = i;
                            }
                        }
#if  false
                        for (int e = 0; e < adjacency.edges.Count; e++)
                        {
                            Adjacency.Edge edge = adjacency.edges[e];
                            for (int et = 0; et < 2; et++)
                            {
                                Debug.Assert(edge.triangles[et] != i);
                                if (edge.triangles[et] == remappedTriangle)
                                {
                                    edge.triangles[et] = i;
                                }
                            }
                        }
#endif

                        i--;
                    }
                }
                else
                {
                    Adjacency.Triangle t = adjacency.triangles[i];
                    t.vertices[0] -= 3;
                    t.vertices[1] -= 3;
                    t.vertices[2] -= 3;
                }
            }
            adjacency.triangles.RemoveRange(numTris, adjacency.triangles.Count - numTris);
            adjacency.vertices = vertices;
            for (int i = 0; i < adjacency.edges.Count; i++)
            {
                adjacency.edges[i].vertices[0] -= 3;
                adjacency.edges[i].vertices[1] -= 3;
            }
        }