public Region(IAdminEntity entity, int regionIndex) { this.entity = entity; this.regionIndex = regionIndex; this.sanitized = true; neighbours = new List <Region> (); }
IEnumerator RemoveHexagons(IAdminEntity[] entities) { Clipper clipper = new Clipper(); Cell[] cells = _map.cells; for (int j = 0; j < cells.Length; j++) { if (j % 100 == 0) { if (hexifyContext.progress != null) { if (hexifyContext.progress((float)j / cells.Length, hexifyContext.title, "Pass 5/6: removing cells from neighbours...")) { cancelled = true; hexifyContext.finish(true); yield break; } } yield return(null); } int entityIndex = procCells [j].entityIndex; if (entityIndex < 0) { continue; } RegionCell regionCell = procCells [j]; IAdminEntity entity = entities [entityIndex]; // Substract cell region from any other entity List <Region> otherRegions; if (entity is Country) { otherRegions = _map.GetCountryRegionsOverlap(regionCell.cellRegion); } else { otherRegions = _map.GetProvinceRegionsOverlap(regionCell.cellRegion); } int orCount = otherRegions.Count; for (int o = 0; o < orCount; o++) { Region otherRegion = otherRegions [o]; IAdminEntity otherEntity = otherRegion.entity; if (otherEntity == entity) { continue; } clipper.Clear(); clipper.AddPath(otherRegion, PolyType.ptSubject); clipper.AddPath(regionCell.cellRegion, PolyType.ptClip); clipper.Execute(ClipType.ctDifference, otherEntity); } } }
void RestoreRegions(List <Region> savedRegions) { for (int k = 0; k < savedRegions.Count; k++) { IAdminEntity entity = savedRegions [k].entity; int regionIndex = savedRegions [k].regionIndex; entity.regions [regionIndex] = savedRegions [k]; } RedrawFrontiers(); }
void SnapRegion(Region region) { int regionPointsCount = region.points.Length; int entitiesCount = entities.Count; int m = Mathf.Max(tw, th); float threshold = 2f / m; threshold *= threshold; for (int k = 0; k < regionPointsCount; k++) { Vector2 p = region.points [k]; Vector2 nearPoint = Misc.Vector2zero; float minDist = float.MaxValue; for (int e = 0; e < entitiesCount; e++) { IAdminEntity entity = entities [e]; if (entity.regions == null) { continue; } int entityRegionsCount = entity.regions.Count; for (int r = 0; r < entityRegionsCount; r++) { Region entityRegion = entity.regions [r]; if (entityRegion == region) { continue; } int entityRegionsPointsCount = entityRegion.points.Length; for (int j = 0; j < entityRegionsPointsCount; j++) { Vector2 op = entityRegion.points [j]; // Check if both points are near float d = (p.x - op.x) * (p.x - op.x) + (p.y - op.y) * (p.y - op.y); if (d < threshold && d < minDist) { nearPoint = op; minDist = d; } } } } // Snap point? if (minDist < float.MaxValue) { region.points [k] = nearPoint; } } region.RemoveDuplicatePoints(); }
void EntityAdd(IAdminEntity newEntity) { if (newEntity is Country) { map.CountryAdd((Country)newEntity); } else { map.ProvinceAdd((Province)newEntity); } }
public void MergeAdjacentRegions(IAdminEntity entity) { // Searches for adjacency - merges in first region int regionCount = entity.regions.Count; for (int k = 0; k < regionCount; k++) { Region region1 = entity.regions [k]; if (region1 == null || region1.points == null || region1.points.Length == 0) { continue; } for (int j = k + 1; j < regionCount; j++) { Region region2 = entity.regions [j]; if (region2 == null || region2.points == null || region2.points.Length == 0) { continue; } if (!region1.Intersects(region2)) { continue; } RegionMagnet(region1, region2); Clipper clipper = new Clipper(); clipper.AddPath(region1, PolyType.ptSubject); clipper.AddPath(region2, PolyType.ptClip); clipper.Execute(ClipType.ctUnion); // Add new neighbours int rnCount = region2.neighbours.Count; for (int n = 0; n < rnCount; n++) { Region neighbour = region2.neighbours [n]; if (neighbour != null && neighbour != region1 && !region1.neighbours.Contains(neighbour)) { region1.neighbours.Add(neighbour); } } // Remove merged region entity.regions.RemoveAt(j); region1.sanitized = false; j = k; regionCount--; entity.mainRegionIndex = 0; // will need to refresh country definition later in the process } } }
IEnumerator AddHexagons(IAdminEntity[] entities) { Cell[] cells = _map.cells; Clipper clipper = new Clipper(); for (int j = 0; j < cells.Length; j++) { if (j % 100 == 0) { if (hexifyContext.progress != null) { if (hexifyContext.progress((float)j / cells.Length, hexifyContext.title, "Pass 3/6: adding hexagons to frontiers...")) { cancelled = true; hexifyContext.finish(true); yield break; } } yield return(null); } int entityIndex = procCells [j].entityIndex; if (entityIndex < 0) { continue; } Cell cell = cells [j]; // Create a region for the cell IAdminEntity entity = entities [entityIndex]; Vector2[] newPoints = new Vector2[6]; for (int k = 0; k < 6; k++) { newPoints [k].x = hexagonPoints [k].x + cell.center.x; newPoints [k].y = hexagonPoints [k].y + cell.center.y; } procCells [j].cellRegion = new Region(entity, entity.regions.Count); procCells [j].cellRegion.UpdatePointsAndRect(newPoints); // Add region to target entity's polygon - only if the entity is touching or crossing target entity frontier Region targetRegion = procCells [j].entityRegion; clipper.Clear(); clipper.AddPath(targetRegion, PolyType.ptSubject); clipper.AddPath(procCells [j].cellRegion, PolyType.ptClip); clipper.Execute(ClipType.ctUnion); } }
IEnumerator RemoveSmallRegions(float minArea, IAdminEntity[] _entities) { // Clear small regions for (int c = 0; c < _entities.Length; c++) { if (c % 10 == 0) { if (hexifyContext.progress != null) { if (hexifyContext.progress((float)c / _entities.Length, hexifyContext.title, "Pass 1/6: removing small regions...")) { cancelled = true; hexifyContext.finish(true); yield break; } } yield return(null); } IAdminEntity entity = _entities [c]; int rCount = entity.regions.Count; bool recalc = false; for (int r = 0; r < rCount; r++) { if (entity.regions [r].rect2DArea < minArea) { entity.regions [r].Clear(); recalc = true; } } if (recalc) { if (entity is Country) { _map.RefreshCountryGeometry(entity); } else { _map.RefreshProvinceGeometry(entity); } } } }
int EntityAdd(IAdminEntity newEntity) { if (newEntity is Country) { Country c = (Country)newEntity; if (map.CountryAdd(c) >= 0) { return(map.GetCountryIndex(c)); } } else { Province p = (Province)newEntity; if (map.ProvinceAdd(p)) { return(map.GetProvinceIndex(p)); } } return(-1); }
void FilterSnapRegions() { if (snapEntityIndex >= entities.Count) { progress = 1f; return; } progress = (float)snapEntityIndex / entities.Count; IAdminEntity entity = entities [snapEntityIndex]; if (entity.regions != null) { int entityRegionsCount = entity.regions.Count; for (int k = 0; k < entityRegionsCount; k++) { Region regionTosnap = entities [snapEntityIndex].regions [k]; SnapRegion(regionTosnap); } } snapEntityIndex++; }
void FilterPolishJoints() { if (polishEntityIndex >= entities.Count) { progress = 1f; return; } progress = (float)polishEntityIndex / entities.Count; IAdminEntity entity = entities [polishEntityIndex]; if (entity.regions != null) { int entityRegionsCount = entity.regions.Count; for (int k = 0; k < entityRegionsCount; k++) { Region region = entities [polishEntityIndex].regions [k]; PolishRegionJoints(region); } } polishEntityIndex++; }
public void Execute(ClipType clipType, IAdminEntity entity) { List <List <IntPoint> > solution = new List <List <IntPoint> > (); Execute(clipType, solution); int contourCount = solution.Count; entity.regions.Clear(); for (int c = 0; c < contourCount; c++) { List <IntPoint> points = solution [c]; int count = points.Count; Vector2[] newPoints = new Vector2[count]; for (int k = 0; k < count; k++) { newPoints [k] = new Vector2((float)points [k].X / MULTIPLIER, (float)points [k].Y / MULTIPLIER); } Region region = new Region(entity, entity.regions.Count); region.UpdatePointsAndRect(newPoints); entity.regions.Add(region); } }
bool AnyRegionContainsPoint(Vector2 p) { int entitiesCount = entities.Count; for (int k = 0; k < entitiesCount; k++) { IAdminEntity entity = entities [k]; if (entity.regions == null) { continue; } int entityRegionsCount = entity.regions.Count; for (int r = 0; r < entityRegionsCount; r++) { Region region = entity.regions [r]; if (region.Contains(p)) { return(true); } } } return(false); }
public void Execute(ClipType clipType, IAdminEntity entity) { if (solution == null) { solution = new List <List <IntPoint> > (); } Execute(clipType, solution); int contourCount = solution.Count; // Remove subject from entity if (subjects != null) { for (int k = 0; k < subjects.Count; k++) { Region sub = subjects[k]; if (entity.regions.Contains(sub)) { entity.regions.Remove(sub); } } } // Add resulting regions for (int c = 0; c < contourCount; c++) { // In the case of difference operations, the resulting polytongs could be artifacts if the frontiers do not match perfectly. In that case, we ignore small isolated triangles. if (clipType == ClipType.ctUnion || solution[c].Count >= 5) { Vector2[] newPoints = BuildPointArray(solution[c]); Region region = new Region(entity, entity.regions.Count); region.UpdatePointsAndRect(newPoints); region.sanitized = true; entity.regions.Add(region); } } entity.mainRegionIndex = 0; }
/// <summary> /// Used internally by the Map Editor. It will recalculate de boundaries and optimize frontiers based on new data of provinces array /// </summary> public void RefreshProvinceGeometry(IAdminEntity province) { lastProvinceLookupCount = -1; float maxVol = 0; if (province.regions == null) { ReadProvincePackedString(province); } if (province.regions == null) { return; } int regionCount = province.regions.Count; Vector2 minProvince = Misc.Vector2one * 10; Vector2 maxProvince = -minProvince; Vector2 min = Misc.Vector2one * 10f; Vector2 max = -min; for (int r = 0; r < regionCount; r++) { Region provinceRegion = province.regions [r]; provinceRegion.entity = province; // just in case one country has been deleted provinceRegion.regionIndex = r; // just in case a region has been deleted int coorCount = provinceRegion.points.Length; min.x = min.y = 10f; max.x = max.y = -10f; for (int c = 0; c < coorCount; c++) { float x = provinceRegion.points [c].x; float y = provinceRegion.points [c].y; if (x < min.x) { min.x = x; } if (x > max.x) { max.x = x; } if (y < min.y) { min.y = y; } if (y > max.y) { max.y = y; } } FastVector.Average(ref min, ref max, ref provinceRegion.center); if (min.x < minProvince.x) { minProvince.x = min.x; } if (min.y < minProvince.y) { minProvince.y = min.y; } if (max.x > maxProvince.x) { maxProvince.x = max.x; } if (max.y > maxProvince.y) { maxProvince.y = max.y; } provinceRegion.rect2D = new Rect(min.x, min.y, max.x - min.x, max.y - min.y); provinceRegion.rect2DArea = provinceRegion.rect2D.width * provinceRegion.rect2D.height; float vol = FastVector.SqrDistance(ref max, ref min); // (max - min).sqrMagnitude; if (vol > maxVol) { maxVol = vol; province.mainRegionIndex = r; province.center = provinceRegion.center; } } province.regionsRect2D = new Rect(minProvince.x, minProvince.y, Math.Abs(maxProvince.x - minProvince.x), Mathf.Abs(maxProvince.y - minProvince.y)); }
public void SplitVertically() { if (entityIndex < 0 || entityIndex >= entities.Length) { return; } IAdminEntity currentEntity = entities [entityIndex]; Region currentRegion = currentEntity.regions [regionIndex]; List <Vector2> half1 = new List <Vector2> (); List <Vector2> half2 = new List <Vector2> (); int prevSide = 0; Vector2 q = currentRegion.latlon [0]; for (int k = 0; k < currentRegion.latlon.Length; k++) { Vector2 p = currentRegion.latlon [k]; if (p.y > splitVerticallyAt) // compare longitudes { if (prevSide == -1) { float t = (splitVerticallyAt - q.y) / (p.y - q.y); Vector2 r = Vector2.Lerp(q, p, t); half1.Add(r); half2.Add(r); } half1.Add(p); q = p; prevSide = 1; } else { if (prevSide == 1) { float t = (splitVerticallyAt - q.y) / (p.y - q.y); Vector2 r = Vector2.Lerp(q, p, t); half1.Add(r); half2.Add(r); } half2.Add(p); q = p; prevSide = -1; } } // Setup new entity IAdminEntity newEntity; if (currentEntity is Country) { string name = map.GetCountryUniqueName("New " + currentEntity.name); newEntity = new Country(name, ((Country)currentEntity).continent); } else { string name = map.GetProvinceUniqueName("New " + currentEntity.name); newEntity = new Province(name, ((Province)currentEntity).countryIndex); newEntity.regions = new List <Region> (); } // Update polygons Region newRegion = new Region(newEntity, 0); if (entities [countryIndex].latlonCenter.y > splitVerticallyAt) { currentRegion.latlon = half1.ToArray(); newRegion.latlon = half2.ToArray(); } else { currentRegion.latlon = half2.ToArray(); newRegion.latlon = half1.ToArray(); } map.RegionSanitize(currentRegion); map.RegionSmooth(currentRegion, 1.5f); map.RegionSanitize(newRegion); map.RegionSmooth(newRegion, 1.5f); newEntity.regions.Add(newRegion); int newEntityIndex = EntityAdd(newEntity); // Refresh old entity and selects the new if (currentEntity is Country) { map.RefreshCountryDefinition(newEntityIndex, null); map.RefreshCountryDefinition(countryIndex, null); SplitCities(countryIndex, newRegion); ClearSelection(); RedrawFrontiers(); countryIndex = newEntityIndex; countryRegionIndex = 0; CountryRegionSelect(); countryChanges = true; cityChanges = true; } else { map.RefreshProvinceDefinition(newEntityIndex); map.RefreshProvinceDefinition(provinceIndex); highlightedRegions = null; ClearSelection(); RedrawFrontiers(); provinceIndex = newEntityIndex; provinceRegionIndex = 0; countryIndex = map.provinces [provinceIndex].countryIndex; countryRegionIndex = map.countries [countryIndex].mainRegionIndex; CountryRegionSelect(); ProvinceRegionSelect(); provinceChanges = true; } map.RedrawMapLabels(); }
/// <summary> /// Exports the geographic data in packed string format with reduced quality. /// </summary> public string GetCountryGeoDataLowQuality() { // step 1: duplicate data IAdminEntity[] entities; if (editingMode == EDITING_MODE.COUNTRIES) { entities = map.countries; } else { entities = map.provinces; } List <IAdminEntity> entities1 = new List <IAdminEntity>(entities); // step 1: prepare data structures for (int k = 0; k < entities1.Count; k++) { entities1[k].regions = new List <Region>(entities1[k].regions); } // step 2: catalog points int totalPoints = 0; List <Vector2> allPoints = new List <Vector2>(150000); for (int k = 0; k < entities1.Count; k++) { for (int r = 0; r < entities1[k].regions.Count; r++) { Region region1 = entities1[k].regions[r]; totalPoints += region1.latlon.Length; allPoints.AddRange(region1.latlon); } } allPoints = DouglasPeucker.SimplifyCurve(allPoints, 0.1); Dictionary <Vector2, bool> allPointsLookup = new Dictionary <Vector2, bool>(allPoints.Count); for (int k = 0; k < allPoints.Count; k++) { if (!allPointsLookup.ContainsKey(allPoints[k])) { allPointsLookup.Add(allPoints[k], true); } } // step 3: reduce region points according to exclusion catalog int savings = 0; List <Vector2> goodLatLons = new List <Vector2>(15000); for (int k = 0; k < entities1.Count; k++) { for (int r = 0; r < entities1[k].regions.Count; r++) { goodLatLons.Clear(); Region region = entities1[k].regions[r]; for (int p = 0; p < region.latlon.Length; p++) { Vector2 latlon = region.latlon[p]; if (allPointsLookup.ContainsKey(latlon)) { goodLatLons.Add(latlon); } } PolygonSanitizer.RemoveCrossingSegments(goodLatLons); if (goodLatLons.Count < 5) { entities1[k].regions.Remove(region); r--; } else { totalPoints += region.latlon.Length; savings += (region.latlon.Length - goodLatLons.Count); region.latlon = goodLatLons.ToArray(); } } } Debug.Log(savings + " points removed of " + totalPoints + " (" + (((float)savings / totalPoints) * 100.0f).ToString("F1") + "%)"); StringBuilder sb = new StringBuilder(); for (int k = 0; k < entities1.Count; k++) { IAdminEntity entity = entities1[k]; if (entity.regions.Count == 0) { continue; } if (k > 0) { sb.Append("|"); } sb.Append(entity.name + "$"); if (entity is Country) { sb.Append(((Country)entity).continent + "$"); } else { sb.Append(map.countries[((Province)entity).countryIndex].name + "$"); } for (int r = 0; r < entity.regions.Count; r++) { if (r > 0) { sb.Append("*"); } Region region = entity.regions [r]; for (int p = 0; p < region.latlon.Length; p++) { if (p > 0) { sb.Append(";"); } Vector2 point = region.latlon [p] * WorldMapGlobe.MAP_PRECISION; sb.Append(Mathf.RoundToInt(point.x).ToString() + ","); sb.Append(Mathf.RoundToInt(point.y).ToString()); } } } return(sb.ToString()); }
/// <summary> /// Creates a new province with the given region /// </summary> public void ProvinceCreate(Region region) { if (region == null) { return; } // Remove region from source entity IAdminEntity entity = region.entity; if (entity != null) { entity.regions.Remove(region); Country country; // Refresh entity definition if (region.entity is Country) { int countryIndex = _map.GetCountryIndex((Country)region.entity); country = _map.countries [countryIndex]; _map.RefreshCountryGeometry(country); } else { int provinceIndex = map.GetProvinceIndex((Province)region.entity); country = _map.countries [_map.provinces [provinceIndex].countryIndex]; _map.RefreshProvinceGeometry(provinceIndex); } } provinceIndex = map.provinces.Length; provinceRegionIndex = 0; string newProvinceName = GetProvinceUniqueName("New Province"); Province newProvince = new Province(newProvinceName, countryIndex, map.GetUniqueId(new List <IExtendableAttribute> (map.provinces))); Region newRegion = new Region(newProvince, 0); newRegion.UpdatePointsAndRect(region.points); newProvince.regions = new List <Region> (); newProvince.regions.Add(newRegion); map.ProvinceAdd(newProvince); map.RefreshProvinceDefinition(provinceIndex, false); // Update cities List <City> cities = _map.GetCities(region); int citiesCount = cities.Count; if (citiesCount > 0) { for (int k = 0; k < citiesCount; k++) { if (cities [k].province != newProvinceName) { cities [k].province = newProvinceName; cityChanges = true; } } } lastProvinceCount = -1; GUIProvinceName = newProvince.name; SyncGUIProvinceSelection(); ProvinceRegionSelect(); provinceChanges = true; }
/// <summary> /// Used internally by the Map Editor. It will recalculate de boundaries and optimize frontiers based on new data of countries array /// </summary> public void RefreshCountryGeometry(IAdminEntity country) { float maxVol = 0; if (country.regions == null) { return; } int regionCount = country.regions.Count; Vector2 min = Misc.Vector2one * 10; Vector2 max = -min; Vector2 minCountry = Misc.Vector2one * 10; Vector2 maxCountry = -minCountry; for (int r = 0; r < regionCount; r++) { Region countryRegion = country.regions [r]; if (countryRegion.points == null) { continue; } countryRegion.entity = country; // just in case one country has been deleted countryRegion.regionIndex = r; // just in case a region has been deleted int coorCount = countryRegion.points.Length; min.x = min.y = 10f; max.x = max.y = -10; for (int c = 0; c < coorCount; c++) { float x = countryRegion.points [c].x; float y = countryRegion.points [c].y; if (x < min.x) { min.x = x; } if (x > max.x) { max.x = x; } if (y < min.y) { min.y = y; } if (y > max.y) { max.y = y; } } FastVector.Average(ref min, ref max, ref countryRegion.center); // countryRegion.center = (min + max) * 0.5f; // Calculate country bounding rect if (min.x < minCountry.x) { minCountry.x = min.x; } if (min.y < minCountry.y) { minCountry.y = min.y; } if (max.x > maxCountry.x) { maxCountry.x = max.x; } if (max.y > maxCountry.y) { maxCountry.y = max.y; } // Calculate bounding rect countryRegion.rect2D = new Rect(min.x, min.y, max.x - min.x, max.y - min.y); countryRegion.rect2DArea = countryRegion.rect2D.width * countryRegion.rect2D.height; float vol = FastVector.SqrDistance(ref max, ref min); // (max - min).sqrMagnitude; if (vol > maxVol) { maxVol = vol; country.mainRegionIndex = r; country.center = countryRegion.center; } } country.regionsRect2D = new Rect(minCountry.x, minCountry.y, Math.Abs(maxCountry.x - minCountry.x), Mathf.Abs(maxCountry.y - minCountry.y)); }
/// <summary> /// For each two points, locate a third point belonging to a different region which is not conected to them and below threshold. Add a new point at that position. /// </summary> void PolishRegionJoints(Region region) { int regionPointsCount = region.points.Length; int entitiesCount = entities.Count; Vector2 midP = Misc.Vector3zero; for (int k = 0; k < regionPointsCount; k++) { Vector2 p0 = region.points [k]; int nextPointIndex = k == regionPointsCount - 1 ? 0 : k + 1; Vector2 p1 = region.points [nextPointIndex]; float p01d = (p0.x - p1.x) * (p0.x - p1.x) + (p0.y - p1.y) * (p0.y - p1.y); bool snapped = false; for (int e = 0; e < entitiesCount && !snapped; e++) { IAdminEntity entity = entities [e]; if (entity.regions == null) { continue; } int entityRegionsCount = entity.regions.Count; for (int r = 0; r < entityRegionsCount && !snapped; r++) { Region entityRegion = entity.regions [r]; if (entityRegion == region) { continue; } int entityRegionsPointsCount = entityRegion.points.Length; for (int j = 0; j < entityRegionsPointsCount; j++) { Vector2 v = entityRegion.points [j]; float d = (v.x - p0.x) * (v.x - p0.x) + (v.y - p0.y) * (v.y - p0.y); if (d > 0 && d < p01d) { d = (v.x - p1.x) * (v.x - p1.x) + (v.y - p1.y) * (v.y - p1.y); if (d > 0 && d < p01d) { // make sure center of triangle is not contained by any region (that would mean the change would create overlap) midP.x = (p0.x + p1.x + v.x) / 3f; midP.y = (p0.y + p1.y + v.y) / 3f; if (!AnyRegionContainsPoint(midP)) { // Insert point v in k+1 position int rpCount = region.points.Length; Vector2[] newPoints = new Vector2[rpCount + 1]; int np = -1; for (int i = 0; i < rpCount; i++) { ++np; if (np == nextPointIndex) { newPoints [np] = v; np++; } newPoints [np] = region.points [i]; } region.points = newPoints; snapped = true; regionPointsCount++; break; } } } } } } } }
/// <summary> /// Moves points of other regions towards current frontier /// </summary> public bool Magnet(Vector3 position, float circleSize) { if (entityIndex < 0 || entityIndex >= entities.Length) { return(false); } Region currentRegion = entities [entityIndex].regions [regionIndex]; float circleSizeSqr = circleSize * circleSize; Dictionary <Vector3, bool> attractorsUse = new Dictionary <Vector3, bool> (); // Attract points of other regions/countries List <Region> regions = new List <Region> (); for (int c = 0; c < entities.Length; c++) { IAdminEntity entity = entities [c]; if (entity.regions == null) { continue; } for (int r = 0; r < entity.regions.Count; r++) { if (c != entityIndex || r != regionIndex) { regions.Add(entities [c].regions [r]); } } } if (editingMode == EDITING_MODE.PROVINCES) { // Also add regions of current country and neighbours for (int r = 0; r < map.countries [countryIndex].regions.Count; r++) { Region region = map.countries [countryIndex].regions [r]; regions.Add(region); for (int n = 0; n < region.neighbours.Count; n++) { Region nregion = region.neighbours [n]; if (!regions.Contains(nregion)) { regions.Add(nregion); } } } } bool changes = false; Vector3 goodAttractor = Misc.Vector3zero; for (int r = 0; r < regions.Count; r++) { Region region = regions [r]; bool changesInThisRegion = false; for (int p = 0; p < region.spherePoints.Length; p++) { Vector3 rp = region.spherePoints [p]; float dist = (rp - position).sqrMagnitude; if (dist < circleSizeSqr) { float minDist = float.MaxValue; int nearest = -1; for (int a = 0; a < currentRegion.spherePoints.Length; a++) { Vector3 attractor = currentRegion.spherePoints [a]; dist = (rp - attractor).sqrMagnitude; if (dist < circleSizeSqr && dist < minDist) { minDist = dist; nearest = a; goodAttractor = attractor; } } if (nearest >= 0) { changes = true; // Check if this attractor is being used by other point bool used = attractorsUse.ContainsKey(goodAttractor); if (!used || magnetAgressiveMode) { region.spherePoints [p] = goodAttractor; if (!used) { attractorsUse.Add(goodAttractor, true); } changesInThisRegion = true; } } } } if (changesInThisRegion) { // Remove duplicate points in this region Dictionary <Vector3, bool> repeated = new Dictionary <Vector3, bool> (); for (int k = 0; k < region.spherePoints.Length; k++) { if (!repeated.ContainsKey(region.spherePoints [k])) { repeated.Add(region.spherePoints [k], true); } } region.spherePoints = new List <Vector3> (repeated.Keys).ToArray(); } } return(changes); }
public void SplitHorizontally() { if (entityIndex < 0 || entityIndex >= entities.Length) { return; } IAdminEntity currentEntity = entities [entityIndex]; Region currentRegion = currentEntity.regions [regionIndex]; Vector2 center = currentRegion.center; List <Vector3> half1 = new List <Vector3> (); List <Vector3> half2 = new List <Vector3> (); int prevSide = 0; for (int k = 0; k < currentRegion.points.Length; k++) { Vector3 p = currentRegion.points [k]; if (p.y > currentRegion.center.y) { half1.Add(p); if (prevSide == -1) { half2.Add(p); } prevSide = 1; } if (p.y <= currentRegion.center.y) { half2.Add(p); if (prevSide == 1) { half1.Add(p); } prevSide = -1; } } // Setup new entity IAdminEntity newEntity; if (currentEntity is Country) { newEntity = new Country("New " + currentEntity.name, ((Country)currentEntity).continent); } else { newEntity = new Province("New " + currentEntity.name, ((Province)currentEntity).countryIndex); newEntity.regions = new List <Region> (); } EntityAdd(newEntity); // Update polygons Region newRegion = new Region(newEntity, 0); if (entities [countryIndex].center.y > center.y) { currentRegion.points = half1.ToArray(); newRegion.points = half2.ToArray(); } else { currentRegion.points = half2.ToArray(); newRegion.points = half1.ToArray(); } newEntity.regions.Add(newRegion); // Refresh old entity and selects the new if (currentEntity is Country) { map.RefreshCountryDefinition(countryIndex, highlightedRegions); countryIndex = map.countries.Length - 1; countryRegionIndex = 0; } else { map.RefreshProvinceDefinition(provinceIndex); provinceIndex = map.provinces.Length - 1; provinceRegionIndex = 0; } // Refresh lines highlightedRegions.Add(newRegion); RedrawFrontiers(); map.RedrawMapLabels(); }
IEnumerator AssignRegionCenters(IAdminEntity[] entities) { List <Region> regions = new List <Region> (); for (int k = 0; k < entities.Length; k++) { IAdminEntity entity = entities [k]; int rCount = entity.regions.Count; for (int r = 0; r < rCount; r++) { Region region = entity.regions [r]; if (region.points.Length > 0) { regions.Add(region); } } } regions.Sort((Region x, Region y) => { return(y.rect2DArea.CompareTo(x.rect2DArea)); }); int regionsCount = regions.Count; for (int r = 0; r < regionsCount; r++) { Region region = regions [r]; int cellIndex = _map.GetCellIndex(region.center); if (cellIndex >= 0 && procCells [cellIndex].entityIndex < 0) { IAdminEntity entity = region.entity; if (entity is Country) { procCells [cellIndex].entityIndex = _map.GetCountryIndex((Country)region.entity); } else { procCells [cellIndex].entityIndex = _map.GetProvinceIndex((Province)region.entity); } procCells [cellIndex].entityRegion = region; } } // Pass 2: iterate all frontier points for (int c = 0; c < entities.Length; c++) { if (c % 10 == 0) { if (hexifyContext.progress != null) { if (hexifyContext.progress((float)c / entities.Length, hexifyContext.title, "Pass 2/6: assigning centers...")) { cancelled = true; hexifyContext.finish(true); yield break; } } yield return(null); } IAdminEntity entity = entities [c]; int rCount = entity.regions.Count; for (int cr = 0; cr < rCount; cr++) { Region region = entity.regions [cr]; for (int p = 0; p < region.points.Length; p++) { int cellIndex = _map.GetCellIndex(region.points [p]); if (cellIndex >= 0 && procCells [cellIndex].entityIndex < 0) { procCells [cellIndex].entityIndex = c; procCells [cellIndex].entityRegion = region; } } } } }
/// <summary> /// Exports the geographic data in packed string format with reduced quality. /// </summary> public string GetCountryGeoDataLowQuality() { // step 1: duplicate data IAdminEntity[] entities; if (editingMode == EDITING_MODE.COUNTRIES) { entities = map.countries; } else { entities = map.provinces; } List <IAdminEntity> entities1 = new List <IAdminEntity> (entities); for (int k = 0; k < entities1.Count; k++) { entities1 [k].regions = new List <Region> (entities1 [k].regions); for (int r = 0; r < entities [k].regions.Count; r++) { entities1 [k].regions [r].points = new List <Vector3> (entities1 [k].regions [r].points).ToArray(); } } // step 2: ensure near points between neighbours float MAX_DIST = 0.00000001f; // int join = 0; for (int k = 0; k < entities1.Count; k++) { for (int r = 0; r < entities1 [k].regions.Count; r++) { Region region1 = entities1 [k].regions [r]; for (int p = 0; p < entities1 [k].regions [r].points.Length; p++) { // Search near points for (int k2 = 0; k2 < region1.neighbours.Count; k2++) { for (int r2 = 0; r2 < entities1 [k2].regions.Count; r2++) { Region region2 = entities1 [k2].regions [r2]; for (int p2 = 0; p2 < entities1 [k2].regions [r2].points.Length; p2++) { float dist = (region1.points [p].x - region2.points [p2].x) * (region1.points [p].x - region2.points [p2].x) + (region1.points [p].y - region2.points [p2].y) * (region1.points [p].y - region2.points [p2].y); if (dist < MAX_DIST) { region2.points [p2] = region1.points [p]; // join++; } } } } } } } // if (join>0) // Debug.Log (join + " points fused."); // step 2: simplify Dictionary <Vector2, bool> frontiersHit = new Dictionary <Vector2, bool> (); List <IAdminEntity> entities2 = new List <IAdminEntity> (entities1.Count); int savings = 0, totalPoints = 0; float FACTOR = 1000f; for (int k = 0; k < entities1.Count; k++) { IAdminEntity refEntity = entities1 [k]; IAdminEntity newEntity; if (refEntity is Country) { newEntity = new Country(refEntity.name, ((Country)refEntity).continent); } else { newEntity = new Province(refEntity.name, ((Province)refEntity).countryIndex); } for (int r = 0; r < refEntity.regions.Count; r++) { Region region = refEntity.regions [r]; int numPoints = region.points.Length; totalPoints += numPoints; List <Vector3> points = new List <Vector3> (numPoints); frontiersHit.Clear(); Vector3[] blockyPoints = new Vector3[numPoints]; for (int p = 0; p < numPoints; p++) { blockyPoints [p] = new Vector2(Mathf.RoundToInt(region.points [p].x * FACTOR) / FACTOR, Mathf.RoundToInt(region.points [p].y * FACTOR) / FACTOR); } points.Add(region.points [0] * WorldMap2D.MAP_PRECISION); for (int p = 1; p < numPoints - 1; p++) { if (blockyPoints [p - 1].y == blockyPoints [p].y && blockyPoints [p].y == blockyPoints [p + 1].y || blockyPoints [p - 1].x == blockyPoints [p].x && blockyPoints [p].x == blockyPoints [p + 1].x) { savings++; continue; } if (!frontiersHit.ContainsKey(blockyPoints [p])) // add neighbour references { frontiersHit.Add(blockyPoints [p], true); points.Add(region.points [p] * WorldMap2D.MAP_PRECISION); } else { savings++; } } points.Add(region.points [numPoints - 1] * WorldMap2D.MAP_PRECISION); if (points.Count >= 5) { Region newRegion = new Region(newEntity, newEntity.regions.Count); newRegion.points = points.ToArray(); newEntity.regions.Add(newRegion); } } if (newEntity.regions.Count > 0) { entities2.Add(newEntity); } } Debug.Log(savings + " points removed of " + totalPoints + " (" + (((float)savings / totalPoints) * 100.0f).ToString("F1") + "%)"); StringBuilder sb = new StringBuilder(); for (int k = 0; k < entities2.Count; k++) { IAdminEntity entity = entities2 [k]; if (k > 0) { sb.Append("|"); } sb.Append(entity.name + "$"); if (entity is Country) { sb.Append(((Country)entity).continent + "$"); } else { sb.Append(map.countries [((Province)entity).countryIndex].name + "$"); } for (int r = 0; r < entity.regions.Count; r++) { if (r > 0) { sb.Append("*"); } Region region = entity.regions [r]; for (int p = 0; p < region.points.Length; p++) { if (p > 0) { sb.Append(";"); } Vector2 point = region.points [p]; sb.Append(point.x.ToString(CultureInfo.InvariantCulture) + ","); sb.Append(point.y.ToString(CultureInfo.InvariantCulture)); } } } return(sb.ToString()); }
/// <summary> /// Creates a new country based on a given region. Existing region is removed from its source entity. /// </summary> /// <param name="region">Region.</param> public void CountryCreate(Region region) { // Remove region from source entity IAdminEntity entity = region.entity; entity.regions.Remove(region); Country country; // Refresh entity definition if (region.entity is Country) { int cIndex = _map.GetCountryIndex((Country)region.entity); country = _map.countries [cIndex]; _map.RefreshCountryGeometry(cIndex); } else { int provinceIndex = map.GetProvinceIndex((Province)region.entity); country = _map.countries [_map.provinces [provinceIndex].countryIndex]; _map.RefreshProvinceGeometry(provinceIndex); } // Create the new country indeed string uniqueName = GetCountryUniqueName(country.name); Country newCountry = new Country(uniqueName, country.continent); if (entity is Country) { newCountry.regions.Add(region); } else { Region newRegion = new Region(newCountry, 0); newRegion.UpdatePointsAndRect(region.points); newCountry.regions.Add(newRegion); } countryIndex = map.CountryAdd(newCountry); countryRegionIndex = 0; map.RefreshCountryDefinition(countryIndex, null); lastCountryCount = -1; GUICountryName = ""; ReloadCountryNames(); countryChanges = true; // Update cities List <City> cities = _map.GetCities(region); if (cities.Count > 0) { cityChanges = true; for (int k = 0; k < cities.Count; k++) { cities [k].countryIndex = countryIndex; } } // Update mount points List <MountPoint> mp = _map.GetMountPoints(region); if (mp.Count > 0) { mountPointChanges = true; for (int k = 0; k < mp.Count; k++) { mp [k].countryIndex = countryIndex; } } // Transfer any contained province if (entity is Country) { List <Province> provinces = _map.GetProvinces(region); for (int k = 0; k < provinces.Count; k++) { Province prov = provinces [k]; if (prov.regions == null) { _map.ReadProvincePackedString(prov); } if (prov.regions == null) { continue; } int pIndex = _map.GetProvinceIndex(prov); ProvinceTransferTo(countryIndex, pIndex); provinceChanges = true; } } map.Redraw(); CountryRegionSelect(); }
public void SplitVertically() { if (entityIndex < 0 || entityIndex >= entities.Length) { return; } IAdminEntity currentEntity = entities[entityIndex]; Region currentRegion = currentEntity.regions[regionIndex]; Vector3 center = currentRegion.center; List <Vector3> half1 = new List <Vector3>(); List <Vector3> half2 = new List <Vector3>(); int prevSide = 0; double centerX = (currentRegion.minMaxLon.x + currentRegion.minMaxLon.y) * 0.5; for (int k = 0; k < currentRegion.latlon.Length; k++) { Vector3 p = currentRegion.points[k]; PolygonPoint latlon = currentRegion.latlon[k]; if (latlon.Y > centerX) // compare longitudes { half1.Add(p); if (prevSide == -1) { half2.Add(p); } prevSide = 1; } if (latlon.Y <= centerX) { half2.Add(p); if (prevSide == 1) { half1.Add(p); } prevSide = -1; } } // Setup new entity IAdminEntity newEntity; if (currentEntity is Country) { newEntity = new Country("New " + currentEntity.name, ((Country)currentEntity).continent); } else { newEntity = new Province("New " + currentEntity.name, ((Province)currentEntity).countryIndex); newEntity.regions = new List <Region>(); } EntityAdd(newEntity); // Update polygons Region newRegion = new Region(newEntity, 0); if (entities[countryIndex].center.x > center.x) { currentRegion.points = half1.ToArray(); newRegion.points = half2.ToArray(); } else { currentRegion.points = half2.ToArray(); newRegion.points = half1.ToArray(); } UpdateLatLonFromPoints(currentRegion); UpdateLatLonFromPoints(newRegion); newEntity.regions.Add(newRegion); // Refresh old entity and selects the new if (currentEntity is Country) { map.RefreshCountryDefinition(countryIndex, highlightedRegions); countryIndex = map.countries.Length - 1; countryRegionIndex = 0; } else { map.RefreshProvinceDefinition(provinceIndex); provinceIndex = map.provinces.Length - 1; provinceRegionIndex = 0; } // Refresh lines highlightedRegions.Add(newRegion); RedrawFrontiers(); map.RedrawMapLabels(); }
/// <summary> /// Unpacks province geodata information. Used by Map Editor. /// </summary> /// <param name="province">Province.</param> public void ReadProvincePackedString(IAdminEntity entity) { Province province = (Province)entity; if (province == null || province.packedRegions == null) { return; } string[] regions = province.packedRegions.Split(SPLIT_SEP_ASTERISK, StringSplitOptions.RemoveEmptyEntries); int regionCount = regions.Length; province.regions = new List <Region> (regionCount); float maxVol = float.MinValue; Vector2 minProvince = Misc.Vector2one * 10; Vector2 maxProvince = -minProvince; for (int r = 0; r < regionCount; r++) { string[] coordinates = regions [r].Split(SPLIT_SEP_SEMICOLON, StringSplitOptions.RemoveEmptyEntries); int coorCount = coordinates.Length; Vector2 min = Misc.Vector2one * 10; Vector2 max = -min; Region provinceRegion = new Region(province, province.regions.Count); provinceRegion.points = new Vector2[coorCount]; for (int c = 0; c < coorCount; c++) { float x, y; GetPointFromPackedString(ref coordinates [c], out x, out y); if (x < min.x) { min.x = x; } if (x > max.x) { max.x = x; } if (y < min.y) { min.y = y; } if (y > max.y) { max.y = y; } provinceRegion.points [c].x = x; provinceRegion.points [c].y = y; } FastVector.Average(ref min, ref max, ref provinceRegion.center); provinceRegion.sanitized = true; province.regions.Add(provinceRegion); // Calculate province bounding rect if (min.x < minProvince.x) { minProvince.x = min.x; } if (min.y < minProvince.y) { minProvince.y = min.y; } if (max.x > maxProvince.x) { maxProvince.x = max.x; } if (max.y > maxProvince.y) { maxProvince.y = max.y; } provinceRegion.rect2D = new Rect(min.x, min.y, max.x - min.x, max.y - min.y); provinceRegion.rect2DArea = provinceRegion.rect2D.width * provinceRegion.rect2D.height; float vol = FastVector.SqrDistance(ref min, ref max); // (max - min).sqrMagnitude; if (vol > maxVol) { maxVol = vol; province.mainRegionIndex = r; province.center = provinceRegion.center; } } province.regionsRect2D = new Rect(minProvince.x, minProvince.y, Math.Abs(maxProvince.x - minProvince.x), Mathf.Abs(maxProvince.y - minProvince.y)); }
public void SplitHorizontally() { if (entityIndex < 0 || entityIndex >= entities.Length) { return; } IAdminEntity currentEntity = entities[entityIndex]; Region currentRegion = currentEntity.regions[regionIndex]; float centerY = currentRegion.latlonCenter.x; // (currentRegion.minMaxLon.x + currentRegion.minMaxLon.y)*0.5; List <Vector2> half1 = new List <Vector2>(); List <Vector2> half2 = new List <Vector2>(); int prevSide = 0; for (int k = 0; k < currentRegion.latlon.Length; k++) { Vector2 p = currentRegion.latlon[k]; if (p.x > centerY) { half1.Add(p); if (prevSide == -1) { half2.Add(p); } prevSide = 1; } else { half2.Add(p); if (prevSide == 1) { half1.Add(p); } prevSide = -1; } } // Setup new entity IAdminEntity newEntity; if (currentEntity is Country) { string name = map.GetCountryUniqueName("New " + currentEntity.name); newEntity = new Country(name, ((Country)currentEntity).continent); } else { string name = map.GetProvinceUniqueName("New " + currentEntity.name); newEntity = new Province(name, ((Province)currentEntity).countryIndex); newEntity.regions = new List <Region>(); } // Update polygons Region newRegion = new Region(newEntity, 0); if (entities[countryIndex].latlonCenter.x > centerY) { currentRegion.latlon = half1.ToArray(); newRegion.latlon = half2.ToArray(); } else { currentRegion.latlon = half2.ToArray(); newRegion.latlon = half1.ToArray(); } map.RegionSanitize(currentRegion); SmoothRegion(currentRegion); map.RegionSanitize(newRegion); SmoothRegion(newRegion); newEntity.regions.Add(newRegion); int newEntityIndex = EntityAdd(newEntity); // Refresh old entity and selects the new if (currentEntity is Country) { map.RefreshCountryDefinition(newEntityIndex, null); map.RefreshCountryDefinition(countryIndex, null); SplitCities(countryIndex, newRegion); ClearSelection(); RedrawFrontiers(); countryIndex = newEntityIndex; countryRegionIndex = 0; CountryRegionSelect(); countryChanges = true; cityChanges = true; } else { map.RefreshProvinceDefinition(newEntityIndex); map.RefreshProvinceDefinition(provinceIndex); highlightedRegions = null; ClearSelection(); RedrawFrontiers(); provinceIndex = newEntityIndex; provinceRegionIndex = 0; countryIndex = map.provinces[provinceIndex].countryIndex; countryRegionIndex = map.countries[countryIndex].mainRegionIndex; CountryRegionSelect(); ProvinceRegionSelect(); provinceChanges = true; } map.RedrawMapLabels(); }