// Improve the random set of points with Lloyd Relaxation. private void ImprovePoints() { // We'd really like to generate "blue noise". Algorithms: // 1. Poisson dart throwing: check each new point against all // existing points, and reject it if it's too close. // 2. Start with a hexagonal grid and randomly perturb points. // 3. Lloyd Relaxation: move each point to the centroid of the // generated Voronoi polygon, then generate Voronoi again. // 4. Use force-based layout algorithms to push points away. // 5. More at http://www.cs.virginia.edu/~gfx/pubs/antimony/ // Option 3 is implemented here. If it's run for too many iterations, // it will turn into a grid, but convergence is very slow, and we only // run it a few times. var newPoints = new List <Vector>(); foreach (var key in _provinces.Keys) { var provincePoints = _provinces[key]; var newpoint = new Vector(2); foreach (var point in provincePoints) { newpoint.Add(point); } newpoint[0] /= provincePoints.Count; newpoint[1] /= provincePoints.Count; newpoint[0] = (newpoint[0] + key[0]) / 2; newpoint[1] = (newpoint[1] + key[1]) / 2; newPoints.Add(newpoint); } var height = Convert.ToInt32(txtHeight.Value); var width = Convert.ToInt32(txtWidth.Value); var points = Convert.ToInt32(txtPoints.Value); OrderedPoints = newPoints .Select(WrapVector) .OrderBy(v => String.Format("{0:000000000}{1:000000000}", height - (int)v[1], (int)v[0])) .ToList(); for (var x = -1; x <= 1; x++) { for (var y = -1; y <= 1; y++) { if (x == 0 && y == 0) { continue; } for (var i = 0; i < points; i++) { var point = newPoints[i]; newPoints.Add(new Vector(point[0] + (x * width), point[1] + (y * height))); } } } _pointList = newPoints; //Text = _pointList.Count.ToString(); _provinces = null; picDiagram.Image = new Bitmap(width, height); Map.Neighbours = new MultiValueDictionary <int, int>(); }
private void GenerateProvinces() { _rightMatrix = new Matrix(); _rightMatrix.Translate(Width(), 0); _leftMatric = new Matrix(); _leftMatric.Translate(-1 * Width(), 0); _downMatrix = new Matrix(); _downMatrix.Translate(0, Height()); _upMatrix = new Matrix(); _upMatrix.Translate(0, -1 * Height()); Map = new Dominons3Map(); Map.MapImage = picDiagram.Image; Map.Title = "Test Map"; Map.Filename = "TestMap"; Map.IsWrapAround = true; Map.Description = "Description of Dom 3 Map."; Map.ProvinceCount = OrderedPoints.Count; var neighbours = new MultiValueDictionary <Vector, Vector>(); _provinces = new MultiValueDictionary <Vector, Vector>(); var height = Convert.ToInt32(txtHeight.Value); var width = Convert.ToInt32(txtWidth.Value); _indexToPath.Clear(); MapBounds = new Rectangle(0, 0, Width(), Height()); _borderPointX = new PointF(Width(), 0); _borderPointEdge = new PointF(Width(), Height()); _borderPointY = new PointF(0, Height()); var visibleEdges = _graph.Edges .Cast <VoronoiEdge>() .Where(edge => IsVertexValid(edge.VVertexA) && IsVertexValid(edge.VVertexB) && (MapBounds.ContainsEdge(edge) || MapBounds.IntersectsEdge(edge))) .ToArray(); foreach (var edge in visibleEdges) { var segmentId = GetSegmentId(edge); if (!_indexToPath.ContainsKey(segmentId)) { _indexToPath[segmentId] = GenerateJaggedSegment(edge); } } foreach (VoronoiEdge edge1 in _graph.Edges) { if (!IsOutofBounds(height, width, edge1.LeftData) && !IsOutofBounds(height, width, edge1.RightData)) { neighbours.Add(edge1.LeftData, edge1.RightData); neighbours.Add(edge1.RightData, edge1.LeftData); var leftProvinceId = OrderedPoints.IndexOf(edge1.LeftData) + 1; var rightProvinceId = OrderedPoints.IndexOf(edge1.RightData) + 1; Map.Neighbours.Add(leftProvinceId, rightProvinceId); Map.Neighbours.Add(rightProvinceId, leftProvinceId); } if (!IsOutofBounds(height, width, edge1.LeftData) && IsOutofBounds(height, width, edge1.RightData)) { var leftProvinceId = OrderedPoints.IndexOf(edge1.LeftData) + 1; var rightProvinceId = OrderedPoints.IndexOf(WrapVector(edge1.RightData)) + 1; Map.Neighbours.Add(leftProvinceId, rightProvinceId); Map.Neighbours.Add(rightProvinceId, leftProvinceId); } if (IsOutofBounds(height, width, edge1.LeftData) && !IsOutofBounds(height, width, edge1.RightData)) { var leftProvinceId = OrderedPoints.IndexOf(WrapVector(edge1.LeftData)) + 1; var rightProvinceId = OrderedPoints.IndexOf(edge1.RightData) + 1; Map.Neighbours.Add(leftProvinceId, rightProvinceId); Map.Neighbours.Add(rightProvinceId, leftProvinceId); } if (!IsOutofBounds(height, width, edge1.LeftData)) { _provinces.Add(edge1.LeftData, edge1.VVertexA); _provinces.Add(edge1.LeftData, edge1.VVertexB); } if (!IsOutofBounds(height, width, edge1.RightData)) { _provinces.Add(edge1.RightData, edge1.VVertexA); _provinces.Add(edge1.RightData, edge1.VVertexB); } } for (int i = 0; i < OrderedPoints.Count; i++) { var provinceId = i + 1; Map.TerrainTypes[provinceId] = 0; } using (var g = Graphics.FromImage(picDiagram.Image)) { foreach (var key in _provinces.Keys.ToList()) { var list = _provinces[key]; var f = list.First(); Vector key1 = key; var points = list .Select(v => new { angle = GetAngle(f, key1, v), v }) .OrderBy(a => a.angle) .Select(a => a.v) .ToList(); _provinces[key] = new HashSet <Vector>(points); var randomBrush = GetRandomBrush(); g.FillPolygon(randomBrush, points.Select(v => v.ToPointF()).ToArray()); g.FillPolygon(randomBrush, points.Select(v => new PointF((float)v[0] + width, (float)v[1])).ToArray()); g.FillPolygon(randomBrush, points.Select(v => new PointF((float)v[0] - width, (float)v[1])).ToArray()); g.FillPolygon(randomBrush, points.Select(v => new PointF((float)v[0], (float)v[1] + height)).ToArray()); g.FillPolygon(randomBrush, points.Select(v => new PointF((float)v[0], (float)v[1] - height)).ToArray()); var provId = OrderedPoints.IndexOf(key); g.DrawString(provId.ToString(), _provFont, Brushes.Black, (float)key[0] + 5f, (float)key[1] + 5f); } } picDiagram.Invalidate(); }