From b45073fdec82a7c7d18256c268da78a280e6c0ff Mon Sep 17 00:00:00 2001 From: Regalis11 Date: Sun, 21 Jun 2015 22:31:56 +0300 Subject: [PATCH] Voronoi diagram generation --- Subsurface/DebugConsole.cs | 3 + Subsurface/Game1.cs | 2 + Subsurface/Map/Level.cs | 135 +++++ Subsurface/Map/Voronoi.cs | 976 ++++++++++++++++++++++++++++++ Subsurface/Map/VoronoiElements.cs | 149 +++++ Subsurface/Screens/GameScreen.cs | 2 + Subsurface/Subsurface.csproj | 3 + Subsurface/Subsurface.csproj.user | 2 +- Subsurface_Solution.v12.suo | Bin 279552 -> 280576 bytes 9 files changed, 1271 insertions(+), 1 deletion(-) create mode 100644 Subsurface/Map/Level.cs create mode 100644 Subsurface/Map/Voronoi.cs create mode 100644 Subsurface/Map/VoronoiElements.cs diff --git a/Subsurface/DebugConsole.cs b/Subsurface/DebugConsole.cs index 772eb006c..58db40c6f 100644 --- a/Subsurface/DebugConsole.cs +++ b/Subsurface/DebugConsole.cs @@ -199,6 +199,9 @@ namespace Subsurface Hull.EditWater = !Hull.EditWater; } break; + case "generatelevel": + Game1.Level = new Level(Game1.localRandom.Next(), 20, 5000, 5000); + break; case "fowenabled": case "fow": case "drawfow": diff --git a/Subsurface/Game1.cs b/Subsurface/Game1.cs index 233a22910..0dc69062c 100644 --- a/Subsurface/Game1.cs +++ b/Subsurface/Game1.cs @@ -30,6 +30,8 @@ namespace Subsurface public static EditMapScreen EditMapScreen; public static EditCharacterScreen EditCharacterScreen; + public static Level Level; + public static GameSession GameSession; public static GameClient Client; diff --git a/Subsurface/Map/Level.cs b/Subsurface/Map/Level.cs new file mode 100644 index 000000000..039baa400 --- /dev/null +++ b/Subsurface/Map/Level.cs @@ -0,0 +1,135 @@ +using FarseerPhysics; +using FarseerPhysics.Common; +using FarseerPhysics.Dynamics; +using FarseerPhysics.Factories; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using Voronoi2; + +namespace Subsurface +{ + class Level + { + private int seed; + + List bodies; + List cells; + + public Level(int seed, int siteCount, int width, int height) + { + this.seed = seed; + + Voronoi voronoi = new Voronoi(1.0); + + List sites = new List(); + Random rand = new Random(seed); + + for (int i = 0; i < siteCount; i++) + { + sites.Add(new PointF((float)(rand.NextDouble() * width), (float)(rand.NextDouble() * width))); + } + + List graphEdges = MakeVoronoiGraph(sites, voronoi, width, height); + + cells = new List(); + foreach (GraphEdge ge in graphEdges) + { + for (int i = 0; i<2; i++) + { + int site = (i==0) ? ge.site1 : ge.site2; + VoronoiCell cell = cells.Find(c => c.site == site); + if (cell == null) + { + cell = new VoronoiCell(site); + cells.Add(cell); + } + if (!cell.edges.Contains(ge)) cell.edges.Add(ge); + } + } + + + bodies = new List(); + foreach (VoronoiCell cell in cells) + { + //List of vectors defining my custom poly + List vlist = new List(); + foreach (GraphEdge ge in cell.edges) + { + if (vlist.Contains(ge.point1)) continue; + vlist.Add(ge.point1); + } + + if (vlist.Count < 2) continue; + + for (int i = 0; i < vlist.Count; i++ ) + { + vlist[i] = ConvertUnits.ToSimUnits(vlist[i]); + } + + + //get farseer 'vertices' from vectors + Vertices _shapevertices = new Vertices(vlist); + //_shapevertices.Sort(less); + + //feed vertices array to BodyFactory.CreatePolygon to get a new farseer polygonal body + Body _newBody = BodyFactory.CreatePolygon(Game1.world, _shapevertices, 15); + _newBody.BodyType = BodyType.Static; + _newBody.CollisionCategories = Physics.CollisionWall; + + bodies.Add(_newBody); + } + } + + + public int Compare(Vector2 a, Vector2 b, Vector2 center) + { + if (a.X - center.X >= 0 && b.X - center.X < 0) return 1; + if (a.X - center.X < 0 && b.X - center.X >= 0) return -1; + if (a.X - center.X == 0 && b.X - center.X == 0) + { + if (a.Y - center.Y >= 0 || b.Y - center.Y >= 0) return Math.Sign(a.Y - b.Y); + return Math.Sign(b.Y-a.Y); + } + + // compute the cross product of vectors (center -> a) x (center -> b) + float det = (a.X - center.X) * (b.Y - center.Y) - (b.X - center.X) * (a.Y - center.Y); + if (det < 0) return 1; + if (det > 0) return -1; + + // points a and b are on the same line from the center + // check which point is closer to the center + float d1 = (a.X - center.X) * (a.X - center.X) + (a.Y - center.Y) * (a.Y - center.Y); + float d2 = (b.X - center.X) * (b.X - center.X) + (b.Y - center.Y) * (b.Y - center.Y); + return Math.Sign(d1 - d2); + } + + List MakeVoronoiGraph(List sites, Voronoi voronoi, int width, int height) + { + double[] xVal = new double[sites.Count]; + double[] yVal = new double[sites.Count]; + for (int i = 0; i < sites.Count; i++) + { + xVal[i] = sites[i].X; + yVal[i] = sites[i].Y; + } + return voronoi.generateVoronoi(xVal, yVal, 0, width, 0, height); + } + + public void Render(SpriteBatch spriteBatch) + { + foreach (VoronoiCell cell in cells) + { + for (int i = 0; i + /// Description of Voronoi. + /// + public class Voronoi + { + // ************* Private members ****************** + double borderMinX, borderMaxX, borderMinY, borderMaxY; + int siteidx; + double xmin, xmax, ymin, ymax, deltax, deltay; + int nvertices; + int nedges; + int nsites; + Site[] sites; + Site bottomsite; + int sqrt_nsites; + double minDistanceBetweenSites; + int PQcount; + int PQmin; + int PQhashsize; + Halfedge[] PQhash; + + const int LE = 0; + const int RE = 1; + + int ELhashsize; + Halfedge[] ELhash; + Halfedge ELleftend, ELrightend; + List allEdges; + + + // ************* Public methods ****************** + // ****************************************** + + // constructor + public Voronoi ( double minDistanceBetweenSites ) + { + siteidx = 0; + sites = null; + + allEdges = null; + this.minDistanceBetweenSites = minDistanceBetweenSites; + } + + /** + * + * @param xValuesIn Array of X values for each site. + * @param yValuesIn Array of Y values for each site. Must be identical length to yValuesIn + * @param minX The minimum X of the bounding box around the voronoi + * @param maxX The maximum X of the bounding box around the voronoi + * @param minY The minimum Y of the bounding box around the voronoi + * @param maxY The maximum Y of the bounding box around the voronoi + * @return + */ + // تستدعى هذه العملية لإنشاء مخطط فورونوي + public List generateVoronoi ( double[] xValuesIn, double[] yValuesIn, double minX, double maxX, double minY, double maxY ) + { + sort(xValuesIn, yValuesIn, xValuesIn.Length); + + // Check bounding box inputs - if mins are bigger than maxes, swap them + double temp = 0; + if ( minX > maxX ) + { + temp = minX; + minX = maxX; + maxX = temp; + } + if ( minY > maxY ) + { + temp = minY; + minY = maxY; + maxY = temp; + } + + borderMinX = minX; + borderMinY = minY; + borderMaxX = maxX; + borderMaxY = maxY; + + siteidx = 0; + voronoi_bd (); + return allEdges; + } + + + /********************************************************* + * Private methods - implementation details + ********************************************************/ + + private void sort ( double[] xValuesIn, double[] yValuesIn, int count ) + { + sites = null; + allEdges = new List(); + + nsites = count; + nvertices = 0; + nedges = 0; + + double sn = (double)nsites + 4; + sqrt_nsites = (int) Math.Sqrt ( sn ); + + // Copy the inputs so we don't modify the originals + double[] xValues = new double[count]; + double[] yValues = new double[count]; + for (int i = 0; i < count; i++) + { + xValues[i] = xValuesIn[i]; + yValues[i] = yValuesIn[i]; + } + sortNode ( xValues, yValues, count ); + } + + private void qsort ( Site[] sites ) + { + List listSites = new List( sites.Length ); + for ( int i = 0; i < sites.Length; i++ ) + { + listSites.Add ( sites[i] ); + } + + listSites.Sort ( new SiteSorterYX () ); + + // Copy back into the array + for (int i=0; i < sites.Length; i++) + { + sites[i] = listSites[i]; + } + } + + private void sortNode ( double[] xValues, double[] yValues, int numPoints ) + { + nsites = numPoints; + sites = new Site[nsites]; + xmin = xValues[0]; + ymin = yValues[0]; + xmax = xValues[0]; + ymax = yValues[0]; + + for ( int i = 0; i < nsites; i++ ) + { + sites[i] = new Site(); + sites[i].coord.setPoint ( xValues[i], yValues[i] ); + sites[i].sitenbr = i; + + if ( xValues[i] < xmin ) + xmin = xValues[i]; + else if ( xValues[i] > xmax ) + xmax = xValues[i]; + + if ( yValues[i] < ymin ) + ymin = yValues[i]; + else if ( yValues[i] > ymax ) + ymax = yValues[i]; + } + + qsort ( sites ); + deltax = xmax - xmin; + deltay = ymax - ymin; + } + + private Site nextone () + { + Site s; + if ( siteidx < nsites ) + { + s = sites[siteidx]; + siteidx++; + return s; + } + return null; + } + + private Edge bisect ( Site s1, Site s2 ) + { + double dx, dy, adx, ady; + Edge newedge; + + newedge = new Edge(); + + newedge.reg[0] = s1; + newedge.reg[1] = s2; + + newedge.ep [0] = null; + newedge.ep[1] = null; + + dx = s2.coord.x - s1.coord.x; + dy = s2.coord.y - s1.coord.y; + + adx = dx > 0 ? dx : -dx; + ady = dy > 0 ? dy : -dy; + newedge.c = (double)(s1.coord.x * dx + s1.coord.y * dy + (dx * dx + dy* dy) * 0.5); + + if ( adx > ady ) + { + newedge.a = 1.0; + newedge.b = dy / dx; + newedge.c /= dx; + } + else + { + newedge.a = dx / dy; + newedge.b = 1.0; + newedge.c /= dy; + } + + newedge.edgenbr = nedges; + nedges++; + + return newedge; + } + + private void makevertex ( Site v ) + { + v.sitenbr = nvertices; + nvertices++; + } + + private bool PQinitialize () + { + PQcount = 0; + PQmin = 0; + PQhashsize = 4 * sqrt_nsites; + PQhash = new Halfedge[ PQhashsize ]; + + for ( int i = 0; i < PQhashsize; i++ ) + { + PQhash [i] = new Halfedge(); + } + return true; + } + + private int PQbucket ( Halfedge he ) + { + int bucket; + + bucket = (int) ((he.ystar - ymin) / deltay * PQhashsize); + if ( bucket < 0 ) + bucket = 0; + if ( bucket >= PQhashsize ) + bucket = PQhashsize - 1; + if ( bucket < PQmin ) + PQmin = bucket; + + return bucket; + } + + // push the HalfEdge into the ordered linked list of vertices + private void PQinsert ( Halfedge he, Site v, double offset ) + { + Halfedge last, next; + + he.vertex = v; + he.ystar = (double)(v.coord.y + offset); + last = PQhash [ PQbucket (he) ]; + + while + ( + (next = last.PQnext) != null + && + (he.ystar > next.ystar || (he.ystar == next.ystar && v.coord.x > next.vertex.coord.x)) + ) + { + last = next; + } + + he.PQnext = last.PQnext; + last.PQnext = he; + PQcount++; + } + + // remove the HalfEdge from the list of vertices + private void PQdelete ( Halfedge he ) + { + Halfedge last; + + if (he.vertex != null) + { + last = PQhash [ PQbucket (he) ]; + while ( last.PQnext != he ) + { + last = last.PQnext; + } + + last.PQnext = he.PQnext; + PQcount--; + he.vertex = null; + } + } + + private bool PQempty () + { + return ( PQcount == 0 ); + } + + private Point PQ_min () + { + Point answer = new Point (); + + while ( PQhash[PQmin].PQnext == null ) + { + PQmin++; + } + + answer.x = PQhash[PQmin].PQnext.vertex.coord.x; + answer.y = PQhash[PQmin].PQnext.ystar; + return answer; + } + + private Halfedge PQextractmin () + { + Halfedge curr; + + curr = PQhash[PQmin].PQnext; + PQhash[PQmin].PQnext = curr.PQnext; + PQcount--; + + return curr; + } + + private Halfedge HEcreate(Edge e, int pm) + { + Halfedge answer = new Halfedge(); + answer.ELedge = e; + answer.ELpm = pm; + answer.PQnext = null; + answer.vertex = null; + + return answer; + } + + private bool ELinitialize() + { + ELhashsize = 2 * sqrt_nsites; + ELhash = new Halfedge[ELhashsize]; + + for (int i = 0; i < ELhashsize; i++) + { + ELhash[i] = null; + } + + ELleftend = HEcreate ( null, 0 ); + ELrightend = HEcreate ( null, 0 ); + ELleftend.ELleft = null; + ELleftend.ELright = ELrightend; + ELrightend.ELleft = ELleftend; + ELrightend.ELright = null; + ELhash[0] = ELleftend; + ELhash[ELhashsize - 1] = ELrightend; + + return true; + } + + private Halfedge ELright( Halfedge he ) + { + return he.ELright; + } + + private Halfedge ELleft( Halfedge he ) + { + return he.ELleft; + } + + private Site leftreg( Halfedge he ) + { + if (he.ELedge == null) + { + return bottomsite; + } + return (he.ELpm == LE ? he.ELedge.reg[LE] : he.ELedge.reg[RE]); + } + + private void ELinsert( Halfedge lb, Halfedge newHe ) + { + newHe.ELleft = lb; + newHe.ELright = lb.ELright; + (lb.ELright).ELleft = newHe; + lb.ELright = newHe; + } + + /* + * This delete routine can't reclaim node, since pointers from hash table + * may be present. + */ + private void ELdelete( Halfedge he ) + { + (he.ELleft).ELright = he.ELright; + (he.ELright).ELleft = he.ELleft; + he.deleted = true; + } + + /* Get entry from hash table, pruning any deleted nodes */ + private Halfedge ELgethash( int b ) + { + Halfedge he; + if (b < 0 || b >= ELhashsize) + return null; + + he = ELhash[b]; + if (he == null || !he.deleted ) + return he; + + /* Hash table points to deleted half edge. Patch as necessary. */ + ELhash[b] = null; + return null; + } + + private Halfedge ELleftbnd( Point p ) + { + int bucket; + Halfedge he; + + /* Use hash table to get close to desired halfedge */ + // use the hash function to find the place in the hash map that this + // HalfEdge should be + bucket = (int) ((p.x - xmin) / deltax * ELhashsize); + + // make sure that the bucket position is within the range of the hash + // array + if ( bucket < 0 ) bucket = 0; + if ( bucket >= ELhashsize ) bucket = ELhashsize - 1; + + he = ELgethash ( bucket ); + + // if the HE isn't found, search backwards and forwards in the hash map + // for the first non-null entry + if ( he == null ) + { + for ( int i = 1; i < ELhashsize; i++ ) + { + if ( (he = ELgethash ( bucket - i ) ) != null ) + break; + if ( (he = ELgethash ( bucket + i ) ) != null ) + break; + } + } + + /* Now search linear list of halfedges for the correct one */ + if ( he == ELleftend || ( he != ELrightend && right_of (he, p) ) ) + { + // keep going right on the list until either the end is reached, or + // you find the 1st edge which the point isn't to the right of + do + { + he = he.ELright; + } + while ( he != ELrightend && right_of(he, p) ); + he = he.ELleft; + } + else + // if the point is to the left of the HalfEdge, then search left for + // the HE just to the left of the point + { + do + { + he = he.ELleft; + } + while ( he != ELleftend && !right_of(he, p) ); + } + + /* Update hash table and reference counts */ + if ( bucket > 0 && bucket < ELhashsize - 1) + { + ELhash[bucket] = he; + } + + return he; + } + + private void pushGraphEdge( Site leftSite, Site rightSite, Vector2 point1, Vector2 point2 ) + { + GraphEdge newEdge = new GraphEdge (); + allEdges.Add ( newEdge ); + newEdge.point1 = point1; + newEdge.point2 = point2; + + newEdge.site1 = leftSite.sitenbr; + newEdge.site2 = rightSite.sitenbr; + } + + private void clip_line( Edge e ) + { + double pxmin, pxmax, pymin, pymax; + Site s1, s2; + + double x1 = e.reg[0].coord.x; + double y1 = e.reg[0].coord.y; + double x2 = e.reg[1].coord.x; + double y2 = e.reg[1].coord.y; + double x = x2- x1; + double y = y2 - y1; + + // if the distance between the two points this line was created from is + // less than the square root of 2 عن جد؟, then ignore it + if ( Math.Sqrt ( (x*x) + (y*y) ) < minDistanceBetweenSites ) + { + return; + } + pxmin = borderMinX; + pymin = borderMinY; + pxmax = borderMaxX; + pymax = borderMaxY; + + if ( e.a == 1.0 && e.b >= 0.0 ) + { + s1 = e.ep[1]; + s2 = e.ep[0]; + } + else + { + s1 = e.ep[0]; + s2 = e.ep[1]; + } + + if ( e.a == 1.0 ) + { + y1 = pymin; + + if ( s1 != null && s1.coord.y > pymin ) + y1 = s1.coord.y; + if ( y1 > pymax ) + y1 = pymax; + x1 = e.c - e.b * y1; + y2 = pymax; + + if ( s2 != null && s2.coord.y < pymax ) + y2 = s2.coord.y; + if ( y2 < pymin ) + y2 = pymin; + x2 = e.c - e.b * y2; + if ( ( (x1 > pxmax) & (x2 > pxmax) ) | ( (x1 < pxmin) & (x2 < pxmin) ) ) + return; + + if ( x1 > pxmax ) + { + x1 = pxmax; + y1 = ( e.c - x1 ) / e.b; + } + if ( x1 < pxmin ) + { + x1 = pxmin; + y1 = ( e.c - x1 ) / e.b; + } + if ( x2 > pxmax ) + { + x2 = pxmax; + y2 = ( e.c - x2 ) / e.b; + } + if ( x2 < pxmin ) + { + x2 = pxmin; + y2 = ( e.c - x2 ) / e.b; + } + + } + else + { + x1 = pxmin; + if ( s1 != null && s1.coord.x > pxmin ) + x1 = s1.coord.x; + if ( x1 > pxmax ) + x1 = pxmax; + y1 = e.c - e.a * x1; + + x2 = pxmax; + if ( s2 != null && s2.coord.x < pxmax ) + x2 = s2.coord.x; + if ( x2 < pxmin ) + x2 = pxmin; + y2 = e.c - e.a * x2; + + if (((y1 > pymax) & (y2 > pymax)) | ((y1 < pymin) & (y2 < pymin))) + return; + + if ( y1 > pymax ) + { + y1 = pymax; + x1 = ( e.c - y1 ) / e.a; + } + if ( y1 < pymin ) + { + y1 = pymin; + x1 = ( e.c - y1 ) / e.a; + } + if ( y2 > pymax ) + { + y2 = pymax; + x2 = ( e.c - y2 ) / e.a; + } + if ( y2 < pymin ) + { + y2 = pymin; + x2 = ( e.c - y2 ) / e.a; + } + } + + pushGraphEdge(e.reg[0], e.reg[1], new Vector2((float)x1, (float)y1), new Vector2((float)x2, (float)y2)); + } + + private void endpoint( Edge e, int lr, Site s ) + { + e.ep[lr] = s; + if ( e.ep[RE - lr] == null ) + return; + clip_line ( e ); + } + + /* returns true if p is to right of halfedge e */ + private bool right_of(Halfedge el, Point p) + { + Edge e; + Site topsite; + bool right_of_site; + bool above, fast; + double dxp, dyp, dxs, t1, t2, t3, yl; + + e = el.ELedge; + topsite = e.reg[1]; + + if ( p.x > topsite.coord.x ) + right_of_site = true; + else + right_of_site = false; + + if ( right_of_site && el.ELpm == LE ) + return true; + if (!right_of_site && el.ELpm == RE ) + return false; + + if ( e.a == 1.0 ) + { + dxp = p.x - topsite.coord.x; + dyp = p.y - topsite.coord.y; + fast = false; + + if ( (!right_of_site & (e.b < 0.0)) | (right_of_site & (e.b >= 0.0)) ) + { + above = dyp >= e.b * dxp; + fast = above; + } + else + { + above = p.x + p.y * e.b > e.c; + if ( e.b < 0.0 ) + above = !above; + if ( !above ) + fast = true; + } + if ( !fast ) + { + dxs = topsite.coord.x - ( e.reg[0] ).coord.x; + above = e.b * (dxp * dxp - dyp * dyp) + < dxs * dyp * (1.0 + 2.0 * dxp / dxs + e.b * e.b); + + if ( e.b < 0 ) + above = !above; + } + } + else // e.b == 1.0 + { + yl = e.c - e.a * p.x; + t1 = p.y - yl; + t2 = p.x - topsite.coord.x; + t3 = yl - topsite.coord.y; + above = t1 * t1 > t2 * t2 + t3 * t3; + } + return ( el.ELpm == LE ? above : !above ); + } + + private Site rightreg(Halfedge he) + { + if (he.ELedge == (Edge) null) + // if this halfedge has no edge, return the bottom site (whatever + // that is) + { + return (bottomsite); + } + + // if the ELpm field is zero, return the site 0 that this edge bisects, + // otherwise return site number 1 + return (he.ELpm == LE ? he.ELedge.reg[RE] : he.ELedge.reg[LE]); + } + + private double dist( Site s, Site t ) + { + double dx, dy; + dx = s.coord.x - t.coord.x; + dy = s.coord.y - t.coord.y; + return Math.Sqrt ( dx * dx + dy * dy ); + } + + // create a new site where the HalfEdges el1 and el2 intersect - note that + // the Point in the argument list is not used, don't know why it's there + private Site intersect( Halfedge el1, Halfedge el2 ) + { + Edge e1, e2, e; + Halfedge el; + double d, xint, yint; + bool right_of_site; + Site v; // vertex + + e1 = el1.ELedge; + e2 = el2.ELedge; + + if ( e1 == null || e2 == null ) + return null; + + // if the two edges bisect the same parent, return null + if ( e1.reg[1] == e2.reg[1] ) + return null; + + d = e1.a * e2.b - e1.b * e2.a; + if ( -1.0e-10 < d && d < 1.0e-10 ) + return null; + + xint = ( e1.c * e2.b - e2.c * e1.b ) / d; + yint = ( e2.c * e1.a - e1.c * e2.a ) / d; + + if ( (e1.reg[1].coord.y < e2.reg[1].coord.y) + || (e1.reg[1].coord.y == e2.reg[1].coord.y && e1.reg[1].coord.x < e2.reg[1].coord.x) ) + { + el = el1; + e = e1; + } + else + { + el = el2; + e = e2; + } + + right_of_site = xint >= e.reg[1].coord.x; + if ((right_of_site && el.ELpm == LE) + || (!right_of_site && el.ELpm == RE)) + return null; + + // create a new site at the point of intersection - this is a new vector + // event waiting to happen + v = new Site(); + v.coord.x = xint; + v.coord.y = yint; + return v; + } + + /* + * implicit parameters: nsites, sqrt_nsites, xmin, xmax, ymin, ymax, deltax, + * deltay (can all be estimates). Performance suffers if they are wrong; + * better to make nsites, deltax, and deltay too big than too small. (?) + */ + private bool voronoi_bd() + { + Site newsite, bot, top, temp, p; + Site v; + Point newintstar = null; + int pm; + Halfedge lbnd, rbnd, llbnd, rrbnd, bisector; + Edge e; + + PQinitialize(); + ELinitialize(); + + bottomsite = nextone(); + newsite = nextone(); + while (true) + { + if (!PQempty()) + { + newintstar = PQ_min(); + } + // if the lowest site has a smaller y value than the lowest vector + // intersection, + // process the site otherwise process the vector intersection + + if (newsite != null && (PQempty() + || newsite.coord.y < newintstar.y + || (newsite.coord.y == newintstar.y + && newsite.coord.x < newintstar.x))) + { + /* new site is smallest -this is a site event */ + // get the first HalfEdge to the LEFT of the new site + lbnd = ELleftbnd((newsite.coord)); + // get the first HalfEdge to the RIGHT of the new site + rbnd = ELright(lbnd); + // if this halfedge has no edge,bot =bottom site (whatever that + // is) + bot = rightreg(lbnd); + // create a new edge that bisects + e = bisect(bot, newsite); + + // create a new HalfEdge, setting its ELpm field to 0 + bisector = HEcreate(e, LE); + // insert this new bisector edge between the left and right + // vectors in a linked list + ELinsert(lbnd, bisector); + + // if the new bisector intersects with the left edge, + // remove the left edge's vertex, and put in the new one + if ((p = intersect(lbnd, bisector)) != null) + { + PQdelete(lbnd); + PQinsert(lbnd, p, dist(p, newsite)); + } + lbnd = bisector; + // create a new HalfEdge, setting its ELpm field to 1 + bisector = HEcreate(e, RE); + // insert the new HE to the right of the original bisector + // earlier in the IF stmt + ELinsert(lbnd, bisector); + + // if this new bisector intersects with the new HalfEdge + if ((p = intersect(bisector, rbnd)) != null) + { + // push the HE into the ordered linked list of vertices + PQinsert(bisector, p, dist(p, newsite)); + } + newsite = nextone(); + } else if (!PQempty()) + /* intersection is smallest - this is a vector event */ + { + // pop the HalfEdge with the lowest vector off the ordered list + // of vectors + lbnd = PQextractmin(); + // get the HalfEdge to the left of the above HE + llbnd = ELleft(lbnd); + // get the HalfEdge to the right of the above HE + rbnd = ELright(lbnd); + // get the HalfEdge to the right of the HE to the right of the + // lowest HE + rrbnd = ELright(rbnd); + // get the Site to the left of the left HE which it bisects + bot = leftreg(lbnd); + // get the Site to the right of the right HE which it bisects + top = rightreg(rbnd); + + v = lbnd.vertex; // get the vertex that caused this event + makevertex(v); // set the vertex number - couldn't do this + // earlier since we didn't know when it would be processed + endpoint(lbnd.ELedge, lbnd.ELpm, v); + // set the endpoint of + // the left HalfEdge to be this vector + endpoint(rbnd.ELedge, rbnd.ELpm, v); + // set the endpoint of the right HalfEdge to + // be this vector + ELdelete(lbnd); // mark the lowest HE for + // deletion - can't delete yet because there might be pointers + // to it in Hash Map + PQdelete(rbnd); + // remove all vertex events to do with the right HE + ELdelete(rbnd); // mark the right HE for + // deletion - can't delete yet because there might be pointers + // to it in Hash Map + pm = LE; // set the pm variable to zero + + if (bot.coord.y > top.coord.y) + // if the site to the left of the event is higher than the + // Site + { // to the right of it, then swap them and set the 'pm' + // variable to 1 + temp = bot; + bot = top; + top = temp; + pm = RE; + } + e = bisect(bot, top); // create an Edge (or line) + // that is between the two Sites. This creates the formula of + // the line, and assigns a line number to it + bisector = HEcreate(e, pm); // create a HE from the Edge 'e', + // and make it point to that edge + // with its ELedge field + ELinsert(llbnd, bisector); // insert the new bisector to the + // right of the left HE + endpoint(e, RE - pm, v); // set one endpoint to the new edge + // to be the vector point 'v'. + // If the site to the left of this bisector is higher than the + // right Site, then this endpoint + // is put in position 0; otherwise in pos 1 + + // if left HE and the new bisector intersect, then delete + // the left HE, and reinsert it + if ((p = intersect(llbnd, bisector)) != null) + { + PQdelete(llbnd); + PQinsert(llbnd, p, dist(p, bot)); + } + + // if right HE and the new bisector intersect, then + // reinsert it + if ((p = intersect(bisector, rrbnd)) != null) + { + PQinsert(bisector, p, dist(p, bot)); + } + } else + { + break; + } + } + + for (lbnd = ELright(ELleftend); lbnd != ELrightend; lbnd = ELright(lbnd)) + { + e = lbnd.ELedge; + clip_line(e); + } + + return true; + } + + } // Voronoi Class End +} // namespace Voronoi2 End \ No newline at end of file diff --git a/Subsurface/Map/VoronoiElements.cs b/Subsurface/Map/VoronoiElements.cs new file mode 100644 index 000000000..d5e933de0 --- /dev/null +++ b/Subsurface/Map/VoronoiElements.cs @@ -0,0 +1,149 @@ +/* + * Created by SharpDevelop. + * User: Burhan + * Date: 17/06/2014 + * Time: 09:29 م + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +/* + Copyright 2011 James Humphreys. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are + permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY James Humphreys ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + The views and conclusions contained in the software and documentation are those of the + authors and should not be interpreted as representing official policies, either expressed + or implied, of James Humphreys. + */ + +/* + * C# Version by Burhan Joukhadar + * + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ + + +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; + +namespace Voronoi2 +{ + public class Point + { + public double x, y; + + public Point () + { + } + + public void setPoint ( double x, double y ) + { + this.x = x; + this.y = y; + } + } + + // use for sites and vertecies + public class Site + { + public Point coord; + public int sitenbr; + + public Site () + { + coord = new Point(); + } + } + + public class Edge + { + public double a = 0, b = 0, c = 0; + public Site[] ep; + public Site[] reg; + public int edgenbr; + + public Edge () + { + ep = new Site[2]; + reg = new Site[2]; + } + } + + + public class Halfedge + { + public Halfedge ELleft, ELright; + public Edge ELedge; + public bool deleted; + public int ELpm; + public Site vertex; + public double ystar; + public Halfedge PQnext; + + public Halfedge () + { + PQnext = null; + } + } + + public class VoronoiCell + { + public List edges; + public int site; + + public VoronoiCell(int site) + { + edges = new List(); + this.site = site; + } + } + + public class GraphEdge + { + public Vector2 point1, point2; + public int site1, site2; + } + + // للترتيب + public class SiteSorterYX : IComparer + { + public int Compare ( Site p1, Site p2 ) + { + Point s1 = p1.coord; + Point s2 = p2.coord; + if ( s1.y < s2.y ) return -1; + if ( s1.y > s2.y ) return 1; + if ( s1.x < s2.x ) return -1; + if ( s1.x > s2.x ) return 1; + return 0; + } + } +} diff --git a/Subsurface/Screens/GameScreen.cs b/Subsurface/Screens/GameScreen.cs index 989b60c86..6d7a8ee63 100644 --- a/Subsurface/Screens/GameScreen.cs +++ b/Subsurface/Screens/GameScreen.cs @@ -183,6 +183,8 @@ namespace Subsurface BlendState.AlphaBlend, null, null, null, null, cam.Transform); + + if (Game1.Level!=null) Game1.Level.Render(spriteBatch); Map.DrawBack(spriteBatch); diff --git a/Subsurface/Subsurface.csproj b/Subsurface/Subsurface.csproj index 35706e91c..620cadf7e 100644 --- a/Subsurface/Subsurface.csproj +++ b/Subsurface/Subsurface.csproj @@ -122,9 +122,12 @@ + + + diff --git a/Subsurface/Subsurface.csproj.user b/Subsurface/Subsurface.csproj.user index 505c3a0bf..693505ea4 100644 --- a/Subsurface/Subsurface.csproj.user +++ b/Subsurface/Subsurface.csproj.user @@ -9,6 +9,6 @@ en-US false - ProjectFiles + ShowAllFiles \ No newline at end of file diff --git a/Subsurface_Solution.v12.suo b/Subsurface_Solution.v12.suo index b61cebe5ae2863ef0d94656f25ebff8beda5e86a..14e4973e3b3950464b5a7592adec90cf54d3f31b 100644 GIT binary patch delta 7751 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 delta 5322 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