Exemple #1
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);
        }
Exemple #2
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;
        }
Exemple #3
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
            }
        }
Exemple #4
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");
            }
        }