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()); }
void Start() { WMSK map = WMSK.instance; // *********************************************************************** // Adding custom attributes to a country (same for provinces, cities, ...) // *********************************************************************** Country canada = map.GetCountry("Canada"); canada.attrib ["Language"] = "French"; // Add language as a custom attribute canada.attrib ["ConstitutionDate"] = new DateTime(1867, 7, 1); // Add the date of British North America Act, 1867 canada.attrib ["AreaKm2"] = 9984670; // Add the land area in km2 // List example List <int> values = new List <int> (10); for (int j = 0; j < 10; j++) { values.Add(j); } canada.attrib ["List"] = JSONObject.FromArray(values); // ****************************************************** // Obtain attributes and print them out over the console. // ****************************************************** Debug.Log("Language = " + canada.attrib ["Language"]); Debug.Log("Constitution Date = " + canada.attrib ["ConstitutionDate"].d); // Note the use of .d to force cast the internal number representation to DateTime Debug.Log("Area in km2 = " + canada.attrib ["AreaKm2"]); Debug.Log("List = " + canada.attrib ["List"]); // ********************************************************* // Now, look up by attribute example using lambda expression // ********************************************************* List <Country> countries = map.GetCountries( (attrib) => "French".Equals(attrib ["Language"]) && attrib ["AreaKm2"] > 1000000 ); Debug.Log("Matches found = " + countries.Count); foreach (Country c in countries) { Debug.Log("Match: " + c.name); } // ***************************************************************** // Export/import individual country attributes in JSON format sample // ***************************************************************** string json = canada.attrib.Print(); // Get raw jSON Debug.Log(json); canada.attrib = new JSONObject(json); // Import from raw jSON int keyCount = canada.attrib.keys.Count; Debug.Log("Imported JSON has " + keyCount + " keys."); for (int k = 0; k < keyCount; k++) { Debug.Log("Key " + (k + 1) + ": " + canada.attrib.keys [k] + " = " + canada.attrib [k]); } // ***************************************************************** // Finally, export all countries attributes in one single JSON file // ***************************************************************** string jsonCountries = map.GetCountriesAttributes(true); // get the complete json for all countries with attributes Debug.Log(jsonCountries); canada.attrib = null; map.SetCountriesAttributes(jsonCountries); // parse the jsonCountries string (expects a jSON compliant string) and loads the attributes Debug.Log("Canada's attributes restored: Lang = " + canada.attrib ["Language"] + ", Date = " + canada.attrib ["ConstitutionDate"].d + ", Area = " + canada.attrib ["AreaKm2"]); }