static string NeighbourName(neighbour_e n) { switch (n) { case neighbour_e.NORTH: return("North"); case neighbour_e.SOUTH: return("South"); case neighbour_e.EAST: return("East"); case neighbour_e.WEST: return("West"); default: return(""); } }
static string NeighbourName(neighbour_e n) { switch (n) { case neighbour_e.NORTH: return "North"; case neighbour_e.SOUTH: return "South"; case neighbour_e.EAST: return "East"; case neighbour_e.WEST: return "West"; default: return ""; } }
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"); } }
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"); } }