public static Mesh CreateVoronoiMesh(GK.VoronoiDiagram diagram) { Mesh mesh = new Mesh(); Vector3[] vertices = new Vector3[diagram.Vertices.Count]; Vector2[] uv = new Vector2[diagram.Vertices.Count]; int[] indices = new int[diagram.Edges.Count * 2]; for (int i = 0; i < diagram.Vertices.Count; i++) { vertices[i] = diagram.Vertices[i]; uv[i] = diagram.Vertices[i]; } int j = 0; for (int i = 0; i < diagram.Edges.Count; i++) { if (diagram.Edges[i].Type == GK.VoronoiDiagram.EdgeType.Segment) { indices[j] = diagram.Edges[i].Vert0; indices[j + 1] = diagram.Edges[i].Vert1; j += 2; } } mesh.SetVertices(vertices); mesh.uv = uv; mesh.SetIndices(indices, MeshTopology.Lines, 0); return(mesh); }
/// <summary> /// Calculate a voronoi diagram and return it. /// </summary> public VoronoiDiagram CalculateDiagram(IList <Vector2> inputVertices) { VoronoiDiagram result = null; CalculateDiagram(inputVertices, ref result); return(result); }
// calcula o diagrama de voronoi link para a implementação utilizada: https://github.com/OskarSigvardsson/unity-delaunay // serve mais para ser usar a triangulação de delaunay do resultado do diagrama public void BuildVoronoi() { var points2d = Graham.GetPointVectorList(); GK.VoronoiCalculator voronoiCalculator = new GK.VoronoiCalculator(); Diagram = voronoiCalculator.CalculateDiagram(Graham.GetPointVectorList()); //var extends = Map.GetComponent<MeshFilter>().mesh.bounds.size; //extends.Scale(Map.transform.localScale); VoronoiMesh = CreateVoronoiMesh(Diagram); BuildGraph(); }
public static Mesh CreateTriangulationMesh(GK.VoronoiDiagram diagram, int[] indices) { Mesh mesh = new Mesh(); Vector3[] vertices = new Vector3[diagram.Triangulation.Vertices.Count]; Vector2[] uv = new Vector2[diagram.Triangulation.Vertices.Count]; for (int i = 0; i < diagram.Triangulation.Vertices.Count; i++) { vertices[i] = diagram.Triangulation.Vertices[i]; uv[i] = diagram.Triangulation.Vertices[i]; } mesh.SetVertices(vertices); mesh.uv = uv; mesh.SetIndices(indices, MeshTopology.Lines, 0); return(mesh); }
/// <summary> /// Non-allocating version of CalculateDiagram. /// /// I guess it's not strictly true that it generates NO garbage, because /// it might if it has to resize internal buffers, but all buffers are /// reused from invocation to invocation. /// </summary> public void CalculateDiagram(IList <Vector2> inputVertices, ref VoronoiDiagram result) { // TODO: special case for 1 points // TODO: special case for 2 points // TODO: special case for 3 points // TODO: special case for collinear points if (inputVertices.Count < 3) { throw new NotImplementedException("Not implemented for < 3 vertices"); } if (result == null) { result = new VoronoiDiagram(); } var trig = result.Triangulation; result.Clear(); Profiler.BeginSample("Delaunay triangulation"); delCalc.CalculateTriangulation(inputVertices, ref trig); Profiler.EndSample(); pts.Clear(); var verts = trig.Vertices; var tris = trig.Triangles; var centers = result.Vertices; var edges = result.Edges; if (tris.Count > pts.Capacity) { { pts.Capacity = tris.Count; } } if (tris.Count > edges.Capacity) { edges.Capacity = tris.Count; } for (int ti = 0; ti < tris.Count; ti += 3) { var p0 = verts[tris[ti]]; var p1 = verts[tris[ti + 1]]; var p2 = verts[tris[ti + 2]]; // Triangle is in CCW order Debug.Assert(Geom.ToTheLeft(p2, p0, p1)); centers.Add(Geom.CircumcircleCenter(p0, p1, p2)); } for (int ti = 0; ti < tris.Count; ti += 3) { pts.Add(new PointTriangle(tris[ti], ti)); pts.Add(new PointTriangle(tris[ti + 1], ti)); pts.Add(new PointTriangle(tris[ti + 2], ti)); } cmp.tris = tris; cmp.verts = verts; Profiler.BeginSample("Sorting"); pts.Sort(cmp); Profiler.EndSample(); // The comparer lives on between runs of the algorithm, so clear the // reference to the arrays so that the reference is lost. It may be // the case that the calculator lives on much longer than the // results, and not clearing these would keep the results alive, // leaking memory. cmp.tris = null; cmp.verts = null; for (int i = 0; i < pts.Count; i++) { result.FirstEdgeBySite.Add(edges.Count); var start = i; var end = -1; for (int j = i + 1; j < pts.Count; j++) { if (pts[i].Point != pts[j].Point) { end = j - 1; break; } } if (end == -1) { end = pts.Count - 1; } i = end; var count = end - start; Debug.Assert(count >= 0); for (int ptiCurr = start; ptiCurr <= end; ptiCurr++) { bool isEdge; var ptiNext = ptiCurr + 1; if (ptiNext > end) { ptiNext = start; } var ptCurr = pts[ptiCurr]; var ptNext = pts[ptiNext]; var tiCurr = ptCurr.Triangle; var tiNext = ptNext.Triangle; var p0 = verts[ptCurr.Point]; var v2nan = new Vector2(float.NaN, float.NaN); if (count == 0) { isEdge = true; } else if (count == 1) { var cCurr = Geom.TriangleCentroid(verts[tris[tiCurr]], verts[tris[tiCurr + 1]], verts[tris[tiCurr + 2]]); var cNext = Geom.TriangleCentroid(verts[tris[tiNext]], verts[tris[tiNext + 1]], verts[tris[tiNext + 2]]); isEdge = Geom.ToTheLeft(cCurr, p0, cNext); } else { isEdge = !SharesEdge(tris, tiCurr, tiNext); } if (isEdge) { Vector2 v0, v1; if (ptCurr.Point == tris[tiCurr]) { v0 = verts[tris[tiCurr + 2]] - verts[tris[tiCurr + 0]]; } else if (ptCurr.Point == tris[tiCurr + 1]) { v0 = verts[tris[tiCurr + 0]] - verts[tris[tiCurr + 1]]; } else { Debug.Assert(ptCurr.Point == tris[tiCurr + 2]); v0 = verts[tris[tiCurr + 1]] - verts[tris[tiCurr + 2]]; } if (ptNext.Point == tris[tiNext]) { v1 = verts[tris[tiNext + 0]] - verts[tris[tiNext + 1]]; } else if (ptNext.Point == tris[tiNext + 1]) { v1 = verts[tris[tiNext + 1]] - verts[tris[tiNext + 2]]; } else { Debug.Assert(ptNext.Point == tris[tiNext + 2]); v1 = verts[tris[tiNext + 2]] - verts[tris[tiNext + 0]]; } edges.Add(new VoronoiDiagram.Edge( VoronoiDiagram.EdgeType.RayCCW, ptCurr.Point, tiCurr / 3, -1, Geom.RotateRightAngle(v0) )); edges.Add(new VoronoiDiagram.Edge( VoronoiDiagram.EdgeType.RayCW, ptCurr.Point, tiNext / 3, -1, Geom.RotateRightAngle(v1) )); } else { if (!Geom.AreCoincident(centers[tiCurr / 3], centers[tiNext / 3])) { edges.Add(new VoronoiDiagram.Edge( VoronoiDiagram.EdgeType.Segment, ptCurr.Point, tiCurr / 3, tiNext / 3, v2nan )); } } } } }
/// <summary> /// Clip site of voronoi diagram using polygon (must be convex), /// returning the clipped vertices in clipped list. Modifies neither /// polygon nor diagram, so can be run in parallel for several sites at /// once. /// </summary> public void ClipSite(VoronoiDiagram diag, IList <Vector2> polygon, int site, ref List <Vector2> clipped) { pointsIn.Clear(); pointsIn.AddRange(polygon); int firstEdge, lastEdge; if (site == diag.Sites.Count - 1) { firstEdge = diag.FirstEdgeBySite[site]; lastEdge = diag.Edges.Count - 1; } else { firstEdge = diag.FirstEdgeBySite[site]; lastEdge = diag.FirstEdgeBySite[site + 1] - 1; } for (int ei = firstEdge; ei <= lastEdge; ei++) { pointsOut.Clear(); var edge = diag.Edges[ei]; Vector2 lp, ld; if (edge.Type == VoronoiDiagram.EdgeType.RayCCW || edge.Type == VoronoiDiagram.EdgeType.RayCW) { lp = diag.Vertices[edge.Vert0]; ld = edge.Direction; if (edge.Type == VoronoiDiagram.EdgeType.RayCW) { ld *= -1; } } else if (edge.Type == VoronoiDiagram.EdgeType.Segment) { var lp0 = diag.Vertices[edge.Vert0]; var lp1 = diag.Vertices[edge.Vert1]; lp = lp0; ld = lp1 - lp0; } else if (edge.Type == VoronoiDiagram.EdgeType.Line) { throw new NotSupportedException("Haven't implemented voronoi halfplanes yet"); } else { Debug.Assert(false); return; } for (int pi0 = 0; pi0 < pointsIn.Count; pi0++) { var pi1 = pi0 == pointsIn.Count - 1 ? 0 : pi0 + 1; var p0 = pointsIn[pi0]; var p1 = pointsIn[pi1]; var p0Inside = Geom.ToTheLeft(p0, lp, lp + ld); var p1Inside = Geom.ToTheLeft(p1, lp, lp + ld); if (p0Inside && p1Inside) { pointsOut.Add(p1); } else if (!p0Inside && !p1Inside) { // Do nothing, both are outside } else { var intersection = Geom.LineLineIntersection(lp, ld.normalized, p0, (p1 - p0).normalized); if (p0Inside) { pointsOut.Add(intersection); } else if (p1Inside) { pointsOut.Add(intersection); pointsOut.Add(p1); } else { Debug.Assert(false); } } } var tmp = pointsIn; pointsIn = pointsOut; pointsOut = tmp; } if (clipped == null) { clipped = new List <Vector2>(); } else { clipped.Clear(); } clipped.AddRange(pointsIn); /* * pointsIn.Clear(); * * pointsIn.AddRange(polygon); * * int firstEdge, lastEdge; * * if (site == diag.Sites.Count - 1) * { * firstEdge = diag.FirstEdgeBySite[site]; * lastEdge = diag.Edges.Count - 1; * } * else * { * firstEdge = diag.FirstEdgeBySite[site]; * lastEdge = diag.FirstEdgeBySite[site + 1] - 1; * } * * for (int ei = firstEdge; ei <= lastEdge; ei++) * { * pointsOut.Clear(); * * var edge = diag.Edges[ei]; * * Vector2 lp, ld; * * if (edge.Type == VoronoiDiagram.EdgeType.RayCCW || edge.Type == VoronoiDiagram.EdgeType.RayCW) * { * lp = diag.Vertices[edge.Vert0]; * ld = edge.Direction; * * if (edge.Type == VoronoiDiagram.EdgeType.RayCW) * { * ld *= -1; * } * } * else if (edge.Type == VoronoiDiagram.EdgeType.Segment) * { * var lp0 = diag.Vertices[edge.Vert0]; * var lp1 = diag.Vertices[edge.Vert1]; * * lp = lp0; * ld = lp1 - lp0; * } * else if (edge.Type == VoronoiDiagram.EdgeType.Line) * { * throw new NotSupportedException("Haven't implemented voronoi halfplanes yet"); * } * else * { * Debug.Assert(false); * return; * } * * for (int pi0 = 0; pi0 < pointsIn.Count; pi0++) * { * var pi1 = pi0 == pointsIn.Count - 1 ? 0 : pi0 + 1; * * var p0 = pointsIn[pi0]; * var p1 = pointsIn[pi1]; * * var p0Inside = Geom.ToTheLeft(p0, lp, lp + ld); * var p1Inside = Geom.ToTheLeft(p1, lp, lp + ld); * * if (p0Inside && p1Inside) * { * pointsOut.Add(p1); * } * else if (!p0Inside && !p1Inside) * { * // Do nothing, both are outside * } * else * { * var intersection = Geom.LineLineIntersection(lp, ld.normalized, p0, (p1 - p0).normalized); * * if (p0Inside) * { * pointsOut.Add(intersection); * } * else if (p1Inside) * { * pointsOut.Add(intersection); * pointsOut.Add(p1); * } * else * { * Debug.Assert(false); * } * } * } * * if(pointsOut.Count == 0) * { * //pointsOut = new List<Vector2>(pointsIn); * } * * var tmp = pointsIn; * pointsIn = pointsOut; * pointsOut = tmp; * } * * if (clipped == null) * { * clipped = new List<Vector2>(); * } * else * { * clipped.Clear(); * } * * clipped.AddRange(pointsIn); * */ }
public void FixSiteEdges(ref VoronoiDiagram diag, int site) { if (diag == null) { return; } List <test> poly = new List <test>(); int firstEdge, lastEdge; Vector2 polyCenter = Vector2.zero; if (site == diag.Sites.Count - 1) { firstEdge = diag.FirstEdgeBySite[site]; lastEdge = diag.Edges.Count - 1; } else { firstEdge = diag.FirstEdgeBySite[site]; lastEdge = diag.FirstEdgeBySite[site + 1] - 1; } for (int i = firstEdge; i < lastEdge; i++) { test _test = new test(); _test.index = diag.Edges[i].Vert0; _test.diag = diag; poly.Add(_test); polyCenter += diag.Vertices[poly[poly.Count - 1].index]; } polyCenter /= poly.Count; //VALIDATE for (int i = 0; i < poly.Count; i++) { for (int j = 0; j < poly.Count; j++) { if (poly[i].index == poly[j].index) { Debug.LogError("THEY MATCH THIS IS BAD"); } } } //List<Vector2> polyList = new List<Vector2>(); //foreach (test t in poly) //{ // polyList.Add(new Vector2(diag.Vertices[t.index].x, diag.Vertices[t.index].y)); //} //CityTest.LineRenderPoly(polyList); poly.Sort((a, b) => { double a2 = ((Mathf.Rad2Deg * (Mathf.Atan2(a.diag.Vertices[a.index].x - polyCenter.x, a.diag.Vertices[a.index].y - polyCenter.y)) + 360.0f) % 360.0f); double a1 = ((Mathf.Rad2Deg * (Math.Atan2(a.diag.Vertices[b.index].x - polyCenter.x, a.diag.Vertices[b.index].y - polyCenter.y)) + 360.0f) % 360.0f); return((int)(a1 - a2)); } //((Mathf.Rad2Deg * (Mathf.Atan2(a.diag.Vertices[a.index].x - polyCenter.x, a.diag.Vertices[a.index].y - polyCenter.y)) + 360) % 360).CompareTo((Mathf.Rad2Deg * (Math.Atan2(a.diag.Vertices[b.index].x - polyCenter.x, a.diag.Vertices[b.index].y - polyCenter.y)) + 360) % 360) ); //polyList.Clear(); //foreach (test t in poly) //{ // polyList.Add(new Vector2(diag.Vertices[t.index].x, diag.Vertices[t.index].y)); //} //CityTest.LineRenderPoly(polyList); for (int i = 0; i < poly.Count; i++) { int nextIndex = poly[0].index; if (nextIndex + 1 < poly.Count) { nextIndex = poly[i + 1].index; } diag.Edges[firstEdge + i] = new VoronoiDiagram.Edge(diag.Edges[i].Type, diag.Edges[i].Site, poly[i].index, nextIndex, diag.Vertices[nextIndex] - diag.Vertices[poly[i].index]); } //List<Vector2> poly222 = new List<Vector2>(); // //for (int ei = firstEdge; ei <= lastEdge; ei++) //{ // poly222.Add(diag.Vertices[diag.Edges[ei].Vert0]); //} // //CityTest.LineRenderPoly(poly222); }
public void GenerateCity() { // use our seed for generation Random.State currentRNGState = Random.state; Random.InitState(citySeed); myCity = new City(); myCity.thisCity = new GameObject("NewCity"); myCity.cityNoiseOffset = new Vector2(Random.Range(-10000, 10000), Random.Range(-10000, 10000)); GK.VoronoiDiagram cityDiagram = new GK.VoronoiDiagram(); //generate points List <Vector2> points = CreateCityPoint(myCity.cityNoiseOffset);// calulate voroni fracture sites //calculate city cityDiagram = new GK.VoronoiCalculator().CalculateDiagram(points);//calculate vorino diagram (ie roads seperating suburbs) using out sites //bounding List <Vector2> Polygon = new List <Vector2>(); Polygon.Add(new Vector2(0, 0)); Polygon.Add(new Vector2(citySize.x, 0)); Polygon.Add(new Vector2(citySize.x, citySize.y)); Polygon.Add(new Vector2(0, citySize.y)); List <Vector2> clippedSite = new List <Vector2>(); for (int siteIndex = 0; siteIndex < cityDiagram.Sites.Count; siteIndex++) { new GK.VoronoiClipper().ClipSite(cityDiagram, Polygon, siteIndex, ref clippedSite);// clip each of our suburbs if (clippedSite.Count > 0) { // assign and create a suburb object Suburb newSuburb = new Suburb(); myCity.suburbs.Add(newSuburb); newSuburb.thisSuburb = new GameObject("Suburb"); newSuburb.sitePosition = cityDiagram.Sites[siteIndex]; newSuburb.thisSuburb.transform.position = new Vector3(newSuburb.sitePosition.x, 0, newSuburb.sitePosition.y); newSuburb.thisSuburb.transform.SetParent(myCity.thisCity.transform); newSuburb.borders = new List <Vector2>(clippedSite); float xCoord = myCity.cityNoiseOffset.x + newSuburb.sitePosition.x / citySize.x * noiseScale; float yCoord = myCity.cityNoiseOffset.x + newSuburb.sitePosition.y / citySize.y * noiseScale; float noiseValue = curve.Evaluate(Mathf.PerlinNoise(xCoord, yCoord)); for (int i = 0; i < curve.keys.Length; i++)//set which type of suburb will be generated bassed off our noise weight at the suburbs position { if (noiseValue == curve.keys[i].value) { switch (i) { case 0: { newSuburb.suburbType = residentialSettings; break; } case 1: { newSuburb.suburbType = urbanSettings; break; } default: { newSuburb.suburbType = citySettings; break; } } } } newSuburb.seed = Random.Range(int.MinValue, int.MaxValue); } } Random.state = currentRNGState; }