/// <summary> /// Merges all provinces in each country so their number fits a given range /// </summary> /// <param name="min">Minimum number of provinces.</param> /// <param name="max">Maximum number of provinces.</param> public void ProvincesEqualize(int min, int max) { if (min < 1) { return; } if (max < min) { max = min; } map.showProvinces = true; map.drawAllProvinces = true; for (int c = 0; c < map.countries.Length; c++) { Country country = map.countries [c]; // if (country.name.Equals("Spain")) { // Debug.Log ("Spain!"); // } if (country == null || country.provinces == null) { continue; } int targetProvCount = UnityEngine.Random.Range(min, max); int provCount = country.provinces.Length; float provStartSize = 0; while (provCount > targetProvCount) { // Take the smaller province and merges with a neighbour float minAreaSize = float.MaxValue; int provinceIndex = -1; for (int p = 0; p < provCount; p++) { Province prov = country.provinces [p]; if (prov == null) { continue; } if (prov.regions == null) { map.ReadProvincePackedString(prov); } if (prov.regions == null || prov.regions.Count == 0 || prov.mainRegion.neighbours == null || prov.mainRegion.neighbours.Count == 0) { continue; } if (prov.regionsRect2DArea < minAreaSize && prov.regionsRect2DArea > provStartSize) { minAreaSize = prov.regionsRect2DArea; provinceIndex = map.GetProvinceIndex(prov); } } if (provinceIndex < 0) { break; } provStartSize = minAreaSize; // Get the smaller neighbour int neighbourIndex = -1; Province province = map.provinces [provinceIndex]; int neighbourCount = province.mainRegion.neighbours.Count; minAreaSize = float.MaxValue; for (int n = 0; n < neighbourCount; n++) { Region neighbour = province.mainRegion.neighbours [n]; Province neighbourProvince = (Province)neighbour.entity; if (neighbourProvince != null && neighbourProvince != province && neighbourProvince.countryIndex == c && neighbour.rect2DArea < minAreaSize) { int neighbourProvIndex = map.GetProvinceIndex(neighbourProvince); if (neighbourProvIndex >= 0) { minAreaSize = neighbour.rect2DArea; neighbourIndex = neighbourProvIndex; } } } if (neighbourIndex < 0) { continue; } // Merges province into neighbour string provinceSource = map.provinces [provinceIndex].name; string provinceTarget = map.provinces [neighbourIndex].name; if (!map.ProvinceTransferProvinceRegion(neighbourIndex, map.provinces [provinceIndex].mainRegion, false)) { Debug.LogWarning("Country: " + map.countries [c].name + " => " + provinceSource + " failed merge into " + provinceTarget + "."); break; } provCount = country.provinces.Length; } } map.Redraw(); provinceChanges = true; cityChanges = true; mountPointChanges = true; }
/// <summary> /// Makes provinceIndex absorb another province providing any of its regions. All regions are transfered to target province. /// This function is quite slow with high definition frontiers. /// </summary> /// <param name="provinceIndex">Province index of the conquering province.</param> /// <param name="sourceRegion">Source region of the loosing province.</param> public bool ProvinceTransferProvinceRegion(int provinceIndex, Region sourceProvinceRegion, bool redraw) { int sourceProvinceIndex = GetProvinceIndex((Province)sourceProvinceRegion.entity); if (provinceIndex < 0 || sourceProvinceIndex < 0 || provinceIndex == sourceProvinceIndex) { return(false); } // Transfer cities Province sourceProvince = provinces[sourceProvinceIndex]; Province targetProvince = provinces[provinceIndex]; if (sourceProvince.countryIndex != targetProvince.countryIndex) { // Transfer source province to target country province if (!CountryTransferProvinceRegion(targetProvince.countryIndex, sourceProvinceRegion)) { return(false); } } int cityCount = cities.Count; for (int k = 0; k < cityCount; k++) { if (_cities[k].countryIndex == sourceProvince.countryIndex && _cities[k].province.Equals(sourceProvince.name)) { _cities[k].province = targetProvince.name; } } // Transfer mount points int mountPointCount = mountPoints.Count; for (int k = 0; k < mountPointCount; k++) { if (mountPoints[k].provinceIndex == sourceProvinceIndex) { mountPoints[k].provinceIndex = provinceIndex; } } // Transfer regions if (sourceProvince.regions.Count > 0) { List <Region> targetRegions = new List <Region>(targetProvince.regions); for (int k = 0; k < sourceProvince.regions.Count; k++) { targetRegions.Add(sourceProvince.regions[k]); } targetProvince.regions = targetRegions; } // Fusion any adjacent regions that results from merge operation ProvinceMergeAdjacentRegions(targetProvince); RegionSanitize(targetProvince.regions, false); targetProvince.mainRegionIndex = 0; // will be updated on RefreshProvinceDefinition // Finish operation ProvinceDeleteAndDependencies(sourceProvinceIndex); if (provinceIndex > sourceProvinceIndex) { provinceIndex--; } if (redraw) { RefreshProvinceDefinition(provinceIndex); } else { RefreshProvinceGeometry(provinceIndex); } // ResortCountryProvinces(provinces[provinceIndex].countryIndex); return(true); }
void CreateProvincesMesh(int countryIndex, bool includeNeighbours) { // prepare a list with the countries to be drawn countryProvincesDrawnIndex = countryIndex; List <Country> targetCountries = new List <Country> (20); // add selected country targetCountries.Add(countries [countryIndex]); // add neighbour countries? if (includeNeighbours) { for (int k = 0; k < countries[countryIndex].regions.Count; k++) { List <Region> neighbours = countries [countryIndex].regions [k].neighbours; for (int n = 0; n < neighbours.Count; n++) { Country c = (Country)neighbours [n].entity; if (!targetCountries.Contains(c)) { targetCountries.Add(c); } } } } if (provinceFrontiersPoints == null) { provinceFrontiersPoints = new List <Vector3> (200000); } else { provinceFrontiersPoints.Clear(); } if (provinceFrontiersCacheHit == null) { provinceFrontiersCacheHit = new Dictionary <Vector3, Region> (200000); } else { provinceFrontiersCacheHit.Clear(); } for (int c = 0; c < targetCountries.Count; c++) { Country targetCountry = targetCountries[c]; if (targetCountry.provinces == null) { continue; } for (int p = 0; p < targetCountry.provinces.Length; p++) { Province province = targetCountry.provinces [p]; if (province.regions == null) // read province data the first time we need it { ReadProvincePackedString(province); } for (int r = 0; r < province.regions.Count; r++) { Region region = province.regions [r]; region.entity = province; region.regionIndex = r; region.neighbours.Clear(); int max = region.points.Length - 1; for (int i = 0; i < max; i++) { Vector3 p0 = region.points [i]; Vector3 p1 = region.points [i + 1]; Vector3 hc = p0 + p1; if (provinceFrontiersCacheHit.ContainsKey(hc)) { Region neighbour = provinceFrontiersCacheHit [hc]; if (neighbour != region) { if (!region.neighbours.Contains(neighbour)) { region.neighbours.Add(neighbour); neighbour.neighbours.Add(region); } } } else { provinceFrontiersCacheHit.Add(hc, region); provinceFrontiersPoints.Add(p0); provinceFrontiersPoints.Add(p1); } } // Close the polygon provinceFrontiersPoints.Add(region.points [max]); provinceFrontiersPoints.Add(region.points [0]); } } } int meshGroups = (provinceFrontiersPoints.Count / 65000) + 1; int meshIndex = -1; provinceFrontiersIndices = new int[meshGroups][]; provinceFrontiers = new Vector3[meshGroups][]; for (int k = 0; k < provinceFrontiersPoints.Count; k += 65000) { int max = Mathf.Min(provinceFrontiersPoints.Count - k, 65000); provinceFrontiers [++meshIndex] = new Vector3[max]; provinceFrontiersIndices [meshIndex] = new int[max]; for (int j = k; j < k + max; j++) { provinceFrontiers [meshIndex] [j - k] = provinceFrontiersPoints [j]; provinceFrontiersIndices [meshIndex] [j - k] = j - k; } } // Create province borders container GameObject provinceObj = new GameObject(countryIndex.ToString()); provinceObj.transform.SetParent(provincesLayer.transform, false); provinceObj.transform.localPosition = MiscVector.Vector3zero; provinceObj.transform.localRotation = Quaternion.Euler(MiscVector.Vector3zero); for (int k = 0; k < provinceFrontiers.Length; k++) { GameObject flayer = new GameObject("flayer"); flayer.transform.SetParent(provinceObj.transform, false); flayer.transform.localPosition = MiscVector.Vector3zero; flayer.transform.localRotation = Quaternion.Euler(MiscVector.Vector3zero); Mesh mesh = new Mesh(); mesh.vertices = provinceFrontiers [k]; mesh.SetIndices(provinceFrontiersIndices [k], MeshTopology.Lines, 0); mesh.RecalculateBounds(); mesh.hideFlags = HideFlags.DontSave; MeshFilter mf = flayer.AddComponent <MeshFilter> (); mf.sharedMesh = mesh; MeshRenderer mr = flayer.AddComponent <MeshRenderer> (); mr.receiveShadows = false; mr.reflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.Off; mr.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; mr.useLightProbes = false; mr.sharedMaterial = provincesMat; } if (_showOutline) { Country country = countries[countryIndex]; Region region = country.regions[country.mainRegionIndex]; provinceCountryOutlineRef = DrawCountryRegionOutline(region, provinceObj); } }
bool GetProvinceUnderMouse(int countryIndex, Vector3 spherePoint, out int provinceIndex, out int regionIndex) { float startingDistance = 0; provinceIndex = regionIndex = -1; Country country = countries[countryIndex]; if (country.provinces == null) { return(false); } int provincesCount = country.provinces.Length; if (provincesCount == 0) { return(false); } PolygonPoint latlonPos = GetLatLonFromSpherePoint(spherePoint); float maxArea = float.MaxValue; // Is this the same province currently selected? if (_provinceHighlightedIndex >= 0 && _provinceRegionHighlightedIndex >= 0 && provinces[_provinceHighlightedIndex].countryIndex == countryIndex) { Region region = provinces[_provinceHighlightedIndex].regions[_provinceRegionHighlightedIndex]; // if (ContainsPoint2D (region.latlon, latlonPos.X, latlonPos.Y)) { if (region.ContainsPoint(latlonPos.X, latlonPos.Y)) { maxArea = region.entity.mainRegionArea; // cannot return yet - need to check if any other province (smaller than this) could be highlighted // provinceIndex = _provinceHighlightedIndex; // regionIndex = _provinceRegionHighlightedIndex; // return true; } } // Check other provinces for (int tries = 0; tries < 75; tries++) { float minDist = float.MaxValue; for (int p = 0; p < provincesCount; p++) { Province province = country.provinces[p]; if (province.regions == null || province.mainRegionArea > maxArea) { continue; } for (int pr = 0; pr < province.regions.Count; pr++) { Vector3 regionCenter = province.regions [pr].center; float dist = (regionCenter - spherePoint).sqrMagnitude; if (dist > startingDistance && dist < minDist) { minDist = dist; provinceIndex = GetProvinceIndex(province); regionIndex = pr; } } } // Check if this region is visible and the mouse is inside if (provinceIndex >= 0) { Region region = provinces[provinceIndex].regions[regionIndex]; if (region.ContainsPoint(latlonPos.X, latlonPos.Y)) { return(true); } } // Continue searching but farther centers startingDistance = minDist; } return(false); }
public void ReadProvincePackedString(Province province) { string[] regions = province.packedRegions.Split(new char[] { '*' }, StringSplitOptions.RemoveEmptyEntries); int regionCount = regions.Length; float maxArea = 0; province.regions = new List <Region> (regionCount); List <Vector3> regionPoints = new List <Vector3>(10000); List <PolygonPoint> latlons = new List <PolygonPoint>(10000); for (int r = 0; r < regionCount; r++) { string[] coordinates = regions [r].Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); int coorCount = coordinates.Length; Vector2 minMaxLat = new Vector2(float.MaxValue, float.MinValue); Vector4 minMaxLon = new Vector2(float.MaxValue, float.MinValue); Region provinceRegion = new Region(province, province.regions.Count); provinceRegion.points = new Vector3[coorCount]; provinceRegion.latlon = new PolygonPoint[coorCount]; regionPoints.Clear(); latlons.Clear(); int regionPointsIndex = -1; for (int c = 0; c < coorCount; c++) { float lat, lon; GetLatLonFromPackedString(coordinates[c], out lat, out lon); // Convert to sphere coordinates Vector3 point = GetSpherePointFromLatLon(lat, lon); if (regionPointsIndex > -1 && regionPoints[regionPointsIndex] == point) { continue; } regionPointsIndex++; regionPoints.Add(point); latlons.Add(new PolygonPoint(lat, lon)); if (lat < minMaxLat.x) { minMaxLat.x = lat; } if (lat > minMaxLat.y) { minMaxLat.y = lat; } if (lon < minMaxLon.x) { minMaxLon.x = lon; } if (lon > minMaxLon.y) { minMaxLon.y = lon; } } provinceRegion.points = regionPoints.ToArray(); provinceRegion.latlon = latlons.ToArray(); provinceRegion.minMaxLat = minMaxLat; provinceRegion.minMaxLon = minMaxLon; provinceRegion.rect2D = GetRect2DFromMinMaxLatLon(minMaxLat, minMaxLon); Vector2 midLatLon = new Vector2((minMaxLat.x + minMaxLat.y) / 2, (minMaxLon.x + minMaxLon.y) / 2); Vector3 normRegionCenter = GetSpherePointFromLatLon(midLatLon.x, midLatLon.y); provinceRegion.center = normRegionCenter; float area = provinceRegion.rect2D.size.sqrMagnitude; if (area > maxArea) { maxArea = area; province.mainRegionIndex = r; province.mainRegionArea = provinceRegion.rect2D.width * provinceRegion.rect2D.height; province.center = provinceRegion.center; } province.regions.Add(provinceRegion); } // Debug.Log ("Total read: " + (DateTime.Now - start).TotalMilliseconds); }
/// <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 RefreshProvinceDefinition(int provinceIndex) { lastProvinceLookupCount = -1; if (provinceIndex < 0 || provinceIndex >= provinces.Length) { return; } float maxArea = 0; Province province = provinces [provinceIndex]; if (province.regions == null) { ReadProvincePackedString(province); // safe check for specific map editor situations } int regionCount = province.regions.Count; for (int r = 0; r < regionCount; r++) { Vector2 minMaxLat = new Vector2(float.MaxValue, float.MinValue); Vector4 minMaxLon = new Vector2(float.MaxValue, float.MinValue); Region provinceRegion = province.regions[r]; provinceRegion.entity = province; provinceRegion.regionIndex = r; int coorCount = provinceRegion.latlon.Length; for (int c = 0; c < coorCount; c++) { PolygonPoint latlon = provinceRegion.latlon[c]; latlon.Reset(); float lat = latlon.Xf; float lon = latlon.Yf; if (lat < minMaxLat.x) { minMaxLat.x = lat; } if (lat > minMaxLat.y) { minMaxLat.y = lat; } if (lon < minMaxLon.x) { minMaxLon.x = lon; } if (lon > minMaxLon.y) { minMaxLon.y = lon; } } provinceRegion.minMaxLat = minMaxLat; provinceRegion.minMaxLon = minMaxLon; provinceRegion.rect2D = GetRect2DFromMinMaxLatLon(minMaxLat, minMaxLon); Vector2 midLatLon = new Vector2((minMaxLat.x + minMaxLat.y) / 2, (minMaxLon.x + minMaxLon.y) / 2); Vector3 normRegionCenter = GetSpherePointFromLatLon(midLatLon.x, midLatLon.y); provinceRegion.center = normRegionCenter; float area = provinceRegion.rect2D.size.sqrMagnitude; if (area > maxArea) { maxArea = area; province.mainRegionIndex = r; province.center = provinceRegion.center; } } DrawProvinces(provinces [provinceIndex].countryIndex, true, true); }
GameObject GenerateProvinceRegionSurface(int provinceIndex, int regionIndex, Material material) { if (provinceIndex < 0 || provinceIndex >= provinces.Length) { return(null); } if (provinces[provinceIndex].regions == null) { ReadProvincePackedString(provinces[provinceIndex]); } if (provinces[provinceIndex].regions == null || regionIndex < 0 || regionIndex >= provinces[provinceIndex].regions.Count) { return(null); } Province province = provinces[provinceIndex]; Region region = province.regions [regionIndex]; // Triangulate to get the polygon vertex indices Poly2Tri.Polygon poly = new Poly2Tri.Polygon(region.latlon); if (_enableProvinceEnclaves && regionIndex == province.mainRegionIndex) { ProvinceSubstractProvinceEnclaves(provinceIndex, region, poly); } // Antarctica, Saskatchewan (Canada), British Columbia (Canada), Krasnoyarsk (Russia) - special cases due to its geometry float step = _frontiersDetail == FRONTIERS_DETAIL.High ? 2f: 5f; List <TriangulationPoint> steinerPoints = new List <TriangulationPoint>(); float x0 = region.latlonRect2D.min.x + step / 2f; float x1 = region.latlonRect2D.max.x - step / 2f; float y0 = region.latlonRect2D.min.y + step / 2f; float y1 = region.latlonRect2D.max.y - step / 2f; for (float x = x0; x < x1; x += step) { for (float y = y0; y < y1; y += step) { float xp = x + UnityEngine.Random.Range(-0.0001f, 0.0001f); float yp = y + UnityEngine.Random.Range(-0.0001f, 0.0001f); if (region.Contains(xp, yp)) { steinerPoints.Add(new TriangulationPoint(xp, yp)); // GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Sphere); // obj.transform.SetParent(WorldMapGlobe.instance.transform, false); // obj.transform.localScale = Vector3.one * 0.01f; // obj.transform.localPosition = Conversion.GetSpherePointFromLatLon(new Vector2(x,y)) * 1.01f; } } } if (steinerPoints.Count > 0) { poly.AddSteinerPoints(steinerPoints); } P2T.Triangulate(poly); int flip1, flip2; if (_earthInvertedMode) { flip1 = 2; flip2 = 1; } else { flip1 = 1; flip2 = 2; } int triCount = poly.Triangles.Count; Vector3[] revisedSurfPoints = new Vector3[triCount * 3]; for (int k = 0; k < triCount; k++) { DelaunayTriangle dt = poly.Triangles[k]; revisedSurfPoints[k * 3] = Conversion.GetSpherePointFromLatLon(dt.Points[0].X, dt.Points[0].Y); revisedSurfPoints[k * 3 + flip1] = Conversion.GetSpherePointFromLatLon(dt.Points[1].X, dt.Points[1].Y); revisedSurfPoints[k * 3 + flip2] = Conversion.GetSpherePointFromLatLon(dt.Points[2].X, dt.Points[2].Y); } int revIndex = revisedSurfPoints.Length - 1; // Generate surface mesh int cacheIndex = GetCacheIndexForProvinceRegion(provinceIndex, regionIndex); string cacheIndexSTR = cacheIndex.ToString(); // Deletes potential residual surface Transform t = surfacesLayer.transform.Find(cacheIndexSTR); if (t != null) { DestroyImmediate(t.gameObject); } GameObject surf = Drawing.CreateSurface(cacheIndexSTR, revisedSurfPoints, revIndex, material); // Rect rect = new Rect(0,0,0,0); // GameObject surf = Drawing.CreateSurface (cacheIndexSTR, poly, material, rect, Vector2.zero, Vector2.zero, 0); surf.transform.SetParent(surfacesLayer.transform, false); surf.transform.localPosition = Misc.Vector3zero; if (_earthInvertedMode) { surf.transform.localScale = Misc.Vector3one * 0.998f; } if (surfaces.ContainsKey(cacheIndex)) { surfaces.Remove(cacheIndex); } surfaces.Add(cacheIndex, surf); return(surf); }
/// <summary> /// Calculates correct province for cities /// </summary> public void FixOrphanCities() { if (_map.provinces == null) { return; } int countryAssigned = 0; int provinceAssigned = 0; int cityCount = _map.cities.Count; for (int c = 0; c < cityCount; c++) { City city = _map.cities[c]; if (city.countryIndex == -1) { for (int k = 0; k < _map.countries.Length; k++) { Country co = _map.countries[k]; if (co.regions == null) { continue; } int regCount = co.regions.Count; for (int kr = 0; kr < regCount; kr++) { if (co.regions[kr].Contains(city.latlon)) { city.countryIndex = k; countryAssigned++; k = 100000; break; } } } } if (city.countryIndex == -1) { float minDist = float.MaxValue; for (int k = 0; k < _map.countries.Length; k++) { Country co = _map.countries[k]; if (co.regions == null) { continue; } int regCount = co.regions.Count; for (int kr = 0; kr < regCount; kr++) { float dist = (co.regions[kr].latlonCenter - city.latlon).sqrMagnitude; if (dist < minDist) { minDist = dist; city.countryIndex = k; countryAssigned++; } } } } if (city.province.Length == 0) { Country country = _map.countries[city.countryIndex]; if (country.provinces == null) { continue; } for (int p = 0; p < country.provinces.Length; p++) { Province province = country.provinces[p]; if (province.regions == null) { _map.ReadProvincePackedString(province); } if (province.regions == null) { continue; } int regCount = province.regions.Count; for (int pr = 0; pr < regCount; pr++) { Region reg = province.regions[pr]; if (reg.Contains(city.latlon)) { city.province = province.name; p = 100000; break; } } } } } for (int c = 0; c < cityCount; c++) { City city = _map.cities[c]; if (city.province.Length == 0) { float minDist = float.MaxValue; int pg = -1; for (int p = 0; p < _map.provinces.Length; p++) { Province province = _map.provinces[p]; if (province.regions == null) { _map.ReadProvincePackedString(province); } if (province.regions == null) { continue; } int regCount = province.regions.Count; for (int pr = 0; pr < regCount; pr++) { Region pregion = province.regions[pr]; for (int prp = 0; prp < pregion.latlon.Length; prp++) { float dist = (city.latlon - pregion.latlon[prp]).sqrMagnitude; if (dist < minDist) { minDist = dist; pg = p; } } } } if (pg >= 0) { city.province = _map.provinces[pg].name; provinceAssigned++; } } } Debug.Log(countryAssigned + " cities were assigned a new country."); Debug.Log(provinceAssigned + " cities were assigned a new province."); if (countryAssigned > 0 || provinceAssigned > 0) { cityChanges = true; } }
bool DrawProvinces(List <Country> targetCountries, bool forceRefresh) { if (provinceFrontiersPoints == null) { provinceFrontiersPoints = new List <Vector3> (200000); } else { provinceFrontiersPoints.Clear(); } if (provinceFrontiersCacheHit == null) { provinceFrontiersCacheHit = new Dictionary <Vector3, Region> (200000); } else { provinceFrontiersCacheHit.Clear(); } int countriesCount = targetCountries.Count; for (int c = 0; c < countriesCount; c++) { Country targetCountry = targetCountries[c]; if (targetCountry.provinces == null) { continue; } for (int p = 0; p < targetCountry.provinces.Length; p++) { Province province = targetCountry.provinces [p]; int regCount = province.regions.Count; for (int r = 0; r < regCount; r++) { Region region = province.regions [r]; region.entity = province; region.regionIndex = r; region.neighbours.Clear(); int max = region.latlon.Length - 1; for (int i = 0; i < max; i++) { Vector3 p0 = region.spherePoints [i]; Vector3 p1 = region.spherePoints [i + 1]; Vector3 hc = p0 + p1; if (provinceFrontiersCacheHit.ContainsKey(hc)) { Region neighbour = provinceFrontiersCacheHit [hc]; if (neighbour != region) { if (!region.neighbours.Contains(neighbour)) { region.neighbours.Add(neighbour); neighbour.neighbours.Add(region); } } } else { provinceFrontiersCacheHit.Add(hc, region); provinceFrontiersPoints.Add(p0); provinceFrontiersPoints.Add(p1); } } // Close the polygon provinceFrontiersPoints.Add(region.spherePoints [max]); provinceFrontiersPoints.Add(region.spherePoints [0]); } } } int meshGroups = (provinceFrontiersPoints.Count / 65000) + 1; int meshIndex = -1; provinceFrontiersIndices = new int[meshGroups][]; provinceFrontiers = new Vector3[meshGroups][]; int pcount = provinceFrontiersPoints.Count; for (int k = 0; k < pcount; k += 65000) { int max = Mathf.Min(provinceFrontiersPoints.Count - k, 65000); provinceFrontiers [++meshIndex] = new Vector3[max]; provinceFrontiersIndices [meshIndex] = new int[max]; int jend = k + max; for (int j = k; j < jend; j++) { provinceFrontiers [meshIndex] [j - k] = provinceFrontiersPoints [j]; provinceFrontiersIndices [meshIndex] [j - k] = j - k; } } // Create province borders container // Create province layer if needed if (provincesObj != null) { DestroyImmediate(provincesObj); } provincesObj = new GameObject(PROVINCES_BORDERS_LAYER); provincesObj.hideFlags = HideFlags.DontSave; provincesObj.transform.SetParent(transform, false); provincesObj.transform.localPosition = Misc.Vector3zero; provincesObj.transform.localRotation = Quaternion.Euler(Misc.Vector3zero); provincesObj.layer = gameObject.layer; // Choose a borders mat if (_provincesColor.a < 1f) { provincesMatCurrent = provincesMatAlpha; } else { provincesMatCurrent = provincesMatOpaque; } provincesMatCurrent.color = _provincesColor; for (int k = 0; k < provinceFrontiers.Length; k++) { GameObject flayer = new GameObject("flayer"); flayer.hideFlags = HideFlags.DontSave | HideFlags.HideInHierarchy; flayer.transform.SetParent(provincesObj.transform, false); flayer.transform.localPosition = Misc.Vector3zero; flayer.transform.localRotation = Quaternion.Euler(Misc.Vector3zero); Mesh mesh = new Mesh(); mesh.vertices = provinceFrontiers [k]; mesh.SetIndices(provinceFrontiersIndices [k], MeshTopology.Lines, 0); mesh.RecalculateBounds(); mesh.hideFlags = HideFlags.DontSave; MeshFilter mf = flayer.AddComponent <MeshFilter> (); mf.sharedMesh = mesh; MeshRenderer mr = flayer.AddComponent <MeshRenderer> (); mr.receiveShadows = false; mr.reflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.Off; mr.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; mr.sharedMaterial = provincesMatCurrent; } // if (_showOutline) { // Country country = countries[countryIndex]; // Region region = country.regions[country.mainRegionIndex]; // provinceCountryOutlineRef = DrawCountryRegionOutline(region, provinceObj); // } return(true); }
void ProvinceSubstractProvinceEnclaves(int provinceIndex, Region region, Poly2Tri.Polygon poly) { List <Region> negativeRegions = new List <Region>(); for (int oc = 0; oc < _countries.Length; oc++) { Country ocCountry = _countries[oc]; if (ocCountry.hidden || ocCountry.provinces == null) { continue; } if (!ocCountry.regionsRect2D.Overlaps(region.latlonRect2D)) { continue; } for (int op = 0; op < ocCountry.provinces.Length; op++) { Province opProvince = ocCountry.provinces[op]; if (opProvince == provinces[provinceIndex]) { continue; } if (opProvince.regions == null) { continue; } if (opProvince.regionsRect2D.Overlaps(region.latlonRect2D, true)) { Region oProvRegion = opProvince.regions[opProvince.mainRegionIndex]; if (region.Contains(oProvRegion)) // just check main region of province for speed purposes { negativeRegions.Add(oProvRegion); } } } } // Collapse negative regions in big holes for (int nr = 0; nr < negativeRegions.Count - 1; nr++) { for (int nr2 = nr + 1; nr2 < negativeRegions.Count; nr2++) { if (negativeRegions[nr].Intersects(negativeRegions[nr2])) { PolygonClipper pc = new PolygonClipper(negativeRegions[nr], negativeRegions[nr2]); if (pc.Compute(PolygonOp.UNION, null)) { negativeRegions.RemoveAt(nr2); nr = -1; break; } } } } // Substract holes for (int r = 0; r < negativeRegions.Count; r++) { Poly2Tri.Polygon polyHole = new Poly2Tri.Polygon(negativeRegions[r].latlon); poly.AddHole(polyHole); } }
/// <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(int provinceIndex) { if (provinceIndex < 0 || provinceIndex >= provinces.Length) { return; } lastProvinceLookupCount = -1; float maxVol = 0; Province province = provinces [provinceIndex]; if (province.regions == null) { ReadProvincePackedString(province); } int regionCount = province.regions.Count; Vector2 minProvince = Misc.Vector2one * 1000; Vector2 maxProvince = -minProvince; 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.latlon.Length; Vector2 min = Misc.Vector2one * 1000; Vector2 max = -min; for (int c = 0; c < coorCount; c++) { float x = provinceRegion.latlon [c].x; float y = provinceRegion.latlon [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; } } Vector3 normRegionCenter = (min + max) * 0.5f; provinceRegion.latlonCenter = normRegionCenter; 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.latlonRect2D = new Rect(min.x, min.y, Math.Abs(max.x - min.x), Mathf.Abs(max.y - min.y)); provinceRegion.rect2DArea = provinceRegion.latlonRect2D.width * provinceRegion.latlonRect2D.height; float vol = (max - min).sqrMagnitude; if (vol > maxVol) { maxVol = vol; province.mainRegionIndex = r; province.latlonCenter = provinceRegion.latlonCenter; } } province.regionsRect2D = new Rect(minProvince.x, minProvince.y, Math.Abs(maxProvince.x - minProvince.x), Mathf.Abs(maxProvince.y - minProvince.y)); }
public void ReadProvincePackedString(Province province) { string[] regions = province.packedRegions.Split(new char[] { '*' }, StringSplitOptions.RemoveEmptyEntries); int regionCount = regions.Length; province.regions = new List <Region> (regionCount); float maxVol = float.MinValue; Vector2 minProvince = Misc.Vector2one * 1000; Vector2 maxProvince = -minProvince; for (int r = 0; r < regionCount; r++) { string[] coordinates = regions [r].Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); int coorCount = coordinates.Length; Vector2 min = Misc.Vector2one * 1000; Vector2 max = -min; Region provinceRegion = new Region(province, province.regions.Count); Vector2[] latlon = new Vector2[coorCount]; for (int c = 0; c < coorCount; c++) { float lat, lon; GetPointFromPackedString(coordinates[c], out lat, out lon); // if (province.name.Equals("Badghis") && r==0 && c==0) { // int x = (int)(lat * WorldMapGlobe.MAP_PRECISION); // Debug.Log (coordinates[c] + " " + x.ToString()); // } if (lat < min.x) { min.x = lat; } if (lat > max.x) { max.x = lat; } if (lon < min.y) { min.y = lon; } if (lon > max.y) { max.y = lon; } latlon[c] = new Vector2(lat, lon); } provinceRegion.latlon = latlon; Vector3 normRegionCenter = (min + max) * 0.5f; provinceRegion.latlonCenter = normRegionCenter; 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.latlonRect2D = new Rect(min.x, min.y, max.x - min.x, max.y - min.y); provinceRegion.rect2DArea = provinceRegion.latlonRect2D.width * provinceRegion.latlonRect2D.height; float vol = (max - min).sqrMagnitude; if (vol > maxVol) { maxVol = vol; province.mainRegionIndex = r; province.latlonCenter = provinceRegion.latlonCenter; } } province.regionsRect2D = new Rect(minProvince.x, minProvince.y, maxProvince.x - minProvince.x, 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; float centerX = currentRegion.latlonCenter.y; // (currentRegion.minMaxLon.x + currentRegion.minMaxLon.y)*0.5; for (int k = 0; k < currentRegion.latlon.Length; k++) { Vector2 p = currentRegion.latlon[k]; if (p.y > centerX) // compare longitudes { 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.y > centerX) { 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(); }
public bool GetVertexNearSpherePos(Vector3 spherePos, out Vector3 nearPoint) { // Iterate country regions int numCountries = _map.countries.Length; Vector3 np = spherePos; float minDist = float.MaxValue; // Countries for (int c = 0; c < numCountries; c++) { Country country = _map.countries[c]; int regCount = country.regions.Count; for (int cr = 0; cr < regCount; cr++) { Region region = country.regions[cr]; int pointCount = region.spherePoints.Length; for (int p = 0; p < pointCount; p++) { float dist = (spherePos - region.spherePoints[p]).sqrMagnitude; if (dist < minDist) { // Check that it's not already in the new shape if (!newShape.Contains(region.spherePoints[p])) { minDist = dist; np = region.spherePoints[p]; } } } } } // Provinces if (_map.editor.editingMode == EDITING_MODE.PROVINCES) { int numProvinces = _map.provinces.Length; for (int p = 0; p < numProvinces; p++) { Province province = _map.provinces[p]; if (province.regions == null) { continue; } int regCount = province.regions.Count; for (int pr = 0; pr < regCount; pr++) { Region region = province.regions[pr]; int pointCount = region.spherePoints.Length; for (int po = 0; po < pointCount; po++) { float dist = (spherePos - region.spherePoints[po]).sqrMagnitude; if (dist < minDist) { // Check that it's not already in the new shape if (!newShape.Contains(region.spherePoints[p])) { minDist = dist; np = region.spherePoints[p]; } } } } } } nearPoint = np; return(nearPoint != spherePos); }
/// <summary> /// Calculates correct province for cities /// </summary> public void FixOrphanCities() { if (_map.provinces == null) { return; } int countryAssigned = 0; int provinceAssigned = 0; int cityCount = _map.cities.Count; for (int c = 0; c < cityCount; c++) { City city = _map.cities [c]; if (city.countryIndex == -1) { for (int k = 0; k < _map.countries.Length; k++) { Country co = _map.countries [k]; if (co.regions == null) { continue; } int regCount = co.regions.Count; for (int kr = 0; kr < regCount; kr++) { if (co.regions [kr].Contains(city.latlon)) { city.countryIndex = k; countryAssigned++; k = 100000; break; } } } } if (city.countryIndex == -1) { float minDist = float.MaxValue; for (int k = 0; k < _map.countries.Length; k++) { Country co = _map.countries [k]; if (co.regions == null) { continue; } int regCount = co.regions.Count; for (int kr = 0; kr < regCount; kr++) { float dist = (co.regions [kr].latlonCenter - city.latlon).sqrMagnitude; if (dist < minDist) { minDist = dist; city.countryIndex = k; countryAssigned++; } } } } if (city.province.Length == 0) { Country country = _map.countries [city.countryIndex]; if (country.provinces == null) { continue; } for (int p = 0; p < country.provinces.Length; p++) { Province province = country.provinces [p]; if (province.regions == null) { _map.ReadProvincePackedString(province); } if (province.regions == null) { continue; } int regCount = province.regions.Count; for (int pr = 0; pr < regCount; pr++) { Region reg = province.regions [pr]; if (reg.Contains(city.latlon)) { city.province = province.name; p = 100000; break; } } } } } for (int c = 0; c < cityCount; c++) { City city = _map.cities [c]; if (city.province.Length == 0) { float minDist = float.MaxValue; int pg = -1; for (int p = 0; p < _map.provinces.Length; p++) { Province province = _map.provinces [p]; if (province.regions == null) { _map.ReadProvincePackedString(province); } if (province.regions == null) { continue; } int regCount = province.regions.Count; for (int pr = 0; pr < regCount; pr++) { Region pregion = province.regions [pr]; for (int prp = 0; prp < pregion.latlon.Length; prp++) { float dist = (city.latlon - pregion.latlon [prp]).sqrMagnitude; if (dist < minDist) { minDist = dist; pg = p; } } } } if (pg >= 0) { city.province = _map.provinces [pg].name; provinceAssigned++; } } else // check for differences in capitals { #if !UNITY_WSA if (!city.name.Equals(city.province) && String.Compare(city.name, city.province, System.Globalization.CultureInfo.CurrentCulture, System.Globalization.CompareOptions.IgnoreNonSpace) == 0) { city.name = city.province; cityChanges = true; } #endif } } Debug.Log(countryAssigned + " cities were assigned a new country."); Debug.Log(provinceAssigned + " cities were assigned a new province."); if (countryAssigned > 0 || provinceAssigned > 0) { cityChanges = true; } }