Пример #1
0
        private void AddEdge(int v0, int v1, float cost)
        {
            // remove all edges crossing our clipping edge (TODO: could we split them instead?)
            Edge         clippingEdge = new Edge(v0, v1, cost);
            Stack <Edge> candidates   = new Stack <Edge>();

            candidates.Push(clippingEdge);
            while (candidates.Count > 0)
            {
                Edge cEdge = candidates.Pop();

                bool isect = false;
                if ((vertices[cEdge.v[0]] - vertices[cEdge.v[1]]).Length() > 1e-3f)
                {
                    foreach (Edge e in edges)
                    {
                        Vertex p;
                        if (e.v[0] != cEdge.v[0] && e.v[0] != cEdge.v[1] &&
                            e.v[1] != cEdge.v[0] && e.v[1] != cEdge.v[1] &&
                            GeomUtils.SegmentIntersect(vertices[e.v[0]], vertices[e.v[1]], vertices[cEdge.v[0]], vertices[cEdge.v[1]], out p))
                        {
                            vertices.Add(p);
                            int isectIdx = vertices.Count - 1;

                            edges.Remove(e);
                            edges.Add(new Edge(e.v[0], isectIdx, e.cost));
                            edges.Add(new Edge(isectIdx, e.v[1], e.cost));

                            candidates.Push(new Edge(cEdge.v[0], isectIdx, cost));
                            candidates.Push(new Edge(isectIdx, cEdge.v[1], cost));
                            isect = true;
                            break;
                        }
                    }
                    if (!isect)
                    {
                        edges.Add(cEdge);
                    }
                }
            }
        }
Пример #2
0
        public bool FlipTriangles(int tri1, int tri2, out int[] result)
        {
            Triangle triangle1 = triangles[tri1];

            Debug.Assert(triangle1.valid);
            Triangle triangle2 = triangles[tri2];

            Debug.Assert(triangle2.valid);

            int commonEdgeIdx = CommonEdge(triangle1, triangle2);

            Debug.Assert(commonEdgeIdx >= 0);
            Edge commonEdge = edges[commonEdgeIdx];

            Debug.Assert(commonEdge.triangles[0] == tri1 || commonEdge.triangles[0] == tri2);
            Debug.Assert(commonEdge.triangles[1] == tri1 || commonEdge.triangles[1] == tri2);

            // Tri1 = A,B,C
            // Tri2 = B,D,C
            // commonEdge = B,C

            int localEdgeT1 = triangle1.LocalEdgeIndex(commonEdgeIdx);

            Debug.Assert(localEdgeT1 >= 0);
            int localEdgeT2 = triangle2.LocalEdgeIndex(commonEdgeIdx);

            Debug.Assert(localEdgeT2 >= 0);

            int A = VertexOutOfTriEdge(tri1, localEdgeT1);
            int D = VertexOutOfTriEdge(tri2, localEdgeT2);

            Debug.Assert(A >= 0);
            Debug.Assert(D >= 0);

            int B = (commonEdge.triangles[0] == tri1 ? commonEdge.vertices[0] : commonEdge.vertices[1]);
            int C = (B == commonEdge.vertices[0] ? commonEdge.vertices[1] : commonEdge.vertices[0]);

            Debug.Assert(A != B && A != C && A != D && B != C && B != D && C != D);

            result = new int[2];

            if (!GeomUtils.SegmentIntersect(vertices[A], vertices[D], vertices[B], vertices[C]))
            {
                // can't flip
                result[0] = result[1] = -1;
                return(false);
            }

            float closestAngleBefore = Math.Min(ClosestAngleOnTri(A, B, C), ClosestAngleOnTri(B, D, C));

            if (!IsWorthFlipping(A, B, C, D))
            {
                // The resulting triangles would not improve the triangulation, don't bother
                return(false);
            }

            RemoveTriangle(tri1);
            RemoveTriangle(tri2);

            result[0] = CreateTriangle(A, B, D);
            result[1] = CreateTriangle(A, D, C);

            float closestAngleAfter = Math.Min(ClosestAngleOnTri(A, B, D), ClosestAngleOnTri(A, D, C));

            //Debug.Assert(closestAngleBefore < closestAngleAfter);
#if DEBUG
            Debug.Assert(CheckConsistency());
#endif
            return(true);
        }
Пример #3
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");
            }
        }