/// <summary> /// Merges all provinces in each continent so their number doesn't exceed a given number /// </summary> /// <param name="max">Maximum number of countries per continent.</param> /// <param name="z">The current country index.</param> public void CountriesEqualize(int max, out bool repeat) { if (max < 1) { max = 1; } if (groupedCountries == null) { groupedCountries = new Dictionary <string, List <Country> >(); } else { groupedCountries.Clear(); } repeat = false; // Select continents for (int k = 0; k < map.countries.Length; k++) { Country country = map.countries[k]; if (country.continent.Length > 0) { List <Country> countries; if (groupedCountries.TryGetValue(country.continent, out countries)) { countries.Add(country); } else { countries = new List <Country>(); countries.Add(country); groupedCountries[country.continent] = countries; } } } List <string> continents = new List <string>(groupedCountries.Keys); for (int k = 0; k < continents.Count; k++) { string continent = continents[k]; int count = groupedCountries[continent].Count; if (count > max) { // Take first country and merge with first neighbour for (int j = 0; j < count; j++) { Country country = groupedCountries[continent][j]; int countryIndex = map.GetCountryIndex(country); int neighbourCount = country.mainRegion.neighbours.Count; if (neighbourCount > 0) { int randomNeighbour = Random.Range(0, neighbourCount - 1); Region otherRegion = country.mainRegion.neighbours[randomNeighbour]; Country otherCountry = (Country)otherRegion.entity; if (otherCountry.continent.Equals(continent)) { if (map.CountryTransferCountryRegion(countryIndex, otherRegion, false)) { countryChanges = true; provinceChanges = true; cityChanges = true; mountPointChanges = true; repeat = true; return; } } } } } } }
/// <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 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); } // Create the new country string uniqueName = GetCountryUniqueName(country.name); Country newCountry = new Country(uniqueName, country.continent, map.GetUniqueId(new List <IExtendableAttribute>(map.countries))); 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; lastCountryCount = -1; GUICountryName = ""; ReloadCountryNames(); countryChanges = true; // Update cities List <City> cities = _map.GetCities(region); if (cities.Count > 0) { for (int k = 0; k < cities.Count; k++) { if (cities [k].countryIndex != countryIndex) { cities [k].countryIndex = countryIndex; cityChanges = true; } } } // Update mount points List <MountPoint> mp = _map.GetMountPoints(region); if (mp.Count > 0) { for (int k = 0; k < mp.Count; k++) { if (mp [k].countryIndex != countryIndex) { mp [k].countryIndex = countryIndex; mountPointChanges = true; } } } // 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; } if (_map.CountryTransferProvinceRegion(countryIndex, prov.mainRegion, false)) { provinceChanges = true; } } } map.Redraw(); CountryRegionSelect(); }
void DrawUnityStandardTextLabels() { ResetDefaultCountryLabelOffset(); if (meshRects == null) { meshRects = new List <MeshRect>(_countries.Length); } else { meshRects.Clear(); } float mw = mapWidth; float mh = mapHeight; for (int countryIndex = 0; countryIndex < _countries.Length; countryIndex++) { Country country = _countries[countryIndex]; if (country.hidden || !country.labelVisible || country.mainRegionIndex < 0 || country.mainRegionIndex >= country.regions.Count) { continue; } Vector2 center = new Vector2(country.center.x * mapWidth, country.center.y * mh) + country.labelOffset; center += GetDefaultCountryLabelOffset(countryIndex); Region region = country.regions[country.mainRegionIndex]; // Adjusts country name length string countryName = country.customLabel != null ? country.customLabel : country.name.ToUpper(); bool introducedCarriageReturn = false; if (countryName.Length > 15) { countryName = BreakOneLineString(countryName); introducedCarriageReturn = true; } // add caption GameObject textObj; TextMesh tm; Renderer tmRenderer; TextMesh tmShadow = null; if (country.labelTextMeshGO == null) { Color labelColor = country.labelColorOverride ? country.labelColor : _countryLabelsColor; Font customFont = country.labelFontOverride ?? labelsFont; if ((object)customFont == null) { continue; } Material customLabelShadowMaterial = country.labelFontShadowMaterial ?? labelsShadowMaterial; tm = Drawing.CreateText(countryName, null, center, customFont, labelColor, _showLabelsShadow, customLabelShadowMaterial, _countryLabelsShadowColor, out tmShadow); textObj = tm.gameObject; country.labelTextMesh = tm; country.labelTextMeshGO = tm.gameObject; tmRenderer = textObj.GetComponent <Renderer>(); Bounds bounds = tmRenderer.bounds; country.labelMeshWidth = bounds.size.x; country.labelMeshHeight = bounds.size.y; country.labelMeshCenter = center; textObj.transform.SetParent(textRoot.transform, false); textObj.transform.localPosition = center; textObj.layer = textRoot.gameObject.layer; if (_showLabelsShadow) { country.labelShadowTextMesh = tmShadow; //textObj.transform.Find ("shadow").GetComponent<TextMesh> (); country.labelShadowTextMesh.gameObject.layer = textObj.layer; } } else { tm = country.labelTextMesh; textObj = tm.gameObject; textObj.transform.localPosition = center; tmRenderer = textObj.GetComponent <Renderer>(); } float meshWidth = country.labelMeshWidth; float meshHeight = country.labelMeshHeight; // adjusts caption Rect rect = new Rect(region.rect2D.xMin * mw, region.rect2D.yMin * mh, region.rect2D.width * mw, region.rect2D.height * mh); float absoluteHeight; if (country.labelRotation > 0) { textObj.transform.localRotation = Quaternion.Euler(0, 0, country.labelRotation); absoluteHeight = Mathf.Min(rect.height * _countryLabelsSize, rect.width); } else if (rect.height > rect.width * 1.45f) { float angle; if (rect.height > rect.width * 1.5f) { angle = 90; } else { angle = Mathf.Atan2(rect.height, rect.width) * Mathf.Rad2Deg; } textObj.transform.localRotation = Quaternion.Euler(0, 0, angle); absoluteHeight = Mathf.Min(rect.width * _countryLabelsSize, rect.height); } else { absoluteHeight = Mathf.Min(rect.height * _countryLabelsSize, rect.width); } // adjusts scale to fit width in rect float adjustedMeshHeight = introducedCarriageReturn ? meshHeight * 0.5f : meshHeight; float scale = absoluteHeight / adjustedMeshHeight; if (country.labelFontSizeOverride) { scale = country.labelFontSize; } else { float desiredWidth = meshWidth * scale; if (desiredWidth > rect.width) { scale = rect.width / meshWidth; } if (adjustedMeshHeight * scale < _countryLabelsAbsoluteMinimumSize) { scale = _countryLabelsAbsoluteMinimumSize / adjustedMeshHeight; } } // stretchs out the caption float displayedMeshWidth = meshWidth * scale; float displayedMeshHeight = meshHeight * scale; string wideName; int times = Mathf.FloorToInt(rect.width * 0.45f / (meshWidth * scale)); if (times > 10) { times = 10; } if (times > 0) { StringBuilder sb = new StringBuilder(); string spaces = new string(' ', times * 2); for (int c = 0; c < countryName.Length; c++) { sb.Append(countryName[c]); if (c < countryName.Length - 1) { sb.Append(spaces); } } wideName = sb.ToString(); } else { wideName = countryName; } if (tm.text.Length != wideName.Length) { tm.text = wideName; displayedMeshWidth = tmRenderer.bounds.size.x * scale; displayedMeshHeight = tmRenderer.bounds.size.y * scale; if (_showLabelsShadow) { tmShadow.text = wideName; } } // apply scale textObj.transform.localScale = new Vector3(scale, scale, 1); // Save mesh rect for overlapping checking if (country.labelOffset == Misc.Vector2zero) { float xMin = center.x - displayedMeshWidth * 0.5f; float yMin = center.y - displayedMeshHeight * 0.5f; float xMax = xMin + displayedMeshWidth; float yMax = yMin + displayedMeshHeight; MeshRect mr = new MeshRect(countryIndex, new Vector4(xMin, yMin, xMax, yMax)); meshRects.Add(mr); } } // Simple-fast overlapping checking int cont = 0; bool needsResort = true; int meshRectsCount = meshRects.Count; while (needsResort && ++cont < 10) { meshRects.Sort(overlapComparer); needsResort = false; for (int c = 1; c < meshRectsCount; c++) { Vector4 r1 = meshRects[c].rect; for (int prevc = c - 1; prevc >= 0; prevc--) { Vector4 r2 = meshRects[prevc].rect; bool overlaps = !(r2.x > r1.z || r2.z <r1.x || r2.y> r1.w || r2.w < r1.y); if (overlaps) { needsResort = true; int thisCountryIndex = meshRects[c].entityIndex; Country country = _countries[thisCountryIndex]; GameObject thisLabel = country.labelTextMeshGO; // displaces this label float offsety = r1.w - r2.y; offsety = Mathf.Min(country.regions[country.mainRegionIndex].rect2D.height * mh * 0.35f, offsety); thisLabel.transform.localPosition = new Vector3(country.labelMeshCenter.x, country.labelMeshCenter.y - offsety, thisLabel.transform.localPosition.z); float width = r1.z - r1.x; float height = r1.w - r1.y; float xMin = thisLabel.transform.localPosition.x - width * 0.5f; float yMin = thisLabel.transform.localPosition.y - height * 0.5f; float xMax = xMin + width; float yMax = yMin + height; r1 = new Vector4(xMin, yMin, xMax, yMax); meshRects[c].rect = r1; } } } } }
/// <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 <Vector2>(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++; } } } } } } } // 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, map.GetUniqueId(new List <IExtendableAttribute>(map.countries))); } else { newEntity = new Province(refEntity.name, ((Province)refEntity).countryIndex, map.GetUniqueId(new List <IExtendableAttribute>(map.provinces))); } for (int r = 0; r < refEntity.regions.Count; r++) { Region region = refEntity.regions[r]; int numPoints = region.points.Length; totalPoints += numPoints; List <Vector2> points = new List <Vector2>(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] * WMSK.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] * WMSK.MAP_PRECISION); } else { savings++; } } points.Add(region.points[numPoints - 1] * WMSK.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() + ","); sb.Append(point.y.ToString()); } } } return(sb.ToString()); }
void DrawTextMeshProLabels() { if (labelsFontTMPro == null) { ReloadFont(); if (labelsFontTMPro == null) { return; } } float mw = mapWidth; float mh = mapHeight; if (labelsCurve == null || labelsCurve.Length == 0) { ComputeLabelsCurve(); } Vector2 center = Misc.Vector2zero; for (int countryIndex = 0; countryIndex < _countries.Length; countryIndex++) { Country country = _countries[countryIndex]; if (country.hidden || !country.labelVisible || country.mainRegionIndex < 0 || country.mainRegionIndex >= country.regions.Count) { continue; } Region region = country.regions[country.mainRegionIndex]; if (!ComputeCurvedLabelData(region)) { continue; } if (country.labelOffset.x != 0 || country.labelOffset.y != 0) { center = country.center + country.labelOffset; } else { FastVector.Average(ref region.curvedLabelInfo.axisStart, ref region.curvedLabelInfo.axisEnd, ref center); // (curvedLabel.axisStart + curvedLabel.axisEnd) * 0.5f; } center.x *= mw; center.y *= mh; // Adjusts country name length string countryName = country.customLabel != null ? country.customLabel : country.name.ToUpper(); if (countryName.Length == 0) { continue; } if (countryName.Length > 15) { countryName = BreakOneLineString(countryName); } // add caption GameObject textObj; TextMeshPro tm; Color labelColor = country.labelColorOverride ? country.labelColor : _countryLabelsColor; if (country.labelTextMeshGO == null) { // create base text textObj = new GameObject(countryName); textObj.hideFlags = HideFlags.DontSave; textObj.hideFlags |= HideFlags.HideInHierarchy; tm = textObj.AddComponent <TextMeshPro>(); tm.alignment = TextAlignmentOptions.Center; tm.enableWordWrapping = false; country.labelTextMeshPro = tm; country.labelTextMeshGO = tm.gameObject; textObj.transform.SetParent(textRoot.transform, false); textObj.layer = textRoot.gameObject.layer; } else { tm = (TextMeshPro)country.labelTextMeshPro; textObj = tm.gameObject; textObj.transform.localPosition = center; } tm.font = (TMP_FontAsset)labelsFontTMPro; tm.color = labelColor; Material fontMat; if (_countryLabelsEnableAutomaticFade) { // By using fontMaterial we're forcing to instantiate the material which will enable individual colors and alpha fontMat = tm.fontMaterial; fontMat.hideFlags = HideFlags.DontSave; } else { fontMat = tm.fontSharedMaterial; } if (_countryLabelsOutlineWidth > 0) { fontMat.SetColor("_OutlineColor", _countryLabelsOutlineColor); fontMat.SetFloat("_OutlineWidth", _countryLabelsOutlineWidth); fontMat.EnableKeyword("OUTLINE_ON"); } else { fontMat.DisableKeyword("OUTLINE_ON"); } tm.text = countryName; textObj.transform.localPosition = center; country.labelMeshWidth = tm.preferredWidth; country.labelMeshHeight = tm.preferredHeight; country.labelMeshCenter = center; float meshWidth = country.labelMeshWidth; float meshHeight = country.labelMeshHeight; // adjusts scale to fit in region Vector2 axis = region.curvedLabelInfo.axisEnd - region.curvedLabelInfo.axisStart; float scale; if (country.labelFontSizeOverride) { scale = country.labelFontSize; } else { // axisWidth represents the length of the label along the longest axis float axisWidth = new Vector2(axis.x * mw, axis.y * mh).magnitude; // axisAveragedWidth represents the average length of the region (used as a maximum height for the label) float axisAveragedThickness = new Vector2(region.curvedLabelInfo.axisAveragedThickness.x * mw, region.curvedLabelInfo.axisAveragedThickness.y * mh).magnitude; float scaleheight = axisAveragedThickness / meshHeight; float scaleWidth = axisWidth / meshWidth; scale = Mathf.Min(scaleWidth, scaleheight); if (meshHeight * scale < _countryLabelsAbsoluteMinimumSize) { scale = _countryLabelsAbsoluteMinimumSize / meshHeight; } scale *= _countryLabelsSize * 2f; } // apply scale textObj.transform.localScale = new Vector3(scale, scale, 1); // Apply axis rotation or user defined rotation if (country.labelRotation > 0) { textObj.transform.localRotation = Quaternion.Euler(0, 0, country.labelRotation); } else { textObj.transform.localRotation = Quaternion.Euler(0, 0, region.curvedLabelInfo.axisAngle); } if (_countryLabelsCurvature > 0) { // Compute fitting curve tm.havePropertiesChanged = true; // Need to force the TextMeshPro Object to be updated. tm.ForceMeshUpdate(); // Generate the mesh and populate the textInfo with data we can use and manipulate. TMP_TextInfo textInfo = tm.textInfo; int characterCount = textInfo.characterCount; float boundsMinX = tm.bounds.min.x; float boundsMaxX = tm.bounds.max.x; // map bounds to axis length float axisLengthWS = new Vector2(axis.x * mw / scale, axis.y * mh / scale).magnitude; float boundsLength = boundsMaxX - boundsMinX; float boundsMid = (boundsMaxX + boundsMinX) * 0.5f; boundsMinX = boundsMid - (boundsMid - boundsMinX) * axisLengthWS / boundsLength; boundsMaxX = boundsMid + (boundsMaxX - boundsMid) * axisLengthWS / boundsLength; float curveMultiplier = new Vector2(region.curvedLabelInfo.axisMidDisplacement.x * mw / scale, region.curvedLabelInfo.axisMidDisplacement.y * mh / scale).magnitude *_countryLabelsCurvature; // check if axisAveragedThickness is above or below axis Vector2 a = axis * 0.5f + region.curvedLabelInfo.axisMidDisplacement; float dot = a.x * -axis.y + a.y * axis.x; if (dot < 0) { curveMultiplier *= -1f; } float boundsWidth = boundsMaxX - boundsMinX; // Get the index of the mesh used by this character. int materialIndex = textInfo.characterInfo[0].materialReferenceIndex; Vector3[] vertices = textInfo.meshInfo[materialIndex].vertices; for (int i = 0; i < characterCount; i++) { if (!textInfo.characterInfo[i].isVisible) { continue; } int vertexIndex = textInfo.characterInfo[i].vertexIndex; // Compute the baseline mid point for each character Vector2 offsetToMidBaseline = new Vector2((vertices[vertexIndex + 0].x + vertices[vertexIndex + 2].x) / 2, textInfo.characterInfo[i].baseLine); if (float.IsNaN(offsetToMidBaseline.x) || float.IsNaN(offsetToMidBaseline.y)) { continue; //offsetToMidBaseline.x = offsetToMidBaseline.y = 0; } // Apply offset to adjust our pivot point. vertices[vertexIndex + 0].x -= offsetToMidBaseline.x; vertices[vertexIndex + 0].y -= offsetToMidBaseline.y; vertices[vertexIndex + 1].x -= offsetToMidBaseline.x; vertices[vertexIndex + 1].y -= offsetToMidBaseline.y; vertices[vertexIndex + 2].x -= offsetToMidBaseline.x; vertices[vertexIndex + 2].y -= offsetToMidBaseline.y; vertices[vertexIndex + 3].x -= offsetToMidBaseline.x; vertices[vertexIndex + 3].y -= offsetToMidBaseline.y; // Compute the angle of rotation for each character based on the animation curve float x0 = (offsetToMidBaseline.x - boundsMinX) / boundsWidth; // Character's position relative to the bounds of the mesh. float x1 = x0 + 0.01f; const float minT = 0.0f; const float maxT = 0.9999f; if (x0 < minT) { x0 = minT; } else if (x0 > maxT) { x0 = maxT; } int ix0 = (int)(x0 * labelsCurve.Length); float y0 = labelsCurve[ix0] * curveMultiplier; if (x1 < minT) { x1 = minT; } else if (x1 > maxT) { x1 = maxT; } int ix1 = (int)(x1 * labelsCurve.Length); float y1 = labelsCurve[ix1] * curveMultiplier; Vector2 tangent = new Vector2(x1 * boundsWidth + boundsMinX - offsetToMidBaseline.x, y1 - y0); dot = Mathf.Acos(tangent.normalized.x) * Mathf.Rad2Deg; float angle = tangent.y > 0 ? dot : 360 - dot; Matrix4x4 matrix = Matrix4x4.TRS(new Vector3(offsetToMidBaseline.x, y0 + offsetToMidBaseline.y, 0), Quaternion.Euler(0, 0, angle), Misc.Vector3one); vertices[vertexIndex + 0] = matrix.MultiplyPoint3x4(vertices[vertexIndex + 0]); vertices[vertexIndex + 1] = matrix.MultiplyPoint3x4(vertices[vertexIndex + 1]); vertices[vertexIndex + 2] = matrix.MultiplyPoint3x4(vertices[vertexIndex + 2]); vertices[vertexIndex + 3] = matrix.MultiplyPoint3x4(vertices[vertexIndex + 3]); } // Upload the mesh with the revised information tm.UpdateVertexData(); } country.labelMeshWidth = tm.bounds.size.x; country.labelMeshHeight = tm.bounds.size.y; // GameObject sphere3 = GameObject.CreatePrimitive (PrimitiveType.Sphere); // sphere3.name = "Axis Middle"; // sphere3.transform.SetParent (o.transform, true); // sphere3.transform.localPosition = (curvedLabel.axisStart + curvedLabel.axisEnd) * 0.5f; // sphere3.transform.localScale *= 0.3f; // // GameObject sphere4 = GameObject.CreatePrimitive (PrimitiveType.Sphere); // sphere4.name = "avgAxisPoint"; // sphere4.transform.SetParent (o.transform, true); // sphere4.transform.localPosition = (curvedLabel.axisStart + curvedLabel.axisEnd) * 0.5f + curvedLabel.axisMidDisplacement; // sphere4.GetComponent<Renderer> ().material.color = Color.red; // sphere4.transform.localScale *= 0.2f; } StartCoroutine(RepositionTexts()); }