/// <summary> /// Initializes a new instance of the <see cref="SharpNav.ContourVertex"/> struct. /// </summary> /// <param name="vec">The array of X,Y,Z coordinates.</param> /// <param name="region">The Region ID.</param> public ContourVertex(Vector3 vec, RegionId region) { this.X = (int)vec.X; this.Y = (int)vec.Y; this.Z = (int)vec.Z; this.RegionId = region; }
/// <summary> /// Initializes a new instance of the <see cref="ContourVertex"/> struct as a copy. /// </summary> /// <param name="vert">The original vertex.</param> /// <param name="index">The index of the original vertex, which is temporarily stored in the <see cref="RegionId"/> field.</param> public ContourVertex(ContourVertex vert, int index) { this.X = vert.X; this.Y = vert.Y; this.Z = vert.Z; this.RegionId = new RegionId(index); }
/// <summary> /// Initializes a new instance of the <see cref="ContourVertex"/> struct. /// </summary> /// <param name="x">The X coordinate.</param> /// <param name="y">The Y coordinate.</param> /// <param name="z">The Z coordinate.</param> /// <param name="region">The region ID.</param> public ContourVertex(int x, int y, int z, RegionId region) { this.X = x; this.Y = y; this.Z = z; this.RegionId = region; }
/// <summary> /// Initializes a new instance of the <see cref="CompactSpan"/> struct. /// </summary> /// <param name="minimum">The span minimum.</param> /// <param name="height">The number of voxels the span contains.</param> public CompactSpan(int minimum, int height) { this.Minimum = minimum; this.Height = height; this.ConnectionWest = NotConnected; this.ConnectionNorth = NotConnected; this.ConnectionEast = NotConnected; this.ConnectionSouth = NotConnected; this.Region = RegionId.Null; }
/// <summary> /// Initializes a new instance of the <see cref="Contour"/> class. /// </summary> /// <param name="verts">The raw vertices of the contour.</param> /// <param name="region">The region ID of the contour.</param> /// <param name="area">The area ID of the contour.</param> /// <param name="borderSize">The size of the border.</param> public Contour(List<ContourVertex> verts, RegionId region, Area area, int borderSize) { this.vertices = verts.ToArray(); this.regionId = region; this.area = area; //remove offset if (borderSize > 0) { for (int j = 0; j < vertices.Length; j++) { vertices[j].X -= borderSize; vertices[j].Z -= borderSize; } } }
public void LoadView(Type viewType, RegionId regionId) { var view = ServiceLocator.Instance[viewType]; switch (regionId) { case RegionId.CONTENT: { MessageBus.Instance.Publish(UXMessage.ASSIGN_CONTENT_REGION, view); break; } case RegionId.NINE_OCLOCK: { MessageBus.Instance.Publish(UXMessage.NINE_OCLOCK_REGION, view); break; } default: { throw new NotImplementedException(); } } }
private int[] vertices; //"numVertsPerPoly" elements #endregion Fields #region Constructors /// <summary> /// Initializes a new instance of the <see cref="Polygon" /> class. /// </summary> /// <param name="numVertsPerPoly">The number of vertices per polygon.</param> /// <param name="area">The AreaId</param> /// <param name="regionId">The RegionId</param> /// <param name="flags">Polygon flags</param> public Polygon(int numVertsPerPoly, Area area, RegionId regionId, int flags) { vertices = new int[numVertsPerPoly]; neighborEdges = new int[numVertsPerPoly]; this.area = area; this.regionId = regionId; this.flags = flags; for (int i = 0; i < numVertsPerPoly; i++) { vertices[i] = NullId; neighborEdges[i] = NullId; } }
/// <summary> /// Simplify the contours by reducing the number of edges /// </summary> /// <param name="rawVerts">Initial vertices</param> /// <param name="simplified">New and simplified vertices</param> /// <param name="maxError">Maximum error allowed</param> /// <param name="maxEdgeLen">The maximum edge length allowed</param> /// <param name="buildFlags">Flags determines how to split the long edges</param> public static void Simplify(List<ContourVertex> rawVerts, List<ContourVertex> simplified, float maxError, int maxEdgeLen, ContourBuildFlags buildFlags) { bool tesselateWallEdges = (buildFlags & ContourBuildFlags.TessellateWallEdges) == ContourBuildFlags.TessellateWallEdges; bool tesselateAreaEdges = (buildFlags & ContourBuildFlags.TessellateAreaEdges) == ContourBuildFlags.TessellateAreaEdges; //add initial points bool hasConnections = false; for (int i = 0; i < rawVerts.Count; i++) { if (rawVerts[i].RegionId.Id != 0) { hasConnections = true; break; } } if (hasConnections) { //contour has some portals to other regions //add new point to every location where region changes for (int i = 0, end = rawVerts.Count; i < end; i++) { int ii = (i + 1) % end; bool differentRegions = rawVerts[i].RegionId.Id != rawVerts[ii].RegionId.Id; bool areaBorders = RegionId.HasFlags(rawVerts[i].RegionId, RegionFlags.AreaBorder) != RegionId.HasFlags(rawVerts[ii].RegionId, RegionFlags.AreaBorder); if (differentRegions || areaBorders) { simplified.Add(new ContourVertex(rawVerts[i], i)); } } } //add some points if thhere are no connections if (simplified.Count == 0) { //find lower-left and upper-right vertices of contour int lowerLeftX = rawVerts[0].X; int lowerLeftY = rawVerts[0].Y; int lowerLeftZ = rawVerts[0].Z; RegionId lowerLeftI = RegionId.Null; int upperRightX = rawVerts[0].X; int upperRightY = rawVerts[0].Y; int upperRightZ = rawVerts[0].Z; RegionId upperRightI = RegionId.Null; //iterate through points for (int i = 0; i < rawVerts.Count; i++) { int x = rawVerts[i].X; int y = rawVerts[i].Y; int z = rawVerts[i].Z; if (x < lowerLeftX || (x == lowerLeftX && z < lowerLeftZ)) { lowerLeftX = x; lowerLeftY = y; lowerLeftZ = z; lowerLeftI = new RegionId(i); } if (x > upperRightX || (x == upperRightX && z > upperRightZ)) { upperRightX = x; upperRightY = y; upperRightZ = z; upperRightI = new RegionId(i); } } //save the points simplified.Add(new ContourVertex(lowerLeftX, lowerLeftY, lowerLeftZ, lowerLeftI)); simplified.Add(new ContourVertex(upperRightX, upperRightY, upperRightZ, upperRightI)); } //add points until all points are within error tolerance of simplified slope int numPoints = rawVerts.Count; for (int i = 0; i < simplified.Count;) { int ii = (i + 1) % simplified.Count; //obtain (x, z) coordinates, along with region id int ax = simplified[i].X; int az = simplified[i].Z; int ai = (int)simplified[i].RegionId; int bx = simplified[ii].X; int bz = simplified[ii].Z; int bi = (int)simplified[ii].RegionId; float maxDeviation = 0; int maxi = -1; int ci, countIncrement, endi; //traverse segment in lexilogical order (try to go from smallest to largest coordinates?) if (bx > ax || (bx == ax && bz > az)) { countIncrement = 1; ci = (int)(ai + countIncrement) % numPoints; endi = (int)bi; } else { countIncrement = numPoints - 1; ci = (int)(bi + countIncrement) % numPoints; endi = (int)ai; } //tessellate only outer edges or edges between areas if (rawVerts[ci].RegionId.Id == 0 || RegionId.HasFlags(rawVerts[ci].RegionId, RegionFlags.AreaBorder)) { //find the maximum deviation while (ci != endi) { float deviation = Distance.PointToSegment2DSquared(rawVerts[ci].X, rawVerts[ci].Z, ax, az, bx, bz); if (deviation > maxDeviation) { maxDeviation = deviation; maxi = ci; } ci = (ci + countIncrement) % numPoints; } } //If max deviation is larger than accepted error, add new point if (maxi != -1 && maxDeviation > (maxError * maxError)) { simplified.Insert(i + 1, new ContourVertex(rawVerts[maxi], maxi)); } else { i++; } } //split too long edges if (maxEdgeLen > 0 && (tesselateAreaEdges || tesselateWallEdges)) { for (int i = 0; i < simplified.Count;) { int ii = (i + 1) % simplified.Count; //get (x, z) coordinates along with region id int ax = simplified[i].X; int az = simplified[i].Z; int ai = (int)simplified[i].RegionId; int bx = simplified[ii].X; int bz = simplified[ii].Z; int bi = (int)simplified[ii].RegionId; //find maximum deviation from segment int maxi = -1; int ci = (int)(ai + 1) % numPoints; //tessellate only outer edges or edges between areas bool tess = false; //wall edges if (tesselateWallEdges && rawVerts[ci].RegionId.Id == 0) tess = true; //edges between areas if (tesselateAreaEdges && RegionId.HasFlags(rawVerts[ci].RegionId, RegionFlags.AreaBorder)) tess = true; if (tess) { int dx = bx - ax; int dz = bz - az; if (dx * dx + dz * dz > maxEdgeLen * maxEdgeLen) { //round based on lexilogical direction (smallest to largest cooridinates, first by x. //if x coordinates are equal, then compare z coordinates) int n = bi < ai ? (bi + numPoints - ai) : (bi - ai); if (n > 1) { if (bx > ax || (bx == ax && bz > az)) maxi = (int)(ai + n / 2) % numPoints; else maxi = (int)(ai + (n + 1) / 2) % numPoints; } } } //add new point if (maxi != -1) { simplified.Insert(i + 1, new ContourVertex(rawVerts[maxi], maxi)); } else { i++; } } } for (int i = 0; i < simplified.Count; i++) { ContourVertex sv = simplified[i]; //take edge vertex flag from current raw point and neighbor region from next raw point int ai = ((int)sv.RegionId + 1) % numPoints; RegionId bi = sv.RegionId; //save new region id sv.RegionId = RegionId.FromRawBits(((int)rawVerts[ai].RegionId & (RegionId.MaskId | (int)RegionFlags.AreaBorder)) | ((int)rawVerts[(int)bi].RegionId & (int)RegionFlags.VertexBorder)); simplified[i] = sv; } }
/// <summary> /// Initializes a new instance of the <see cref="ContourVertex"/> struct as a copy. /// </summary> /// <param name="vert">The original vertex.</param> /// <param name="region">The region that the vertex belongs to.</param> public ContourVertex(ContourVertex vert, RegionId region) { this.X = vert.X; this.Y = vert.Y; this.Z = vert.Z; this.RegionId = region; }
/// <summary> /// Merge two stacks to get a single stack. /// </summary> /// <param name="source">The original stack</param> /// <param name="destination">The new stack</param> /// <param name="regions">Region ids</param> private static void AppendStacks(List<CompactSpanReference> source, List<CompactSpanReference> destination, RegionId[] regions) { for (int j = 0; j < source.Count; j++) { var spanRef = source[j]; if (spanRef.Index < 0 || regions[spanRef.Index] != 0) continue; destination.Add(spanRef); } }
/// <summary> /// Try to visit all the spans. May be needed in filtering small regions. /// </summary> /// <param name="regions">an array of region values</param> /// <param name="spanRef">The span to start walking from.</param> /// <param name="dir">The direction to start walking in.</param> /// <param name="cont">A collection of regions to append to.</param> private void WalkContour(RegionId[] regions, CompactSpanReference spanRef, Direction dir, List<RegionId> cont) { Direction startDir = dir; int starti = spanRef.Index; CompactSpan ss = spans[starti]; RegionId curReg = RegionId.Null; if (ss.IsConnected(dir)) { int dx = spanRef.X + dir.GetHorizontalOffset(); int dy = spanRef.Y + dir.GetVerticalOffset(); int di = cells[dx + dy * width].StartIndex + CompactSpan.GetConnection(ref ss, dir); curReg = regions[di]; } cont.Add(curReg); int iter = 0; while (++iter < 40000) { CompactSpan s = spans[spanRef.Index]; if (IsSolidEdge(regions, ref spanRef, dir)) { //choose the edge corner RegionId r = RegionId.Null; if (s.IsConnected(dir)) { int dx = spanRef.X + dir.GetHorizontalOffset(); int dy = spanRef.Y + dir.GetVerticalOffset(); int di = cells[dx + dy * width].StartIndex + CompactSpan.GetConnection(ref s, dir); r = regions[di]; } if (r != curReg) { curReg = r; cont.Add(curReg); } dir = dir.NextClockwise(); //rotate clockwise } else { int di = -1; int dx = spanRef.X + dir.GetHorizontalOffset(); int dy = spanRef.Y + dir.GetVerticalOffset(); if (s.IsConnected(dir)) { CompactCell dc = cells[dx + dy * width]; di = dc.StartIndex + CompactSpan.GetConnection(ref s, dir); } if (di == -1) { //shouldn't happen return; } spanRef = new CompactSpanReference(dx, dy, di); dir = dir.NextCounterClockwise(); //rotate counterclockwise } if (starti == spanRef.Index && startDir == dir) break; } //remove adjacent duplicates if (cont.Count > 1) { for (int j = 0; j < cont.Count;) { //next element int nj = (j + 1) % cont.Count; //adjacent duplicate found if (cont[j] == cont[nj]) cont.RemoveAt(j); else j++; } } }
/// <summary> /// Checks whether the edge from a span in a direction is a solid edge. /// A solid edge is an edge between two regions. /// </summary> /// <param name="regions">The region ID array.</param> /// <param name="spanRef">A reference to the span connected to the edge.</param> /// <param name="dir">The direction of the edge.</param> /// <returns>A value indicating whether the described edge is solid.</returns> private bool IsSolidEdge(RegionId[] regions, ref CompactSpanReference spanRef, Direction dir) { CompactSpan s = spans[spanRef.Index]; RegionId r = RegionId.Null; if (s.IsConnected(dir)) { int dx = spanRef.X + dir.GetHorizontalOffset(); int dy = spanRef.Y + dir.GetVerticalOffset(); int di = cells[dx + dy * width].StartIndex + CompactSpan.GetConnection(ref s, dir); r = regions[di]; } if (r == regions[spanRef.Index]) return false; return true; }
/// <summary> /// Floods the regions at a certain level /// </summary> /// <param name="regions">source region</param> /// <param name="floodDistances">source distances</param> /// <param name="regionIndex">region id</param> /// <param name="level">current level</param> /// <param name="start">A reference to the starting span.</param> /// <returns>Always true.</returns> private bool FloodRegion(RegionId[] regions, int[] floodDistances, int regionIndex, int level, ref CompactSpanReference start) { //TODO this method should always return true, make it not return a bool? //flood fill mark region Stack<CompactSpanReference> stack = new Stack<CompactSpanReference>(); stack.Push(start); Area area = areas[start.Index]; regions[start.Index] = new RegionId(regionIndex); floodDistances[start.Index] = 0; int lev = level >= 2 ? level - 2 : 0; int count = 0; while (stack.Count > 0) { CompactSpanReference cell = stack.Pop(); CompactSpan cs = spans[cell.Index]; //check if any of the neighbors already have a valid reigon set RegionId ar = RegionId.Null; for (var dir = Direction.West; dir <= Direction.South; dir++) { //8 connected if (cs.IsConnected(dir)) { int dx = cell.X + dir.GetHorizontalOffset(); int dy = cell.Y + dir.GetVerticalOffset(); int di = cells[dx + dy * width].StartIndex + CompactSpan.GetConnection(ref cs, dir); if (areas[di] != area) continue; RegionId nr = regions[di]; if (RegionId.HasFlags(nr, RegionFlags.Border)) //skip borders continue; if (nr != 0 && nr != regionIndex) { ar = nr; break; } CompactSpan ds = spans[di]; Direction dir2 = dir.NextClockwise(); if (ds.IsConnected(dir2)) { int dx2 = dx + dir2.GetHorizontalOffset(); int dy2 = dy + dir2.GetVerticalOffset(); int di2 = cells[dx2 + dy2 * width].StartIndex + CompactSpan.GetConnection(ref ds, dir2); if (areas[di2] != area) continue; RegionId nr2 = regions[di2]; if (nr2 != 0 && nr2 != regionIndex) { ar = nr2; break; } } } } if (ar != 0) { regions[cell.Index] = RegionId.Null; continue; } count++; //expand neighbors for (var dir = Direction.West; dir <= Direction.South; dir++) { if (cs.IsConnected(dir)) { int dx = cell.X + dir.GetHorizontalOffset(); int dy = cell.Y + dir.GetVerticalOffset(); int di = cells[dx + dy * width].StartIndex + CompactSpan.GetConnection(ref cs, dir); if (areas[di] != area) continue; if (distances[di] >= lev && regions[di] == 0) { regions[di] = new RegionId(regionIndex); floodDistances[di] = 0; stack.Push(new CompactSpanReference(dx, dy, di)); } } } } return count > 0; }
/// <summary> /// Fill in a rectangular area with a region ID. Spans in a null area are skipped. /// </summary> /// <param name="regions">The region ID array.</param> /// <param name="newRegionId">The ID to fill in.</param> /// <param name="left">The left edge of the rectangle.</param> /// <param name="right">The right edge of the rectangle.</param> /// <param name="bottom">The bottom edge of the rectangle.</param> /// <param name="top">The top edge of the rectangle.</param> private void FillRectangleRegion(RegionId[] regions, RegionId newRegionId, int left, int right, int bottom, int top) { for (int y = bottom; y < top; y++) { for (int x = left; x < right; x++) { CompactCell c = cells[x + y * width]; for (int i = c.StartIndex, end = c.StartIndex + c.Count; i < end; i++) { if (areas[i].IsWalkable) regions[i] = newRegionId; } } } }
protected void Page_Load(object sender, EventArgs e) { SetMeta(string.Format("{0} - {1}", AdvantShop.Configuration.SettingsMain.ShopName, Resources.Resource.Admin_MasterPageAdmin_Sities)); if (RegionId < 0 && CountryId < 0) { Response.Redirect("Country.aspx"); } if (RegionId != 0) { var region = RegionService.GetRegion(RegionId); if (region != null) { lblHead.Text = region.Name; hlBack.NavigateUrl = "Regions.aspx?CountryId=" + region.CountryID; hlBack.Text = Resources.Resource.Admin_Cities_BackToRegions; hlBack2.NavigateUrl = "Country.aspx"; hlBack2.Text = Resources.Resource.Admin_Cities_BackToCoutries; } } if (CountryId != 0) { var country = CountryService.GetCountry(CountryId); if (country != null) { lblHead.Text = country.Name; hlBack.NavigateUrl = "Regions.aspx?CountryId=" + country.CountryId; hlBack.Text = Resources.Resource.Admin_Cities_BackToRegions; hlBack2.NavigateUrl = "Country.aspx"; hlBack2.Text = Resources.Resource.Admin_Cities_BackToCoutries; } btnAddCity.Visible = false; } if (!IsPostBack) { _paging = new SqlPaging { TableName = "[Customers].[City] Left Join Customers.Region On Region.RegionId=City.RegionId", ItemsPerPage = 20 }; _paging.AddFieldsRange( new List <Field> { new Field { Name = "CityID as ID", IsDistinct = true }, new Field { Name = "CityName", Sorting = SortDirection.Ascending }, new Field { Name = "CitySort" }, new Field { Name = "City.RegionID" }, new Field { Name = "City.DisplayInPopup" }, new Field { Name = "PhoneNumber" }, new Field { Name = "Region.CountryId", NotInQuery = true }, }); if (RegionId != 0) { _paging.Fields["City.RegionID"].Filter = new EqualFieldFilter { ParamName = "@RegionID", Value = RegionId.ToString() }; } if (CountryId != 0) { _paging.Fields["Region.CountryId"].Filter = new EqualFieldFilter { ParamName = "@CountryId", Value = CountryId.ToString() }; } grid.ChangeHeaderImageUrl("arrowCityName", "images/arrowup.gif"); pageNumberer.CurrentPageIndex = 1; _paging.CurrentPageIndex = 1; ViewState["Paging"] = _paging; } else { _paging = (SqlPaging)(ViewState["Paging"]); _paging.ItemsPerPage = SQLDataHelper.GetInt(ddRowsPerPage.SelectedValue); if (_paging == null) { throw (new Exception("Paging lost")); } string strIds = Request.Form["SelectedIds"]; if (!string.IsNullOrEmpty(strIds)) { strIds = strIds.Trim(); string[] arrids = strIds.Split(' '); var ids = new string[arrids.Length]; _selectionFilter = new InSetFieldFilter { IncludeValues = true }; for (int idx = 0; idx <= ids.Length - 1; idx++) { int t = int.Parse(arrids[idx]); if (t != -1) { ids[idx] = t.ToString(); } else { _selectionFilter.IncludeValues = false; _inverseSelection = true; } } _selectionFilter.Values = ids; } } }
/// <summary> /// Initializes a new instance of the <see cref="Edge"/> struct. /// </summary> /// <param name="vert0">Vertex A</param> /// <param name="vert1">Vertex B</param> /// <param name="region">Region id</param> /// <param name="area">Area id</param> public Edge(int vert0, int vert1, RegionId region, Area area) { Vert0 = vert0; Vert1 = vert1; Region = region; Area = area; }
/// <summary> /// 地方ノードを追加する /// </summary> /// <param name="region">地方</param> /// <param name="parent">親ノード</param> private static void AddRegionTreeItem(RegionId region, TreeNode parent) { // 地方ノードを追加する TreeNode node = new TreeNode { Text = Provinces.GetRegionName(region), Tag = region }; parent.Nodes.Add(node); // 地域ノードを順に追加する if (Provinces.RegionAreaMap.ContainsKey(region)) { foreach (AreaId area in Provinces.RegionAreaMap[region]) { AddAreaTreeItem(area, node); } } }
public void ParseJsonFiles() { // matches the [region]_xxx of the file // so the file must be named with the region of the matches first var regex = new Regex(@"^([A-Z]+)"); foreach (string filename in fileNames) { var fileids = DeserializeFile(filename); System.Text.RegularExpressions.Match match = regex.Match(filename); foreach(string id in fileids.Skip(4000).Take(1000)) { var regionid = new RegionId() { Id = id, Region = match.Value }; IDMatches.Enqueue(regionid); } } }
/// <summary> /// 地方名を取得する /// </summary> /// <param name="region">地方</param> /// <returns>地方名</returns> public static string GetRegionName(RegionId region) { // AoD1.10以降の場合、文字列定義が変更になっているかをチェックする if ((Game.Type == GameType.ArsenalOfDemocracy) && (Game.Version >= 110)) { if (ReplacingRegionNamesAod.ContainsKey(region)) { return Config.GetText(ReplacingRegionNamesAod[region]); } } return Config.GetText(RegionNames[(int) region]); }
/// <summary> /// Discards regions that are too small. /// </summary> /// <param name="regionIds">region data</param> /// <param name="minRegionArea">The minimum area a region can have</param> /// <param name="mergeRegionSize">The size of the regions after merging</param> /// <param name="maxRegionId">determines the number of regions available</param> /// <returns>The reduced number of regions.</returns> private int FilterSmallRegions(RegionId[] regionIds, int minRegionArea, int mergeRegionSize, int maxRegionId) { int numRegions = maxRegionId + 1; Region[] regions = new Region[numRegions]; //construct regions for (int i = 0; i < numRegions; i++) regions[i] = new Region(i); //find edge of a region and find connections around a contour for (int y = 0; y < length; y++) { for (int x = 0; x < width; x++) { CompactCell c = cells[x + y * width]; for (int i = c.StartIndex, end = c.StartIndex + c.Count; i < end; i++) { CompactSpanReference spanRef = new CompactSpanReference(x, y, i); //HACK since the border region flag makes r negative, I changed r == 0 to r <= 0. Figure out exactly what maxRegionId's purpose is and see if Region.IsBorderOrNull is all we need. int r = (int)regionIds[i]; if (r <= 0 || (int)r >= numRegions) continue; Region reg = regions[(int)r]; reg.SpanCount++; //update floors for (int j = c.StartIndex; j < end; j++) { if (i == j) continue; RegionId floorId = regionIds[j]; if (floorId == 0 || (int)floorId >= numRegions) continue; reg.AddUniqueFloorRegion(floorId); } //have found contour if (reg.Connections.Count > 0) continue; reg.AreaType = areas[i]; //check if this cell is next to a border for (var dir = Direction.West; dir <= Direction.South; dir++) { if (IsSolidEdge(regionIds, ref spanRef, dir)) { //The cell is at a border. //Walk around contour to find all neighbors WalkContour(regionIds, spanRef, dir, reg.Connections); break; } } } } } //Remove too small regions Stack<RegionId> stack = new Stack<RegionId>(); List<RegionId> trace = new List<RegionId>(); for (int i = 0; i < numRegions; i++) { Region reg = regions[i]; if (reg.IsBorderOrNull || reg.SpanCount == 0 || reg.Visited) continue; //count the total size of all connected regions //also keep track of the regions connections to a tile border bool connectsToBorder = false; int spanCount = 0; stack.Clear(); trace.Clear(); reg.Visited = true; stack.Push(reg.Id); while (stack.Count > 0) { //pop RegionId ri = stack.Pop(); Region creg = regions[(int)ri]; spanCount += creg.SpanCount; trace.Add(ri); for (int j = 0; j < creg.Connections.Count; j++) { if (RegionId.HasFlags(creg.Connections[j], RegionFlags.Border)) { connectsToBorder = true; continue; } Region neiReg = regions[(int)creg.Connections[j]]; if (neiReg.Visited || neiReg.IsBorderOrNull) continue; //visit stack.Push(neiReg.Id); neiReg.Visited = true; } } //if the accumulated region size is too small, remove it //do not remove areas which connect to tile borders as their size can't be estimated correctly //and removing them can potentially remove necessary areas if (spanCount < minRegionArea && !connectsToBorder) { //kill all visited regions for (int j = 0; j < trace.Count; j++) { int index = (int)trace[j]; regions[index].SpanCount = 0; regions[index].Id = RegionId.Null; } } } //Merge too small regions to neighbor regions int mergeCount = 0; do { mergeCount = 0; for (int i = 0; i < numRegions; i++) { Region reg = regions[i]; if (reg.IsBorderOrNull || reg.SpanCount == 0) continue; //check to see if region should be merged if (reg.SpanCount > mergeRegionSize && reg.IsConnectedToBorder()) continue; //small region with more than one connection or region which is not connected to border at all //find smallest neighbor that connects to this one int smallest = int.MaxValue; RegionId mergeId = reg.Id; for (int j = 0; j < reg.Connections.Count; j++) { if (RegionId.HasFlags(reg.Connections[j], RegionFlags.Border)) continue; Region mreg = regions[(int)reg.Connections[j]]; if (mreg.IsBorderOrNull) continue; if (mreg.SpanCount < smallest && reg.CanMergeWith(mreg) && mreg.CanMergeWith(reg)) { smallest = mreg.SpanCount; mergeId = mreg.Id; } } //found new id if (mergeId != reg.Id) { RegionId oldId = reg.Id; Region target = regions[(int)mergeId]; //merge regions if (target.MergeWithRegion(reg)) { //fix regions pointing to current region for (int j = 0; j < numRegions; j++) { if (regions[j].IsBorderOrNull) continue; //if another regions was already merged into current region //change the nid of the previous region too if (regions[j].Id == oldId) regions[j].Id = mergeId; //replace current region with new one if current region is neighbor regions[j].ReplaceNeighbour(oldId, mergeId); } mergeCount++; } } } } while (mergeCount > 0); //Compress region ids for (int i = 0; i < numRegions; i++) { regions[i].Remap = false; if (regions[i].IsBorderOrNull) continue; regions[i].Remap = true; } int regIdGen = 0; for (int i = 0; i < numRegions; i++) { if (!regions[i].Remap) continue; RegionId oldId = regions[i].Id; RegionId newId = new RegionId(++regIdGen); for (int j = i; j < numRegions; j++) { if (regions[j].Id == oldId) { regions[j].Id = newId; regions[j].Remap = false; } } } //Remap regions for (int i = 0; i < spans.Length; i++) { if (!RegionId.HasFlags(regionIds[i], RegionFlags.Border)) regionIds[i] = regions[(int)regionIds[i]].Id; } return regIdGen; }
/// <summary> /// プロヴィンスの地方を変更する /// </summary> /// <param name="province">プロヴィンス</param> /// <param name="region">地方</param> public static void ModifyRegion(Province province, RegionId region) { // 地方と地域の対応付けを変更する DetachRegionArea(province.Region, province.Area); AttachRegionArea(region, province.Area); // 大陸と地方の対応付けを変更する DetachContinentRegion(province.Continent, province.Region); AttachContinentRegion(province.Continent, region); // 値を更新する province.Region = region; }
/// <summary> /// Helper method for WalkContour function /// </summary> /// <param name="sr">The span to get the corner height for.</param> /// <param name="dir">The direction to get the corner height from.</param> /// <param name="isBorderVertex">Determine whether the vertex is a border or not.</param> /// <returns>The corner height.</returns> private int GetCornerHeight(CompactSpanReference sr, Direction dir, out bool isBorderVertex) { isBorderVertex = false; CompactSpan s = this[sr]; int cornerHeight = s.Minimum; Direction dirp = dir.NextClockwise(); //new clockwise direction RegionId[] cornerRegs = new RegionId[4]; Area[] cornerAreas = new Area[4]; //combine region and area codes in order to prevent border vertices, which are in between two areas, to be removed cornerRegs[0] = s.Region; cornerAreas[0] = areas[sr.Index]; if (s.IsConnected(dir)) { //get neighbor span int dx = sr.X + dir.GetHorizontalOffset(); int dy = sr.Y + dir.GetVerticalOffset(); int di = cells[dx + dy * width].StartIndex + CompactSpan.GetConnection(ref s, dir); CompactSpan ds = spans[di]; cornerHeight = Math.Max(cornerHeight, ds.Minimum); cornerRegs[1] = spans[di].Region; cornerAreas[1] = areas[di]; //get neighbor of neighbor's span if (ds.IsConnected(dirp)) { int dx2 = dx + dirp.GetHorizontalOffset(); int dy2 = dy + dirp.GetVerticalOffset(); int di2 = cells[dx2 + dy2 * width].StartIndex + CompactSpan.GetConnection(ref ds, dirp); CompactSpan ds2 = spans[di2]; cornerHeight = Math.Max(cornerHeight, ds2.Minimum); cornerRegs[2] = ds2.Region; cornerAreas[2] = areas[di2]; } } //get neighbor span if (s.IsConnected(dirp)) { int dx = sr.X + dirp.GetHorizontalOffset(); int dy = sr.Y + dirp.GetVerticalOffset(); int di = cells[dx + dy * width].StartIndex + CompactSpan.GetConnection(ref s, dirp); CompactSpan ds = spans[di]; cornerHeight = Math.Max(cornerHeight, ds.Minimum); cornerRegs[3] = ds.Region; cornerAreas[3] = areas[di]; //get neighbor of neighbor's span if (ds.IsConnected(dir)) { int dx2 = dx + dir.GetHorizontalOffset(); int dy2 = dy + dir.GetVerticalOffset(); int di2 = cells[dx2 + dy2 * width].StartIndex + CompactSpan.GetConnection(ref ds, dir); CompactSpan ds2 = spans[di2]; cornerHeight = Math.Max(cornerHeight, ds2.Minimum); cornerRegs[2] = ds2.Region; cornerAreas[2] = areas[di2]; } } //check if vertex is special edge vertex //if so, these vertices will be removed later for (int j = 0; j < 4; j++) { int a = j; int b = (j + 1) % 4; int c = (j + 2) % 4; int d = (j + 3) % 4; RegionId ra = cornerRegs[a], rb = cornerRegs[b], rc = cornerRegs[c], rd = cornerRegs[d]; Area aa = cornerAreas[a], ab = cornerAreas[b], ac = cornerAreas[c], ad = cornerAreas[d]; //the vertex is a border vertex if: //two same exterior cells in a row followed by two interior cells and none of the regions are out of bounds bool twoSameExteriors = RegionId.HasFlags(ra, RegionFlags.Border) && RegionId.HasFlags(rb, RegionFlags.Border) && (ra == rb && aa == ab); bool twoSameInteriors = !(RegionId.HasFlags(rc, RegionFlags.Border) || RegionId.HasFlags(rd, RegionFlags.Border)); bool intsSameArea = ac == ad; bool noZeros = ra != 0 && rb != 0 && rc != 0 && rd != 0 && aa != 0 && ab != 0 && ac != 0 && ad != 0; if (twoSameExteriors && twoSameInteriors && intsSameArea && noZeros) { isBorderVertex = true; break; } } return cornerHeight; }
/// <summary> /// 大陸と地方の対応付けを設定する /// </summary> /// <param name="continentId">大陸</param> /// <param name="regionId">地方</param> private static void AttachContinentRegion(ContinentId continentId, RegionId regionId) { // 大陸の項目がなければ作成する if (!ContinentRegionMap.ContainsKey(continentId)) { ContinentRegionMap.Add(continentId, new List<RegionId>()); } // 地方リストに地方を追加する if (!ContinentRegionMap[continentId].Contains(regionId)) { ContinentRegionMap[continentId].Add(regionId); } }
/// <summary> /// Sort the compact spans /// </summary> /// <param name="regions">Region data</param> /// <param name="stacks">Temporary stack of CompactSpanReference values</param> /// <param name="startlevel">Starting level</param> /// <param name="numStacks">The number of layers</param> /// <param name="logLevelsPerStack">log base 2 of stack levels</param> private void SortCellsByLevel(RegionId[] regions, List<CompactSpanReference>[] stacks, int startlevel, int numStacks, int logLevelsPerStack) { startlevel = startlevel >> logLevelsPerStack; for (int j = 0; j < numStacks; j++) stacks[j].Clear(); for (int y = 0; y < length; y++) { for (int x = 0; x < width; x++) { CompactCell c = cells[y * width + x]; for (int i = c.StartIndex, end = c.StartIndex + c.Count; i < end; i++) { if (!areas[i].IsWalkable || !regions[i].IsNull) continue; int level = distances[i] >> logLevelsPerStack; int sId = startlevel - level; if (sId >= numStacks) continue; if (sId < 0) sId = 0; stacks[sId].Add(new CompactSpanReference(x, y, i)); } } } }
/// <summary> /// 地方と地域の対応付けを設定する /// </summary> /// <param name="regionId">地方</param> /// <param name="areaId">地域</param> private static void AttachRegionArea(RegionId regionId, AreaId areaId) { // 地方の項目がなければ作成する if (!RegionAreaMap.ContainsKey(regionId)) { RegionAreaMap.Add(regionId, new List<AreaId>()); } // 地域リストに地域を追加する if (!RegionAreaMap[regionId].Contains(areaId)) { RegionAreaMap[regionId].Add(areaId); } }
/// <summary> /// The central method for building regions, which consists of connected, non-overlapping walkable spans. /// </summary> /// <param name="borderSize">The border size</param> /// <param name="minRegionArea">If smaller than this value, region will be null</param> /// <param name="mergeRegionArea">Reduce unneccesarily small regions</param> public void BuildRegions(int borderSize, int minRegionArea, int mergeRegionArea) { if (distances == null) BuildDistanceField(); const int LogStackCount = 3; const int StackCount = 1 << LogStackCount; List<CompactSpanReference>[] stacks = new List<CompactSpanReference>[StackCount]; for (int i = 0; i < stacks.Length; i++) stacks[i] = new List<CompactSpanReference>(1024); RegionId[] regions = new RegionId[spans.Length]; int[] floodDistances = new int[spans.Length]; RegionId[] regionBuffer = new RegionId[spans.Length]; int[] distanceBuffer = new int[spans.Length]; int regionIndex = 1; int level = ((maxDistance + 1) / 2) * 2; const int ExpandIters = 8; if (borderSize > 0) { //make sure border doesn't overflow int borderWidth = Math.Min(width, borderSize); int borderHeight = Math.Min(length, borderSize); //fill regions FillRectangleRegion(regions, new RegionId(regionIndex++, RegionFlags.Border), 0, borderWidth, 0, length); FillRectangleRegion(regions, new RegionId(regionIndex++, RegionFlags.Border), width - borderWidth, width, 0, length); FillRectangleRegion(regions, new RegionId(regionIndex++, RegionFlags.Border), 0, width, 0, borderHeight); FillRectangleRegion(regions, new RegionId(regionIndex++, RegionFlags.Border), 0, width, length - borderHeight, length); this.borderSize = borderSize; } int stackId = -1; while (level > 0) { level = level >= 2 ? level - 2 : 0; stackId = (stackId + 1) & (StackCount - 1); if (stackId == 0) SortCellsByLevel(regions, stacks, level, StackCount, 1); else AppendStacks(stacks[stackId - 1], stacks[stackId], regions); //expand current regions until no new empty connected cells found ExpandRegions(regions, floodDistances, ExpandIters, level, stacks[stackId], regionBuffer, distanceBuffer); //mark new regions with ids for (int j = 0; j < stacks[stackId].Count; j++) { var spanRef = stacks[stackId][j]; if (spanRef.Index >= 0 && regions[spanRef.Index] == 0) if (FloodRegion(regions, floodDistances, regionIndex, level, ref spanRef)) regionIndex++; } } //expand current regions until no new empty connected cells found ExpandRegions(regions, floodDistances, ExpandIters * 8, 0, null, regionBuffer, distanceBuffer); //filter out small regions this.maxRegions = FilterSmallRegions(regions, minRegionArea, mergeRegionArea, regionIndex); //write the result out for (int i = 0; i < spans.Length; i++) spans[i].Region = regions[i]; }
/// <summary> /// 大陸と地方の対応付けを解除する /// </summary> /// <param name="continentId">大陸</param> /// <param name="regionId">地方</param> private static void DetachContinentRegion(ContinentId continentId, RegionId regionId) { // 大陸の項目がなければ何もしない if (!ContinentRegionMap.ContainsKey(continentId)) { return; } // 地方リストから地方を削除する if (ContinentRegionMap[continentId].Contains(regionId)) { ContinentRegionMap[continentId].Remove(regionId); // 地方リストの項目がなくなれば大陸の項目を削除する if (ContinentRegionMap[continentId].Count == 0) { ContinentRegionMap.Remove(continentId); } } }
/// <summary> /// Expands regions to include spans above a specified water level. /// </summary> /// <param name="regions">The array of region IDs.</param> /// <param name="floodDistances">The array of flooding distances.</param> /// <param name="maxIterations">The maximum number of allowed iterations before breaking.</param> /// <param name="level">The current water level.</param> /// <param name="stack">A stack of span references that are being expanded.</param> /// <param name="regionBuffer">A buffer to store region IDs. Must be at least the same size as <c>regions</c>.</param> /// <param name="distanceBuffer">A buffer to store flood distances. Must be at least the same size as <c>floodDistances</c>.</param> private void ExpandRegions(RegionId[] regions, int[] floodDistances, int maxIterations, int level, List<CompactSpanReference> stack = null, RegionId[] regionBuffer = null, int[] distanceBuffer = null) { //generate buffers if they're not passed in or if they're too small. if (regionBuffer == null || regionBuffer.Length < regions.Length) regionBuffer = new RegionId[regions.Length]; if (distanceBuffer == null || distanceBuffer.Length < floodDistances.Length) distanceBuffer = new int[floodDistances.Length]; //copy existing data into the buffers. Array.Copy(regions, 0, regionBuffer, 0, regions.Length); Array.Copy(floodDistances, 0, distanceBuffer, 0, floodDistances.Length); //find cells that are being expanded to. if (stack == null) { stack = new List<CompactSpanReference>(); for (int y = 0; y < length; y++) { for (int x = 0; x < width; x++) { CompactCell c = cells[x + y * width]; for (int i = c.StartIndex, end = c.StartIndex + c.Count; i < end; i++) { //a cell is being expanded to if it's distance is greater than the current level, //but no region has been asigned yet. It must also not be in a null area. if (this.distances[i] >= level && regions[i] == 0 && areas[i].IsWalkable) stack.Add(new CompactSpanReference(x, y, i)); } } } } else { for (int j = 0; j < stack.Count; j++) { if (regions[stack[j].Index] != 0) stack[j] = CompactSpanReference.Null; } } //assign regions to all the cells that are being expanded to. //will run until it's done or it runs maxIterations times. int iter = 0; while (stack.Count > 0) { //spans in the stack that are skipped: // - assigned a region ID in an earlier iteration // - not neighboring any spans with region IDs int skipped = 0; for (int j = 0; j < stack.Count; j++) { CompactSpanReference spanRef = stack[j]; int x = spanRef.X; int y = spanRef.Y; int i = spanRef.Index; //skip regions already assigned to if (i < 0) { skipped++; continue; } RegionId r = regions[i]; Area area = areas[i]; CompactSpan s = spans[i]; //search direct neighbors for the one with the smallest distance value int minDist = int.MaxValue; for (var dir = Direction.West; dir <= Direction.South; dir++) { if (!s.IsConnected(dir)) continue; int dx = x + dir.GetHorizontalOffset(); int dy = y + dir.GetVerticalOffset(); int di = cells[dx + dy * width].StartIndex + CompactSpan.GetConnection(ref s, dir); if (areas[di] != area) continue; //compare distance to previous best RegionId ri = regions[di]; int dist = floodDistances[di]; if (!(ri.IsNull || RegionId.HasFlags(ri, RegionFlags.Border))) { //set region and distance if better if (dist + 2 < minDist) { r = ri; minDist = dist + 2; } } } if (r != 0) { //set the region and distance for this span regionBuffer[i] = r; distanceBuffer[i] = minDist; //mark this item in the stack as assigned for the next iteration. stack[j] = CompactSpanReference.Null; } else { //skip spans that don't neighbor any regions skipped++; } } //if the entire stack is being skipped, we're done. if (skipped == stack.Count) break; //Copy from the buffers back to the original arrays. This is done after each iteration //because changing it in-place has some side effects for the other spans in the stack. Array.Copy(regionBuffer, 0, regions, 0, regions.Length); Array.Copy(distanceBuffer, 0, floodDistances, 0, floodDistances.Length); if (level > 0) { //if we hit maxIterations before expansion is done, break out anyways. ++iter; if (iter >= maxIterations) break; } } }
/// <summary> /// 地方と地域の対応付けを解除する /// </summary> /// <param name="regionId">地方</param> /// <param name="areaId">地域</param> private static void DetachRegionArea(RegionId regionId, AreaId areaId) { // 地方の項目がなければ何もしない if (!RegionAreaMap.ContainsKey(regionId)) { return; } // 地域リストから地域を削除する if (RegionAreaMap[regionId].Contains(areaId)) { RegionAreaMap[regionId].Remove(areaId); // 地域リストの項目がなくなれば地方の項目を削除する if (RegionAreaMap[regionId].Count == 0) { RegionAreaMap.Remove(regionId); } } }