You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2277 lines
102 KiB

  1. using UnityEngine;
  2. using System.Collections;
  3. namespace TMPro
  4. {
  5. public enum CaretPosition { None, Left, Right }
  6. /// <summary>
  7. /// Structure which contains the character index and position of caret relative to the character.
  8. /// </summary>
  9. public struct CaretInfo
  10. {
  11. public int index;
  12. public CaretPosition position;
  13. public CaretInfo(int index, CaretPosition position)
  14. {
  15. this.index = index;
  16. this.position = position;
  17. }
  18. }
  19. public static class TMP_TextUtilities
  20. {
  21. private static Vector3[] m_rectWorldCorners = new Vector3[4];
  22. // TEXT INPUT COMPONENT RELATED FUNCTIONS
  23. /// <summary>
  24. ///
  25. /// </summary>
  26. /// <param name="textComponent">A reference to the text object.</param>
  27. /// <param name="position">Position to check for intersection.</param>
  28. /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
  29. /// <returns></returns>
  30. //public static CaretInfo GetCursorInsertionIndex(TMP_Text textComponent, Vector3 position, Camera camera)
  31. //{
  32. // int index = TMP_TextUtilities.FindNearestCharacter(textComponent, position, camera, false);
  33. // RectTransform rectTransform = textComponent.rectTransform;
  34. // // Convert position into Worldspace coordinates
  35. // ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
  36. // TMP_CharacterInfo cInfo = textComponent.textInfo.characterInfo[index];
  37. // // Get Bottom Left and Top Right position of the current character
  38. // Vector3 bl = rectTransform.TransformPoint(cInfo.bottomLeft);
  39. // //Vector3 tl = rectTransform.TransformPoint(new Vector3(cInfo.bottomLeft.x, cInfo.topRight.y, 0));
  40. // Vector3 tr = rectTransform.TransformPoint(cInfo.topRight);
  41. // //Vector3 br = rectTransform.TransformPoint(new Vector3(cInfo.topRight.x, cInfo.bottomLeft.y, 0));
  42. // float insertPosition = (position.x - bl.x) / (tr.x - bl.x);
  43. // if (insertPosition < 0.5f)
  44. // return new CaretInfo(index, CaretPosition.Left);
  45. // else
  46. // return new CaretInfo(index, CaretPosition.Right);
  47. //}
  48. /// <summary>
  49. /// Function returning the index of the character whose origin is closest to the cursor.
  50. /// </summary>
  51. /// <param name="textComponent">A reference to the text object.</param>
  52. /// <param name="position">Position to check for intersection.</param>
  53. /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
  54. /// <returns></returns>
  55. public static int GetCursorIndexFromPosition(TMP_Text textComponent, Vector3 position, Camera camera)
  56. {
  57. int index = TMP_TextUtilities.FindNearestCharacter(textComponent, position, camera, false);
  58. RectTransform rectTransform = textComponent.rectTransform;
  59. // Convert position into Worldspace coordinates
  60. ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
  61. TMP_CharacterInfo cInfo = textComponent.textInfo.characterInfo[index];
  62. // Get Bottom Left and Top Right position of the current character
  63. Vector3 bl = rectTransform.TransformPoint(cInfo.bottomLeft);
  64. Vector3 tr = rectTransform.TransformPoint(cInfo.topRight);
  65. float insertPosition = (position.x - bl.x) / (tr.x - bl.x);
  66. if (insertPosition < 0.5f)
  67. return index;
  68. else
  69. return index + 1;
  70. }
  71. /// <summary>
  72. /// Function returning the index of the character whose origin is closest to the cursor.
  73. /// </summary>
  74. /// <param name="textComponent">A reference to the text object.</param>
  75. /// <param name="position">Position to check for intersection.</param>
  76. /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
  77. /// <param name="cursor">The position of the cursor insertion position relative to the position.</param>
  78. /// <returns></returns>
  79. //public static int GetCursorIndexFromPosition(TMP_Text textComponent, Vector3 position, Camera camera, out CaretPosition cursor)
  80. //{
  81. // int index = TMP_TextUtilities.FindNearestCharacter(textComponent, position, camera, false);
  82. // RectTransform rectTransform = textComponent.rectTransform;
  83. // // Convert position into Worldspace coordinates
  84. // ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
  85. // TMP_CharacterInfo cInfo = textComponent.textInfo.characterInfo[index];
  86. // // Get Bottom Left and Top Right position of the current character
  87. // Vector3 bl = rectTransform.TransformPoint(cInfo.bottomLeft);
  88. // Vector3 tr = rectTransform.TransformPoint(cInfo.topRight);
  89. // float insertPosition = (position.x - bl.x) / (tr.x - bl.x);
  90. // if (insertPosition < 0.5f)
  91. // {
  92. // cursor = CaretPosition.Left;
  93. // return index;
  94. // }
  95. // else
  96. // {
  97. // cursor = CaretPosition.Right;
  98. // return index;
  99. // }
  100. //}
  101. /// <summary>
  102. /// Function returning the index of the character whose origin is closest to the cursor.
  103. /// </summary>
  104. /// <param name="textComponent">A reference to the text object.</param>
  105. /// <param name="position">Position to check for intersection.</param>
  106. /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
  107. /// <param name="cursor">The position of the cursor insertion position relative to the position.</param>
  108. /// <returns></returns>
  109. public static int GetCursorIndexFromPosition(TMP_Text textComponent, Vector3 position, Camera camera, out CaretPosition cursor)
  110. {
  111. int line = TMP_TextUtilities.FindNearestLine(textComponent, position, camera);
  112. int index = FindNearestCharacterOnLine(textComponent, position, line, camera, false);
  113. // Special handling if line contains only one character.
  114. if (textComponent.textInfo.lineInfo[line].characterCount == 1)
  115. {
  116. cursor = CaretPosition.Left;
  117. return index;
  118. }
  119. RectTransform rectTransform = textComponent.rectTransform;
  120. // Convert position into Worldspace coordinates
  121. ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
  122. TMP_CharacterInfo cInfo = textComponent.textInfo.characterInfo[index];
  123. // Get Bottom Left and Top Right position of the current character
  124. Vector3 bl = rectTransform.TransformPoint(cInfo.bottomLeft);
  125. Vector3 tr = rectTransform.TransformPoint(cInfo.topRight);
  126. float insertPosition = (position.x - bl.x) / (tr.x - bl.x);
  127. if (insertPosition < 0.5f)
  128. {
  129. cursor = CaretPosition.Left;
  130. return index;
  131. }
  132. else
  133. {
  134. cursor = CaretPosition.Right;
  135. return index;
  136. }
  137. }
  138. /// <summary>
  139. /// Function returning the line nearest to the position.
  140. /// </summary>
  141. /// <param name="textComponent"></param>
  142. /// <param name="position"></param>
  143. /// <param name="camera"></param>
  144. /// <returns></returns>
  145. public static int FindNearestLine(TMP_Text text, Vector3 position, Camera camera)
  146. {
  147. RectTransform rectTransform = text.rectTransform;
  148. float distance = Mathf.Infinity;
  149. int closest = -1;
  150. // Convert position into Worldspace coordinates
  151. ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
  152. for (int i = 0; i < text.textInfo.lineCount; i++)
  153. {
  154. TMP_LineInfo lineInfo = text.textInfo.lineInfo[i];
  155. float ascender = rectTransform.TransformPoint(new Vector3(0, lineInfo.ascender, 0)).y;
  156. float descender = rectTransform.TransformPoint(new Vector3(0, lineInfo.descender, 0)).y;
  157. if (ascender > position.y && descender < position.y)
  158. {
  159. //Debug.Log("Position is on line " + i);
  160. return i;
  161. }
  162. float d0 = Mathf.Abs(ascender - position.y);
  163. float d1 = Mathf.Abs(descender - position.y);
  164. float d = Mathf.Min(d0, d1);
  165. if (d < distance)
  166. {
  167. distance = d;
  168. closest = i;
  169. }
  170. }
  171. //Debug.Log("Closest line to position is " + closest);
  172. return closest;
  173. }
  174. /// <summary>
  175. /// Function returning the nearest character to position on a given line.
  176. /// </summary>
  177. /// <param name="text"></param>
  178. /// <param name="position"></param>
  179. /// <param name="line"></param>
  180. /// <param name="camera"></param>
  181. /// <returns></returns>
  182. public static int FindNearestCharacterOnLine(TMP_Text text, Vector3 position, int line, Camera camera, bool visibleOnly)
  183. {
  184. RectTransform rectTransform = text.rectTransform;
  185. // Convert position into Worldspace coordinates
  186. ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
  187. int firstCharacter = text.textInfo.lineInfo[line].firstCharacterIndex;
  188. int lastCharacter = text.textInfo.lineInfo[line].lastCharacterIndex;
  189. float distanceSqr = Mathf.Infinity;
  190. int closest = lastCharacter;
  191. for (int i = firstCharacter; i < lastCharacter; i++)
  192. {
  193. // Get current character info.
  194. TMP_CharacterInfo cInfo = text.textInfo.characterInfo[i];
  195. if (visibleOnly && !cInfo.isVisible) continue;
  196. // Get Bottom Left and Top Right position of the current character
  197. Vector3 bl = rectTransform.TransformPoint(cInfo.bottomLeft);
  198. Vector3 tl = rectTransform.TransformPoint(new Vector3(cInfo.bottomLeft.x, cInfo.topRight.y, 0));
  199. Vector3 tr = rectTransform.TransformPoint(cInfo.topRight);
  200. Vector3 br = rectTransform.TransformPoint(new Vector3(cInfo.topRight.x, cInfo.bottomLeft.y, 0));
  201. if (PointIntersectRectangle(position, bl, tl, tr, br))
  202. {
  203. closest = i;
  204. break;
  205. }
  206. // Find the closest corner to position.
  207. float dbl = DistanceToLine(bl, tl, position);
  208. float dtl = DistanceToLine(tl, tr, position);
  209. float dtr = DistanceToLine(tr, br, position);
  210. float dbr = DistanceToLine(br, bl, position);
  211. float d = dbl < dtl ? dbl : dtl;
  212. d = d < dtr ? d : dtr;
  213. d = d < dbr ? d : dbr;
  214. if (distanceSqr > d)
  215. {
  216. distanceSqr = d;
  217. closest = i;
  218. }
  219. }
  220. return closest;
  221. }
  222. /// <summary>
  223. /// Function used to determine if the position intersects with the RectTransform.
  224. /// </summary>
  225. /// <param name="rectTransform">A reference to the RectTranform of the text object.</param>
  226. /// <param name="position">Position to check for intersection.</param>
  227. /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
  228. /// <returns></returns>
  229. public static bool IsIntersectingRectTransform(RectTransform rectTransform, Vector3 position, Camera camera)
  230. {
  231. // Convert position into Worldspace coordinates
  232. ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
  233. rectTransform.GetWorldCorners(m_rectWorldCorners);
  234. if (PointIntersectRectangle(position, m_rectWorldCorners[0], m_rectWorldCorners[1], m_rectWorldCorners[2], m_rectWorldCorners[3]))
  235. {
  236. return true;
  237. }
  238. return false;
  239. }
  240. // CHARACTER HANDLING
  241. /// <summary>
  242. /// Function returning the index of the character at the given position (if any).
  243. /// </summary>
  244. /// <param name="text">A reference to the TextMeshPro component.</param>
  245. /// <param name="position">Position to check for intersection.</param>
  246. /// <param name="camera">The scene camera which is rendering the text or whichever one might be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
  247. /// <param name="visibleOnly">Only check for visible characters.</param>
  248. /// <returns></returns>
  249. public static int FindIntersectingCharacter(TMP_Text text, Vector3 position, Camera camera, bool visibleOnly)
  250. {
  251. RectTransform rectTransform = text.rectTransform;
  252. // Convert position into Worldspace coordinates
  253. ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
  254. for (int i = 0; i < text.textInfo.characterCount; i++)
  255. {
  256. // Get current character info.
  257. TMP_CharacterInfo cInfo = text.textInfo.characterInfo[i];
  258. if (visibleOnly && !cInfo.isVisible) continue;
  259. // Get Bottom Left and Top Right position of the current character
  260. Vector3 bl = rectTransform.TransformPoint(cInfo.bottomLeft);
  261. Vector3 tl = rectTransform.TransformPoint(new Vector3(cInfo.bottomLeft.x, cInfo.topRight.y, 0));
  262. Vector3 tr = rectTransform.TransformPoint(cInfo.topRight);
  263. Vector3 br = rectTransform.TransformPoint(new Vector3(cInfo.topRight.x, cInfo.bottomLeft.y, 0));
  264. if (PointIntersectRectangle(position, bl, tl, tr, br))
  265. return i;
  266. }
  267. return -1;
  268. }
  269. /// <summary>
  270. /// Function returning the index of the character at the given position (if any).
  271. /// </summary>
  272. /// <param name="text">A reference to the TextMeshPro UGUI component.</param>
  273. /// <param name="position">Position to check for intersection.</param>
  274. /// <param name="camera">The camera which is rendering the text object.</param>
  275. /// <param name="visibleOnly">Only check for visible characters.</param>
  276. /// <returns></returns>
  277. //public static int FindIntersectingCharacter(TextMeshPro text, Vector3 position, Camera camera, bool visibleOnly)
  278. //{
  279. // Transform textTransform = text.transform;
  280. // // Convert position into Worldspace coordinates
  281. // ScreenPointToWorldPointInRectangle(textTransform, position, camera, out position);
  282. // for (int i = 0; i < text.textInfo.characterCount; i++)
  283. // {
  284. // // Get current character info.
  285. // TMP_CharacterInfo cInfo = text.textInfo.characterInfo[i];
  286. // if ((visibleOnly && !cInfo.isVisible) || (text.OverflowMode == TextOverflowModes.Page && cInfo.pageNumber + 1 != text.pageToDisplay))
  287. // continue;
  288. // // Get Bottom Left and Top Right position of the current character
  289. // Vector3 bl = textTransform.TransformPoint(cInfo.bottomLeft);
  290. // Vector3 tl = textTransform.TransformPoint(new Vector3(cInfo.bottomLeft.x, cInfo.topRight.y, 0));
  291. // Vector3 tr = textTransform.TransformPoint(cInfo.topRight);
  292. // Vector3 br = textTransform.TransformPoint(new Vector3(cInfo.topRight.x, cInfo.bottomLeft.y, 0));
  293. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  294. // return i;
  295. // }
  296. // return -1;
  297. //}
  298. /// <summary>
  299. /// Function to find the nearest character to position.
  300. /// </summary>
  301. /// <param name="text">A reference to the TMP Text component.</param>
  302. /// <param name="position">Position to check for intersection.</param>
  303. /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
  304. /// <param name="visibleOnly">Only check for visible characters.</param>
  305. /// <returns></returns>
  306. public static int FindNearestCharacter(TMP_Text text, Vector3 position, Camera camera, bool visibleOnly)
  307. {
  308. RectTransform rectTransform = text.rectTransform;
  309. float distanceSqr = Mathf.Infinity;
  310. int closest = 0;
  311. // Convert position into Worldspace coordinates
  312. ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
  313. for (int i = 0; i < text.textInfo.characterCount; i++)
  314. {
  315. // Get current character info.
  316. TMP_CharacterInfo cInfo = text.textInfo.characterInfo[i];
  317. if (visibleOnly && !cInfo.isVisible) continue;
  318. // Get Bottom Left and Top Right position of the current character
  319. Vector3 bl = rectTransform.TransformPoint(cInfo.bottomLeft);
  320. Vector3 tl = rectTransform.TransformPoint(new Vector3(cInfo.bottomLeft.x, cInfo.topRight.y, 0));
  321. Vector3 tr = rectTransform.TransformPoint(cInfo.topRight);
  322. Vector3 br = rectTransform.TransformPoint(new Vector3(cInfo.topRight.x, cInfo.bottomLeft.y, 0));
  323. if (PointIntersectRectangle(position, bl, tl, tr, br))
  324. return i;
  325. // Find the closest corner to position.
  326. float dbl = DistanceToLine(bl, tl, position);
  327. float dtl = DistanceToLine(tl, tr, position);
  328. float dtr = DistanceToLine(tr, br, position);
  329. float dbr = DistanceToLine(br, bl, position);
  330. float d = dbl < dtl ? dbl : dtl;
  331. d = d < dtr ? d : dtr;
  332. d = d < dbr ? d : dbr;
  333. if (distanceSqr > d)
  334. {
  335. distanceSqr = d;
  336. closest = i;
  337. }
  338. }
  339. return closest;
  340. }
  341. /// <summary>
  342. /// Function to find the nearest character to position.
  343. /// </summary>
  344. /// <param name="text">A reference to the TextMeshPro UGUI component.</param>
  345. /// <param name="position">Position to check for intersection.</param>
  346. /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
  347. /// <param name="visibleOnly">Only check for visible characters.</param>
  348. /// <returns></returns>
  349. //public static int FindNearestCharacter(TextMeshProUGUI text, Vector3 position, Camera camera, bool visibleOnly)
  350. //{
  351. // RectTransform rectTransform = text.rectTransform;
  352. // float distanceSqr = Mathf.Infinity;
  353. // int closest = 0;
  354. // // Convert position into Worldspace coordinates
  355. // ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
  356. // for (int i = 0; i < text.textInfo.characterCount; i++)
  357. // {
  358. // // Get current character info.
  359. // TMP_CharacterInfo cInfo = text.textInfo.characterInfo[i];
  360. // if ((visibleOnly && !cInfo.isVisible) || (text.OverflowMode == TextOverflowModes.Page && cInfo.pageNumber + 1 != text.pageToDisplay))
  361. // continue;
  362. // // Get Bottom Left and Top Right position of the current character
  363. // Vector3 bl = rectTransform.TransformPoint(cInfo.bottomLeft);
  364. // Vector3 tl = rectTransform.TransformPoint(new Vector3(cInfo.bottomLeft.x, cInfo.topRight.y, 0));
  365. // Vector3 tr = rectTransform.TransformPoint(cInfo.topRight);
  366. // Vector3 br = rectTransform.TransformPoint(new Vector3(cInfo.topRight.x, cInfo.bottomLeft.y, 0));
  367. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  368. // return i;
  369. // // Find the closest corner to position.
  370. // float dbl = DistanceToLine(bl, tl, position);
  371. // float dtl = DistanceToLine(tl, tr, position);
  372. // float dtr = DistanceToLine(tr, br, position);
  373. // float dbr = DistanceToLine(br, bl, position);
  374. // float d = dbl < dtl ? dbl : dtl;
  375. // d = d < dtr ? d : dtr;
  376. // d = d < dbr ? d : dbr;
  377. // if (distanceSqr > d)
  378. // {
  379. // distanceSqr = d;
  380. // closest = i;
  381. // }
  382. // }
  383. // //Debug.Log("Returning nearest character at index: " + closest);
  384. // return closest;
  385. //}
  386. /// <summary>
  387. /// Function to find the nearest character to position.
  388. /// </summary>
  389. /// <param name="text">A reference to the TextMeshPro component.</param>
  390. /// <param name="position">Position to check for intersection.</param>
  391. /// <param name="camera">The camera which is rendering the text object.</param>
  392. /// <param name="visibleOnly">Only check for visible characters.</param>
  393. /// <returns></returns>
  394. //public static int FindNearestCharacter(TextMeshPro text, Vector3 position, Camera camera, bool visibleOnly)
  395. //{
  396. // Transform textTransform = text.transform;
  397. // float distanceSqr = Mathf.Infinity;
  398. // int closest = 0;
  399. // // Convert position into Worldspace coordinates
  400. // ScreenPointToWorldPointInRectangle(textTransform, position, camera, out position);
  401. // for (int i = 0; i < text.textInfo.characterCount; i++)
  402. // {
  403. // // Get current character info.
  404. // TMP_CharacterInfo cInfo = text.textInfo.characterInfo[i];
  405. // if ((visibleOnly && !cInfo.isVisible) || (text.OverflowMode == TextOverflowModes.Page && cInfo.pageNumber + 1 != text.pageToDisplay))
  406. // continue;
  407. // // Get Bottom Left and Top Right position of the current character
  408. // Vector3 bl = textTransform.TransformPoint(cInfo.bottomLeft);
  409. // Vector3 tl = textTransform.TransformPoint(new Vector3(cInfo.bottomLeft.x, cInfo.topRight.y, 0));
  410. // Vector3 tr = textTransform.TransformPoint(cInfo.topRight);
  411. // Vector3 br = textTransform.TransformPoint(new Vector3(cInfo.topRight.x, cInfo.bottomLeft.y, 0));
  412. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  413. // return i;
  414. // // Find the closest corner to position.
  415. // float dbl = DistanceToLine(bl, tl, position); // (position - bl).sqrMagnitude;
  416. // float dtl = DistanceToLine(tl, tr, position); // (position - tl).sqrMagnitude;
  417. // float dtr = DistanceToLine(tr, br, position); // (position - tr).sqrMagnitude;
  418. // float dbr = DistanceToLine(br, bl, position); // (position - br).sqrMagnitude;
  419. // float d = dbl < dtl ? dbl : dtl;
  420. // d = d < dtr ? d : dtr;
  421. // d = d < dbr ? d : dbr;
  422. // if (distanceSqr > d)
  423. // {
  424. // distanceSqr = d;
  425. // closest = i;
  426. // }
  427. // }
  428. // //Debug.Log("Returning nearest character at index: " + closest);
  429. // return closest;
  430. //}
  431. // WORD HANDLING
  432. /// <summary>
  433. /// Function returning the index of the word at the given position (if any).
  434. /// </summary>
  435. /// <param name="text">A reference to the TMP_Text component.</param>
  436. /// <param name="position">Position to check for intersection.</param>
  437. /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
  438. /// <returns></returns>
  439. public static int FindIntersectingWord(TMP_Text text, Vector3 position, Camera camera)
  440. {
  441. RectTransform rectTransform = text.rectTransform;
  442. // Convert position into Worldspace coordinates
  443. ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
  444. for (int i = 0; i < text.textInfo.wordCount; i++)
  445. {
  446. TMP_WordInfo wInfo = text.textInfo.wordInfo[i];
  447. bool isBeginRegion = false;
  448. Vector3 bl = Vector3.zero;
  449. Vector3 tl = Vector3.zero;
  450. Vector3 br = Vector3.zero;
  451. Vector3 tr = Vector3.zero;
  452. float maxAscender = -Mathf.Infinity;
  453. float minDescender = Mathf.Infinity;
  454. // Iterate through each character of the word
  455. for (int j = 0; j < wInfo.characterCount; j++)
  456. {
  457. int characterIndex = wInfo.firstCharacterIndex + j;
  458. TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[characterIndex];
  459. int currentLine = currentCharInfo.lineNumber;
  460. bool isCharacterVisible = currentCharInfo.isVisible;
  461. // Track maximum Ascender and minimum Descender for each word.
  462. maxAscender = Mathf.Max(maxAscender, currentCharInfo.ascender);
  463. minDescender = Mathf.Min(minDescender, currentCharInfo.descender);
  464. if (isBeginRegion == false && isCharacterVisible)
  465. {
  466. isBeginRegion = true;
  467. bl = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0);
  468. tl = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0);
  469. //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
  470. // If Word is one character
  471. if (wInfo.characterCount == 1)
  472. {
  473. isBeginRegion = false;
  474. br = new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0);
  475. tr = new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0);
  476. // Transform coordinates to be relative to transform and account min descender and max ascender.
  477. bl = rectTransform.TransformPoint(new Vector3(bl.x, minDescender, 0));
  478. tl = rectTransform.TransformPoint(new Vector3(tl.x, maxAscender, 0));
  479. tr = rectTransform.TransformPoint(new Vector3(tr.x, maxAscender, 0));
  480. br = rectTransform.TransformPoint(new Vector3(br.x, minDescender, 0));
  481. // Check for Intersection
  482. if (PointIntersectRectangle(position, bl, tl, tr, br))
  483. return i;
  484. //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  485. }
  486. }
  487. // Last Character of Word
  488. if (isBeginRegion && j == wInfo.characterCount - 1)
  489. {
  490. isBeginRegion = false;
  491. br = new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0);
  492. tr = new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0);
  493. // Transform coordinates to be relative to transform and account min descender and max ascender.
  494. bl = rectTransform.TransformPoint(new Vector3(bl.x, minDescender, 0));
  495. tl = rectTransform.TransformPoint(new Vector3(tl.x, maxAscender, 0));
  496. tr = rectTransform.TransformPoint(new Vector3(tr.x, maxAscender, 0));
  497. br = rectTransform.TransformPoint(new Vector3(br.x, minDescender, 0));
  498. // Check for Intersection
  499. if (PointIntersectRectangle(position, bl, tl, tr, br))
  500. return i;
  501. //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  502. }
  503. // If Word is split on more than one line.
  504. else if (isBeginRegion && currentLine != text.textInfo.characterInfo[characterIndex + 1].lineNumber)
  505. {
  506. isBeginRegion = false;
  507. br = new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0);
  508. tr = new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0);
  509. // Transform coordinates to be relative to transform and account min descender and max ascender.
  510. bl = rectTransform.TransformPoint(new Vector3(bl.x, minDescender, 0));
  511. tl = rectTransform.TransformPoint(new Vector3(tl.x, maxAscender, 0));
  512. tr = rectTransform.TransformPoint(new Vector3(tr.x, maxAscender, 0));
  513. br = rectTransform.TransformPoint(new Vector3(br.x, minDescender, 0));
  514. maxAscender = -Mathf.Infinity;
  515. minDescender = Mathf.Infinity;
  516. // Check for Intersection
  517. if (PointIntersectRectangle(position, bl, tl, tr, br))
  518. return i;
  519. //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  520. }
  521. }
  522. //Debug.Log("Word at Index: " + i + " is located at (" + bl + ", " + tl + ", " + tr + ", " + br + ").");
  523. }
  524. return -1;
  525. }
  526. /// <summary>
  527. /// Function returning the index of the word at the given position (if any).
  528. /// </summary>
  529. /// <param name="text">A reference to the TextMeshPro UGUI component.</param>
  530. /// <param name="position">Position to check for intersection.</param>
  531. /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
  532. /// <returns></returns>
  533. //public static int FindIntersectingWord(TextMeshProUGUI text, Vector3 position, Camera camera)
  534. //{
  535. // RectTransform rectTransform = text.rectTransform;
  536. // // Convert position into Worldspace coordinates
  537. // ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
  538. // for (int i = 0; i < text.textInfo.wordCount; i++)
  539. // {
  540. // TMP_WordInfo wInfo = text.textInfo.wordInfo[i];
  541. // bool isBeginRegion = false;
  542. // Vector3 bl = Vector3.zero;
  543. // Vector3 tl = Vector3.zero;
  544. // Vector3 br = Vector3.zero;
  545. // Vector3 tr = Vector3.zero;
  546. // float maxAscender = -Mathf.Infinity;
  547. // float minDescender = Mathf.Infinity;
  548. // // Iterate through each character of the word
  549. // for (int j = 0; j < wInfo.characterCount; j++)
  550. // {
  551. // int characterIndex = wInfo.firstCharacterIndex + j;
  552. // TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[characterIndex];
  553. // int currentLine = currentCharInfo.lineNumber;
  554. // bool isCharacterVisible = characterIndex > text.maxVisibleCharacters ||
  555. // currentCharInfo.lineNumber > text.maxVisibleLines ||
  556. // (text.OverflowMode == TextOverflowModes.Page && currentCharInfo.pageNumber + 1 != text.pageToDisplay) ? false : true;
  557. // // Track maximum Ascender and minimum Descender for each word.
  558. // maxAscender = Mathf.Max(maxAscender, currentCharInfo.ascender);
  559. // minDescender = Mathf.Min(minDescender, currentCharInfo.descender);
  560. // if (isBeginRegion == false && isCharacterVisible)
  561. // {
  562. // isBeginRegion = true;
  563. // bl = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0);
  564. // tl = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0);
  565. // //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
  566. // // If Word is one character
  567. // if (wInfo.characterCount == 1)
  568. // {
  569. // isBeginRegion = false;
  570. // br = new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0);
  571. // tr = new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0);
  572. // // Transform coordinates to be relative to transform and account min descender and max ascender.
  573. // bl = rectTransform.TransformPoint(new Vector3(bl.x, minDescender, 0));
  574. // tl = rectTransform.TransformPoint(new Vector3(tl.x, maxAscender, 0));
  575. // tr = rectTransform.TransformPoint(new Vector3(tr.x, maxAscender, 0));
  576. // br = rectTransform.TransformPoint(new Vector3(br.x, minDescender, 0));
  577. // // Check for Intersection
  578. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  579. // return i;
  580. // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  581. // }
  582. // }
  583. // // Last Character of Word
  584. // if (isBeginRegion && j == wInfo.characterCount - 1)
  585. // {
  586. // isBeginRegion = false;
  587. // br = new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0);
  588. // tr = new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0);
  589. // // Transform coordinates to be relative to transform and account min descender and max ascender.
  590. // bl = rectTransform.TransformPoint(new Vector3(bl.x, minDescender, 0));
  591. // tl = rectTransform.TransformPoint(new Vector3(tl.x, maxAscender, 0));
  592. // tr = rectTransform.TransformPoint(new Vector3(tr.x, maxAscender, 0));
  593. // br = rectTransform.TransformPoint(new Vector3(br.x, minDescender, 0));
  594. // // Check for Intersection
  595. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  596. // return i;
  597. // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  598. // }
  599. // // If Word is split on more than one line.
  600. // else if (isBeginRegion && currentLine != text.textInfo.characterInfo[characterIndex + 1].lineNumber)
  601. // {
  602. // isBeginRegion = false;
  603. // br = new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0);
  604. // tr = new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0);
  605. // // Transform coordinates to be relative to transform and account min descender and max ascender.
  606. // bl = rectTransform.TransformPoint(new Vector3(bl.x, minDescender, 0));
  607. // tl = rectTransform.TransformPoint(new Vector3(tl.x, maxAscender, 0));
  608. // tr = rectTransform.TransformPoint(new Vector3(tr.x, maxAscender, 0));
  609. // br = rectTransform.TransformPoint(new Vector3(br.x, minDescender, 0));
  610. // maxAscender = -Mathf.Infinity;
  611. // minDescender = Mathf.Infinity;
  612. // // Check for Intersection
  613. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  614. // return i;
  615. // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  616. // }
  617. // }
  618. // //Debug.Log("Word at Index: " + i + " is located at (" + bl + ", " + tl + ", " + tr + ", " + br + ").");
  619. // }
  620. // return -1;
  621. //}
  622. /// <summary>
  623. /// Function returning the index of the word at the given position (if any).
  624. /// </summary>
  625. /// <param name="text">A reference to the TextMeshPro component.</param>
  626. /// <param name="position">Position to check for intersection.</param>
  627. /// <param name="camera">The camera which is rendering the text object.</param>
  628. /// <returns></returns>
  629. //public static int FindIntersectingWord(TextMeshPro text, Vector3 position, Camera camera)
  630. //{
  631. // Transform textTransform = text.transform;
  632. // // Convert position into Worldspace coordinates
  633. // ScreenPointToWorldPointInRectangle(textTransform, position, camera, out position);
  634. // for (int i = 0; i < text.textInfo.wordCount; i++)
  635. // {
  636. // TMP_WordInfo wInfo = text.textInfo.wordInfo[i];
  637. // bool isBeginRegion = false;
  638. // Vector3 bl = Vector3.zero;
  639. // Vector3 tl = Vector3.zero;
  640. // Vector3 br = Vector3.zero;
  641. // Vector3 tr = Vector3.zero;
  642. // float maxAscender = -Mathf.Infinity;
  643. // float minDescender = Mathf.Infinity;
  644. // // Iterate through each character of the word
  645. // for (int j = 0; j < wInfo.characterCount; j++)
  646. // {
  647. // int characterIndex = wInfo.firstCharacterIndex + j;
  648. // TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[characterIndex];
  649. // int currentLine = currentCharInfo.lineNumber;
  650. // bool isCharacterVisible = characterIndex > text.maxVisibleCharacters ||
  651. // currentCharInfo.lineNumber > text.maxVisibleLines ||
  652. // (text.OverflowMode == TextOverflowModes.Page && currentCharInfo.pageNumber + 1 != text.pageToDisplay) ? false : true;
  653. // // Track maximum Ascender and minimum Descender for each word.
  654. // maxAscender = Mathf.Max(maxAscender, currentCharInfo.ascender);
  655. // minDescender = Mathf.Min(minDescender, currentCharInfo.descender);
  656. // if (isBeginRegion == false && isCharacterVisible)
  657. // {
  658. // isBeginRegion = true;
  659. // bl = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0);
  660. // tl = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0);
  661. // //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
  662. // // If Word is one character
  663. // if (wInfo.characterCount == 1)
  664. // {
  665. // isBeginRegion = false;
  666. // br = new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0);
  667. // tr = new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0);
  668. // // Transform coordinates to be relative to transform and account min descender and max ascender.
  669. // bl = textTransform.TransformPoint(new Vector3(bl.x, minDescender, 0));
  670. // tl = textTransform.TransformPoint(new Vector3(tl.x, maxAscender, 0));
  671. // tr = textTransform.TransformPoint(new Vector3(tr.x, maxAscender, 0));
  672. // br = textTransform.TransformPoint(new Vector3(br.x, minDescender, 0));
  673. // // Check for Intersection
  674. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  675. // return i;
  676. // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  677. // }
  678. // }
  679. // // Last Character of Word
  680. // if (isBeginRegion && j == wInfo.characterCount - 1)
  681. // {
  682. // isBeginRegion = false;
  683. // br = new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0);
  684. // tr = new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0);
  685. // // Transform coordinates to be relative to transform and account min descender and max ascender.
  686. // bl = textTransform.TransformPoint(new Vector3(bl.x, minDescender, 0));
  687. // tl = textTransform.TransformPoint(new Vector3(tl.x, maxAscender, 0));
  688. // tr = textTransform.TransformPoint(new Vector3(tr.x, maxAscender, 0));
  689. // br = textTransform.TransformPoint(new Vector3(br.x, minDescender, 0));
  690. // // Check for Intersection
  691. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  692. // return i;
  693. // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  694. // }
  695. // // If Word is split on more than one line.
  696. // else if (isBeginRegion && currentLine != text.textInfo.characterInfo[characterIndex + 1].lineNumber)
  697. // {
  698. // isBeginRegion = false;
  699. // br = new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0);
  700. // tr = new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0);
  701. // // Transform coordinates to be relative to transform and account min descender and max ascender.
  702. // bl = textTransform.TransformPoint(new Vector3(bl.x, minDescender, 0));
  703. // tl = textTransform.TransformPoint(new Vector3(tl.x, maxAscender, 0));
  704. // tr = textTransform.TransformPoint(new Vector3(tr.x, maxAscender, 0));
  705. // br = textTransform.TransformPoint(new Vector3(br.x, minDescender, 0));
  706. // // Reset maxAscender and minDescender for next word segment.
  707. // maxAscender = -Mathf.Infinity;
  708. // minDescender = Mathf.Infinity;
  709. // // Check for Intersection
  710. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  711. // return i;
  712. // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  713. // }
  714. // }
  715. // }
  716. // return -1;
  717. //}
  718. /// <summary>
  719. /// Function returning the index of the word at the given position (if any).
  720. /// </summary>
  721. /// <param name="text">A reference to the TMP_Text component.</param>
  722. /// <param name="position"></param>
  723. /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
  724. /// <returns></returns>
  725. public static int FindNearestWord(TMP_Text text, Vector3 position, Camera camera)
  726. {
  727. RectTransform rectTransform = text.rectTransform;
  728. float distanceSqr = Mathf.Infinity;
  729. int closest = 0;
  730. // Convert position into Worldspace coordinates
  731. ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
  732. for (int i = 0; i < text.textInfo.wordCount; i++)
  733. {
  734. TMP_WordInfo wInfo = text.textInfo.wordInfo[i];
  735. bool isBeginRegion = false;
  736. Vector3 bl = Vector3.zero;
  737. Vector3 tl = Vector3.zero;
  738. Vector3 br = Vector3.zero;
  739. Vector3 tr = Vector3.zero;
  740. // Iterate through each character of the word
  741. for (int j = 0; j < wInfo.characterCount; j++)
  742. {
  743. int characterIndex = wInfo.firstCharacterIndex + j;
  744. TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[characterIndex];
  745. int currentLine = currentCharInfo.lineNumber;
  746. bool isCharacterVisible = currentCharInfo.isVisible;
  747. if (isBeginRegion == false && isCharacterVisible)
  748. {
  749. isBeginRegion = true;
  750. bl = rectTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0));
  751. tl = rectTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0));
  752. //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
  753. // If Word is one character
  754. if (wInfo.characterCount == 1)
  755. {
  756. isBeginRegion = false;
  757. br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  758. tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  759. // Check for Intersection
  760. if (PointIntersectRectangle(position, bl, tl, tr, br))
  761. return i;
  762. // Find the closest line segment to position.
  763. float dbl = DistanceToLine(bl, tl, position);
  764. float dtl = DistanceToLine(tl, tr, position);
  765. float dtr = DistanceToLine(tr, br, position);
  766. float dbr = DistanceToLine(br, bl, position);
  767. float d = dbl < dtl ? dbl : dtl;
  768. d = d < dtr ? d : dtr;
  769. d = d < dbr ? d : dbr;
  770. if (distanceSqr > d)
  771. {
  772. distanceSqr = d;
  773. closest = i;
  774. }
  775. }
  776. }
  777. // Last Character of Word
  778. if (isBeginRegion && j == wInfo.characterCount - 1)
  779. {
  780. isBeginRegion = false;
  781. br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  782. tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  783. // Check for Intersection
  784. if (PointIntersectRectangle(position, bl, tl, tr, br))
  785. return i;
  786. // Find the closest line segment to position.
  787. float dbl = DistanceToLine(bl, tl, position);
  788. float dtl = DistanceToLine(tl, tr, position);
  789. float dtr = DistanceToLine(tr, br, position);
  790. float dbr = DistanceToLine(br, bl, position);
  791. float d = dbl < dtl ? dbl : dtl;
  792. d = d < dtr ? d : dtr;
  793. d = d < dbr ? d : dbr;
  794. if (distanceSqr > d)
  795. {
  796. distanceSqr = d;
  797. closest = i;
  798. }
  799. }
  800. // If Word is split on more than one line.
  801. else if (isBeginRegion && currentLine != text.textInfo.characterInfo[characterIndex + 1].lineNumber)
  802. {
  803. isBeginRegion = false;
  804. br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  805. tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  806. // Check for Intersection
  807. if (PointIntersectRectangle(position, bl, tl, tr, br))
  808. return i;
  809. // Find the closest line segment to position.
  810. float dbl = DistanceToLine(bl, tl, position);
  811. float dtl = DistanceToLine(tl, tr, position);
  812. float dtr = DistanceToLine(tr, br, position);
  813. float dbr = DistanceToLine(br, bl, position);
  814. float d = dbl < dtl ? dbl : dtl;
  815. d = d < dtr ? d : dtr;
  816. d = d < dbr ? d : dbr;
  817. if (distanceSqr > d)
  818. {
  819. distanceSqr = d;
  820. closest = i;
  821. }
  822. }
  823. }
  824. //Debug.Log("Word at Index: " + i + " is located at (" + bl + ", " + tl + ", " + tr + ", " + br + ").");
  825. }
  826. return closest;
  827. }
  828. /// <summary>
  829. /// Function returning the index of the word at the given position (if any).
  830. /// </summary>
  831. /// <param name="text">A reference to the TextMeshPro UGUI component.</param>
  832. /// <param name="position"></param>
  833. /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
  834. /// <returns></returns>
  835. //public static int FindNearestWord(TextMeshProUGUI text, Vector3 position, Camera camera)
  836. //{
  837. // RectTransform rectTransform = text.rectTransform;
  838. // float distanceSqr = Mathf.Infinity;
  839. // int closest = 0;
  840. // // Convert position into Worldspace coordinates
  841. // ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
  842. // for (int i = 0; i < text.textInfo.wordCount; i++)
  843. // {
  844. // TMP_WordInfo wInfo = text.textInfo.wordInfo[i];
  845. // bool isBeginRegion = false;
  846. // Vector3 bl = Vector3.zero;
  847. // Vector3 tl = Vector3.zero;
  848. // Vector3 br = Vector3.zero;
  849. // Vector3 tr = Vector3.zero;
  850. // // Iterate through each character of the word
  851. // for (int j = 0; j < wInfo.characterCount; j++)
  852. // {
  853. // int characterIndex = wInfo.firstCharacterIndex + j;
  854. // TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[characterIndex];
  855. // int currentLine = currentCharInfo.lineNumber;
  856. // bool isCharacterVisible = characterIndex > text.maxVisibleCharacters ||
  857. // currentCharInfo.lineNumber > text.maxVisibleLines ||
  858. // (text.OverflowMode == TextOverflowModes.Page && currentCharInfo.pageNumber + 1 != text.pageToDisplay) ? false : true;
  859. // if (isBeginRegion == false && isCharacterVisible)
  860. // {
  861. // isBeginRegion = true;
  862. // bl = rectTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0));
  863. // tl = rectTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0));
  864. // //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
  865. // // If Word is one character
  866. // if (wInfo.characterCount == 1)
  867. // {
  868. // isBeginRegion = false;
  869. // br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  870. // tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  871. // // Check for Intersection
  872. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  873. // return i;
  874. // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  875. // }
  876. // }
  877. // // Last Character of Word
  878. // if (isBeginRegion && j == wInfo.characterCount - 1)
  879. // {
  880. // isBeginRegion = false;
  881. // br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  882. // tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  883. // // Check for Intersection
  884. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  885. // return i;
  886. // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  887. // }
  888. // // If Word is split on more than one line.
  889. // else if (isBeginRegion && currentLine != text.textInfo.characterInfo[characterIndex + 1].lineNumber)
  890. // {
  891. // isBeginRegion = false;
  892. // br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  893. // tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  894. // // Check for Intersection
  895. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  896. // return i;
  897. // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  898. // }
  899. // }
  900. // // Find the closest line segment to position.
  901. // float dbl = DistanceToLine(bl, tl, position); // (position - bl).sqrMagnitude;
  902. // float dtl = DistanceToLine(tl, tr, position); // (position - tl).sqrMagnitude;
  903. // float dtr = DistanceToLine(tr, br, position); // (position - tr).sqrMagnitude;
  904. // float dbr = DistanceToLine(br, bl, position); // (position - br).sqrMagnitude;
  905. // float d = dbl < dtl ? dbl : dtl;
  906. // d = d < dtr ? d : dtr;
  907. // d = d < dbr ? d : dbr;
  908. // if (distanceSqr > d)
  909. // {
  910. // distanceSqr = d;
  911. // closest = i;
  912. // }
  913. // //Debug.Log("Word at Index: " + i + " is located at (" + bl + ", " + tl + ", " + tr + ", " + br + ").");
  914. // }
  915. // return closest;
  916. //}
  917. /// <summary>
  918. /// Function returning the index of the word at the given position (if any).
  919. /// </summary>
  920. /// <param name="text">A reference to the TextMeshPro UGUI component.</param>
  921. /// <param name="position">Position to check for intersection.</param>
  922. /// <param name="camera">The camera which is rendering the text object.</param>
  923. /// <returns></returns>
  924. //public static int FindNearestWord(TextMeshPro text, Vector3 position, Camera camera)
  925. //{
  926. // Transform textTransform = text.transform;
  927. // float distanceSqr = Mathf.Infinity;
  928. // int closest = 0;
  929. // // Convert position into Worldspace coordinates
  930. // ScreenPointToWorldPointInRectangle(textTransform, position, camera, out position);
  931. // for (int i = 0; i < text.textInfo.wordCount; i++)
  932. // {
  933. // TMP_WordInfo wInfo = text.textInfo.wordInfo[i];
  934. // bool isBeginRegion = false;
  935. // Vector3 bl = Vector3.zero;
  936. // Vector3 tl = Vector3.zero;
  937. // Vector3 br = Vector3.zero;
  938. // Vector3 tr = Vector3.zero;
  939. // // Iterate through each character of the word
  940. // for (int j = 0; j < wInfo.characterCount; j++)
  941. // {
  942. // int characterIndex = wInfo.firstCharacterIndex + j;
  943. // TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[characterIndex];
  944. // int currentLine = currentCharInfo.lineNumber;
  945. // bool isCharacterVisible = characterIndex > text.maxVisibleCharacters ||
  946. // currentCharInfo.lineNumber > text.maxVisibleLines ||
  947. // (text.OverflowMode == TextOverflowModes.Page && currentCharInfo.pageNumber + 1 != text.pageToDisplay) ? false : true;
  948. // if (isBeginRegion == false && isCharacterVisible)
  949. // {
  950. // isBeginRegion = true;
  951. // bl = textTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0));
  952. // tl = textTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0));
  953. // //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
  954. // // If Word is one character
  955. // if (wInfo.characterCount == 1)
  956. // {
  957. // isBeginRegion = false;
  958. // br = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  959. // tr = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  960. // // Check for Intersection
  961. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  962. // return i;
  963. // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  964. // }
  965. // }
  966. // // Last Character of Word
  967. // if (isBeginRegion && j == wInfo.characterCount - 1)
  968. // {
  969. // isBeginRegion = false;
  970. // br = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  971. // tr = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  972. // // Check for Intersection
  973. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  974. // return i;
  975. // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  976. // }
  977. // // If Word is split on more than one line.
  978. // else if (isBeginRegion && currentLine != text.textInfo.characterInfo[characterIndex + 1].lineNumber)
  979. // {
  980. // isBeginRegion = false;
  981. // br = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  982. // tr = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  983. // // Check for Intersection
  984. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  985. // return i;
  986. // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  987. // }
  988. // }
  989. // // Find the closest line segment to position.
  990. // float dbl = DistanceToLine(bl, tl, position);
  991. // float dtl = DistanceToLine(tl, tr, position);
  992. // float dtr = DistanceToLine(tr, br, position);
  993. // float dbr = DistanceToLine(br, bl, position);
  994. // float d = dbl < dtl ? dbl : dtl;
  995. // d = d < dtr ? d : dtr;
  996. // d = d < dbr ? d : dbr;
  997. // if (distanceSqr > d)
  998. // {
  999. // distanceSqr = d;
  1000. // closest = i;
  1001. // }
  1002. // //Debug.Log("Word at Index: " + i + " is located at (" + bl + ", " + tl + ", " + tr + ", " + br + ").");
  1003. // }
  1004. // return closest;
  1005. //}
  1006. /// <summary>
  1007. /// Function returning the line intersecting the position.
  1008. /// </summary>
  1009. /// <param name="textComponent"></param>
  1010. /// <param name="position"></param>
  1011. /// <param name="camera"></param>
  1012. /// <returns></returns>
  1013. public static int FindIntersectingLine(TMP_Text text, Vector3 position, Camera camera)
  1014. {
  1015. RectTransform rectTransform = text.rectTransform;
  1016. int closest = -1;
  1017. // Convert position into Worldspace coordinates
  1018. ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
  1019. for (int i = 0; i < text.textInfo.lineCount; i++)
  1020. {
  1021. TMP_LineInfo lineInfo = text.textInfo.lineInfo[i];
  1022. float ascender = rectTransform.TransformPoint(new Vector3(0, lineInfo.ascender, 0)).y;
  1023. float descender = rectTransform.TransformPoint(new Vector3(0, lineInfo.descender, 0)).y;
  1024. if (ascender > position.y && descender < position.y)
  1025. {
  1026. //Debug.Log("Position is on line " + i);
  1027. return i;
  1028. }
  1029. }
  1030. //Debug.Log("Closest line to position is " + closest);
  1031. return closest;
  1032. }
  1033. /// <summary>
  1034. /// Function returning the index of the Link at the given position (if any).
  1035. /// </summary>
  1036. /// <param name="text">A reference to the TMP_Text component.</param>
  1037. /// <param name="position">Position to check for intersection.</param>
  1038. /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
  1039. /// <returns></returns>
  1040. public static int FindIntersectingLink(TMP_Text text, Vector3 position, Camera camera)
  1041. {
  1042. Transform rectTransform = text.transform;
  1043. // Convert position into Worldspace coordinates
  1044. ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
  1045. for (int i = 0; i < text.textInfo.linkCount; i++)
  1046. {
  1047. TMP_LinkInfo linkInfo = text.textInfo.linkInfo[i];
  1048. bool isBeginRegion = false;
  1049. Vector3 bl = Vector3.zero;
  1050. Vector3 tl = Vector3.zero;
  1051. Vector3 br = Vector3.zero;
  1052. Vector3 tr = Vector3.zero;
  1053. // Iterate through each character of the word
  1054. for (int j = 0; j < linkInfo.linkTextLength; j++)
  1055. {
  1056. int characterIndex = linkInfo.linkTextfirstCharacterIndex + j;
  1057. TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[characterIndex];
  1058. int currentLine = currentCharInfo.lineNumber;
  1059. // Check if Link characters are on the current page
  1060. if (text.overflowMode == TextOverflowModes.Page && currentCharInfo.pageNumber + 1 != text.pageToDisplay) continue;
  1061. if (isBeginRegion == false)
  1062. {
  1063. isBeginRegion = true;
  1064. bl = rectTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0));
  1065. tl = rectTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0));
  1066. //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
  1067. // If Word is one character
  1068. if (linkInfo.linkTextLength == 1)
  1069. {
  1070. isBeginRegion = false;
  1071. br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  1072. tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  1073. // Check for Intersection
  1074. if (PointIntersectRectangle(position, bl, tl, tr, br))
  1075. return i;
  1076. //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  1077. }
  1078. }
  1079. // Last Character of Word
  1080. if (isBeginRegion && j == linkInfo.linkTextLength - 1)
  1081. {
  1082. isBeginRegion = false;
  1083. br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  1084. tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  1085. // Check for Intersection
  1086. if (PointIntersectRectangle(position, bl, tl, tr, br))
  1087. return i;
  1088. //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  1089. }
  1090. // If Word is split on more than one line.
  1091. else if (isBeginRegion && currentLine != text.textInfo.characterInfo[characterIndex + 1].lineNumber)
  1092. {
  1093. isBeginRegion = false;
  1094. br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  1095. tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  1096. // Check for Intersection
  1097. if (PointIntersectRectangle(position, bl, tl, tr, br))
  1098. return i;
  1099. //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  1100. }
  1101. }
  1102. //Debug.Log("Word at Index: " + i + " is located at (" + bl + ", " + tl + ", " + tr + ", " + br + ").");
  1103. }
  1104. return -1;
  1105. }
  1106. /// <summary>
  1107. /// Function returning the index of the Link at the given position (if any).
  1108. /// </summary>
  1109. /// <param name="text">A reference to the TextMeshPro UGUI component.</param>
  1110. /// <param name="position">Position to check for intersection.</param>
  1111. /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
  1112. /// <returns></returns>
  1113. //public static int FindIntersectingLink(TextMeshProUGUI text, Vector3 position, Camera camera)
  1114. //{
  1115. // Transform rectTransform = text.transform;
  1116. // // Convert position into Worldspace coordinates
  1117. // ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
  1118. // for (int i = 0; i < text.textInfo.linkCount; i++)
  1119. // {
  1120. // TMP_LinkInfo linkInfo = text.textInfo.linkInfo[i];
  1121. // bool isBeginRegion = false;
  1122. // Vector3 bl = Vector3.zero;
  1123. // Vector3 tl = Vector3.zero;
  1124. // Vector3 br = Vector3.zero;
  1125. // Vector3 tr = Vector3.zero;
  1126. // // Iterate through each character of the word
  1127. // for (int j = 0; j < linkInfo.linkTextLength; j++)
  1128. // {
  1129. // int characterIndex = linkInfo.linkTextfirstCharacterIndex + j;
  1130. // TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[characterIndex];
  1131. // int currentLine = currentCharInfo.lineNumber;
  1132. // // Check if Link characters are on the current page
  1133. // if (text.OverflowMode == TextOverflowModes.Page && currentCharInfo.pageNumber + 1 != text.pageToDisplay) continue;
  1134. // if (isBeginRegion == false)
  1135. // {
  1136. // isBeginRegion = true;
  1137. // bl = rectTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0));
  1138. // tl = rectTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0));
  1139. // //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
  1140. // // If Word is one character
  1141. // if (linkInfo.linkTextLength == 1)
  1142. // {
  1143. // isBeginRegion = false;
  1144. // br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  1145. // tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  1146. // // Check for Intersection
  1147. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  1148. // return i;
  1149. // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  1150. // }
  1151. // }
  1152. // // Last Character of Word
  1153. // if (isBeginRegion && j == linkInfo.linkTextLength - 1)
  1154. // {
  1155. // isBeginRegion = false;
  1156. // br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  1157. // tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  1158. // // Check for Intersection
  1159. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  1160. // return i;
  1161. // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  1162. // }
  1163. // // If Word is split on more than one line.
  1164. // else if (isBeginRegion && currentLine != text.textInfo.characterInfo[characterIndex + 1].lineNumber)
  1165. // {
  1166. // isBeginRegion = false;
  1167. // br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  1168. // tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  1169. // // Check for Intersection
  1170. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  1171. // return i;
  1172. // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  1173. // }
  1174. // }
  1175. // //Debug.Log("Word at Index: " + i + " is located at (" + bl + ", " + tl + ", " + tr + ", " + br + ").");
  1176. // }
  1177. // return -1;
  1178. //}
  1179. /// <summary>
  1180. /// Function returning the index of the Link at the given position (if any).
  1181. /// </summary>
  1182. /// <param name="text">A reference to the TextMeshPro component.</param>
  1183. /// <param name="position">Position to check for intersection.</param>
  1184. /// <param name="camera">The camera which is rendering the text object.</param>
  1185. /// <returns></returns>
  1186. //public static int FindIntersectingLink(TextMeshPro text, Vector3 position, Camera camera)
  1187. //{
  1188. // Transform textTransform = text.transform;
  1189. // // Convert position into Worldspace coordinates
  1190. // ScreenPointToWorldPointInRectangle(textTransform, position, camera, out position);
  1191. // for (int i = 0; i < text.textInfo.linkCount; i++)
  1192. // {
  1193. // TMP_LinkInfo linkInfo = text.textInfo.linkInfo[i];
  1194. // bool isBeginRegion = false;
  1195. // Vector3 bl = Vector3.zero;
  1196. // Vector3 tl = Vector3.zero;
  1197. // Vector3 br = Vector3.zero;
  1198. // Vector3 tr = Vector3.zero;
  1199. // // Iterate through each character of the word
  1200. // for (int j = 0; j < linkInfo.linkTextLength; j++)
  1201. // {
  1202. // int characterIndex = linkInfo.linkTextfirstCharacterIndex + j;
  1203. // TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[characterIndex];
  1204. // int currentLine = currentCharInfo.lineNumber;
  1205. // // Check if Link characters are on the current page
  1206. // if (text.OverflowMode == TextOverflowModes.Page && currentCharInfo.pageNumber + 1 != text.pageToDisplay) continue;
  1207. // if (isBeginRegion == false)
  1208. // {
  1209. // isBeginRegion = true;
  1210. // bl = textTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0));
  1211. // tl = textTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0));
  1212. // //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
  1213. // // If Word is one character
  1214. // if (linkInfo.linkTextLength == 1)
  1215. // {
  1216. // isBeginRegion = false;
  1217. // br = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  1218. // tr = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  1219. // // Check for Intersection
  1220. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  1221. // return i;
  1222. // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  1223. // }
  1224. // }
  1225. // // Last Character of Word
  1226. // if (isBeginRegion && j == linkInfo.linkTextLength - 1)
  1227. // {
  1228. // isBeginRegion = false;
  1229. // br = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  1230. // tr = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  1231. // // Check for Intersection
  1232. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  1233. // return i;
  1234. // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  1235. // }
  1236. // // If Word is split on more than one line.
  1237. // else if (isBeginRegion && currentLine != text.textInfo.characterInfo[characterIndex + 1].lineNumber)
  1238. // {
  1239. // isBeginRegion = false;
  1240. // br = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  1241. // tr = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  1242. // // Check for Intersection
  1243. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  1244. // return i;
  1245. // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  1246. // }
  1247. // }
  1248. // //Debug.Log("Word at Index: " + i + " is located at (" + bl + ", " + tl + ", " + tr + ", " + br + ").");
  1249. // }
  1250. // return -1;
  1251. //}
  1252. /// <summary>
  1253. /// Function returning the index of the word at the given position (if any).
  1254. /// </summary>
  1255. /// <param name="text">A reference to the TMP_Text component.</param>
  1256. /// <param name="position">Position to check for intersection.</param>
  1257. /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
  1258. /// <returns></returns>
  1259. public static int FindNearestLink(TMP_Text text, Vector3 position, Camera camera)
  1260. {
  1261. RectTransform rectTransform = text.rectTransform;
  1262. // Convert position into Worldspace coordinates
  1263. ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
  1264. float distanceSqr = Mathf.Infinity;
  1265. int closest = 0;
  1266. for (int i = 0; i < text.textInfo.linkCount; i++)
  1267. {
  1268. TMP_LinkInfo linkInfo = text.textInfo.linkInfo[i];
  1269. bool isBeginRegion = false;
  1270. Vector3 bl = Vector3.zero;
  1271. Vector3 tl = Vector3.zero;
  1272. Vector3 br = Vector3.zero;
  1273. Vector3 tr = Vector3.zero;
  1274. // Iterate through each character of the link
  1275. for (int j = 0; j < linkInfo.linkTextLength; j++)
  1276. {
  1277. int characterIndex = linkInfo.linkTextfirstCharacterIndex + j;
  1278. TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[characterIndex];
  1279. int currentLine = currentCharInfo.lineNumber;
  1280. // Check if Link characters are on the current page
  1281. if (text.overflowMode == TextOverflowModes.Page && currentCharInfo.pageNumber + 1 != text.pageToDisplay) continue;
  1282. if (isBeginRegion == false)
  1283. {
  1284. isBeginRegion = true;
  1285. //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
  1286. bl = rectTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0));
  1287. tl = rectTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0));
  1288. // If Link is one character
  1289. if (linkInfo.linkTextLength == 1)
  1290. {
  1291. isBeginRegion = false;
  1292. br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  1293. tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  1294. // Check for Intersection
  1295. if (PointIntersectRectangle(position, bl, tl, tr, br))
  1296. return i;
  1297. // Find the closest line segment to position.
  1298. float dbl = DistanceToLine(bl, tl, position);
  1299. float dtl = DistanceToLine(tl, tr, position);
  1300. float dtr = DistanceToLine(tr, br, position);
  1301. float dbr = DistanceToLine(br, bl, position);
  1302. float d = dbl < dtl ? dbl : dtl;
  1303. d = d < dtr ? d : dtr;
  1304. d = d < dbr ? d : dbr;
  1305. if (distanceSqr > d)
  1306. {
  1307. distanceSqr = d;
  1308. closest = i;
  1309. }
  1310. }
  1311. }
  1312. // Last Character of Word
  1313. if (isBeginRegion && j == linkInfo.linkTextLength - 1)
  1314. {
  1315. isBeginRegion = false;
  1316. br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  1317. tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  1318. // Check for Intersection
  1319. if (PointIntersectRectangle(position, bl, tl, tr, br))
  1320. return i;
  1321. // Find the closest line segment to position.
  1322. float dbl = DistanceToLine(bl, tl, position);
  1323. float dtl = DistanceToLine(tl, tr, position);
  1324. float dtr = DistanceToLine(tr, br, position);
  1325. float dbr = DistanceToLine(br, bl, position);
  1326. float d = dbl < dtl ? dbl : dtl;
  1327. d = d < dtr ? d : dtr;
  1328. d = d < dbr ? d : dbr;
  1329. if (distanceSqr > d)
  1330. {
  1331. distanceSqr = d;
  1332. closest = i;
  1333. }
  1334. }
  1335. // If Link is split on more than one line.
  1336. else if (isBeginRegion && currentLine != text.textInfo.characterInfo[characterIndex + 1].lineNumber)
  1337. {
  1338. isBeginRegion = false;
  1339. br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  1340. tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  1341. // Check for Intersection
  1342. if (PointIntersectRectangle(position, bl, tl, tr, br))
  1343. return i;
  1344. // Find the closest line segment to position.
  1345. float dbl = DistanceToLine(bl, tl, position);
  1346. float dtl = DistanceToLine(tl, tr, position);
  1347. float dtr = DistanceToLine(tr, br, position);
  1348. float dbr = DistanceToLine(br, bl, position);
  1349. float d = dbl < dtl ? dbl : dtl;
  1350. d = d < dtr ? d : dtr;
  1351. d = d < dbr ? d : dbr;
  1352. if (distanceSqr > d)
  1353. {
  1354. distanceSqr = d;
  1355. closest = i;
  1356. }
  1357. }
  1358. }
  1359. //Debug.Log("Word at Index: " + i + " is located at (" + bl + ", " + tl + ", " + tr + ", " + br + ").");
  1360. }
  1361. return closest;
  1362. }
  1363. /// <summary>
  1364. /// Function returning the index of the word at the given position (if any).
  1365. /// </summary>
  1366. /// <param name="text">A reference to the TextMeshPro UGUI component.</param>
  1367. /// <param name="position">Position to check for intersection.</param>
  1368. /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
  1369. /// <returns></returns>
  1370. //public static int FindNearestLink(TextMeshProUGUI text, Vector3 position, Camera camera)
  1371. //{
  1372. // RectTransform rectTransform = text.rectTransform;
  1373. // // Convert position into Worldspace coordinates
  1374. // ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
  1375. // float distanceSqr = Mathf.Infinity;
  1376. // int closest = 0;
  1377. // for (int i = 0; i < text.textInfo.linkCount; i++)
  1378. // {
  1379. // TMP_LinkInfo linkInfo = text.textInfo.linkInfo[i];
  1380. // bool isBeginRegion = false;
  1381. // Vector3 bl = Vector3.zero;
  1382. // Vector3 tl = Vector3.zero;
  1383. // Vector3 br = Vector3.zero;
  1384. // Vector3 tr = Vector3.zero;
  1385. // // Iterate through each character of the word
  1386. // for (int j = 0; j < linkInfo.linkTextLength; j++)
  1387. // {
  1388. // int characterIndex = linkInfo.linkTextfirstCharacterIndex + j;
  1389. // TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[characterIndex];
  1390. // int currentLine = currentCharInfo.lineNumber;
  1391. // if (isBeginRegion == false)
  1392. // {
  1393. // isBeginRegion = true;
  1394. // bl = rectTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0));
  1395. // tl = rectTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0));
  1396. // //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
  1397. // // If Word is one character
  1398. // if (linkInfo.linkTextLength == 1)
  1399. // {
  1400. // isBeginRegion = false;
  1401. // br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  1402. // tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  1403. // // Check for Intersection
  1404. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  1405. // return i;
  1406. // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  1407. // }
  1408. // }
  1409. // // Last Character of Word
  1410. // if (isBeginRegion && j == linkInfo.linkTextLength - 1)
  1411. // {
  1412. // isBeginRegion = false;
  1413. // br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  1414. // tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  1415. // // Check for Intersection
  1416. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  1417. // return i;
  1418. // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  1419. // }
  1420. // // If Word is split on more than one line.
  1421. // else if (isBeginRegion && currentLine != text.textInfo.characterInfo[characterIndex + 1].lineNumber)
  1422. // {
  1423. // isBeginRegion = false;
  1424. // br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  1425. // tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  1426. // // Check for Intersection
  1427. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  1428. // return i;
  1429. // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  1430. // }
  1431. // }
  1432. // // Find the closest line segment to position.
  1433. // float dbl = DistanceToLine(bl, tl, position); // (position - bl).sqrMagnitude;
  1434. // float dtl = DistanceToLine(tl, tr, position); // (position - tl).sqrMagnitude;
  1435. // float dtr = DistanceToLine(tr, br, position); // (position - tr).sqrMagnitude;
  1436. // float dbr = DistanceToLine(br, bl, position); // (position - br).sqrMagnitude;
  1437. // float d = dbl < dtl ? dbl : dtl;
  1438. // d = d < dtr ? d : dtr;
  1439. // d = d < dbr ? d : dbr;
  1440. // if (distanceSqr > d)
  1441. // {
  1442. // distanceSqr = d;
  1443. // closest = i;
  1444. // }
  1445. // //Debug.Log("Word at Index: " + i + " is located at (" + bl + ", " + tl + ", " + tr + ", " + br + ").");
  1446. // }
  1447. // return closest;
  1448. //}
  1449. /// <summary>
  1450. /// Function returning the index of the word at the given position (if any).
  1451. /// </summary>
  1452. /// <param name="text">A reference to the TextMeshPro component.</param>
  1453. /// <param name="position">Position to check for intersection.</param>
  1454. /// <param name="camera">The camera which is rendering the text object.</param>
  1455. /// <returns></returns>
  1456. //public static int FindNearestLink(TextMeshPro text, Vector3 position, Camera camera)
  1457. //{
  1458. // Transform textTransform = text.transform;
  1459. // // Convert position into Worldspace coordinates
  1460. // ScreenPointToWorldPointInRectangle(textTransform, position, camera, out position);
  1461. // float distanceSqr = Mathf.Infinity;
  1462. // int closest = 0;
  1463. // for (int i = 0; i < text.textInfo.linkCount; i++)
  1464. // {
  1465. // TMP_LinkInfo linkInfo = text.textInfo.linkInfo[i];
  1466. // bool isBeginRegion = false;
  1467. // Vector3 bl = Vector3.zero;
  1468. // Vector3 tl = Vector3.zero;
  1469. // Vector3 br = Vector3.zero;
  1470. // Vector3 tr = Vector3.zero;
  1471. // // Iterate through each character of the word
  1472. // for (int j = 0; j < linkInfo.linkTextLength; j++)
  1473. // {
  1474. // int characterIndex = linkInfo.linkTextfirstCharacterIndex + j;
  1475. // TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[characterIndex];
  1476. // int currentLine = currentCharInfo.lineNumber;
  1477. // if (isBeginRegion == false)
  1478. // {
  1479. // isBeginRegion = true;
  1480. // bl = textTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0));
  1481. // tl = textTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0));
  1482. // //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
  1483. // // If Word is one character
  1484. // if (linkInfo.linkTextLength == 1)
  1485. // {
  1486. // isBeginRegion = false;
  1487. // br = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  1488. // tr = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  1489. // // Check for Intersection
  1490. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  1491. // return i;
  1492. // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  1493. // }
  1494. // }
  1495. // // Last Character of Word
  1496. // if (isBeginRegion && j == linkInfo.linkTextLength - 1)
  1497. // {
  1498. // isBeginRegion = false;
  1499. // br = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  1500. // tr = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  1501. // // Check for Intersection
  1502. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  1503. // return i;
  1504. // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  1505. // }
  1506. // // If Word is split on more than one line.
  1507. // else if (isBeginRegion && currentLine != text.textInfo.characterInfo[characterIndex + 1].lineNumber)
  1508. // {
  1509. // isBeginRegion = false;
  1510. // br = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
  1511. // tr = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
  1512. // // Check for Intersection
  1513. // if (PointIntersectRectangle(position, bl, tl, tr, br))
  1514. // return i;
  1515. // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  1516. // }
  1517. // }
  1518. // // Find the closest line segment to position.
  1519. // float dbl = DistanceToLine(bl, tl, position);
  1520. // float dtl = DistanceToLine(tl, tr, position);
  1521. // float dtr = DistanceToLine(tr, br, position);
  1522. // float dbr = DistanceToLine(br, bl, position);
  1523. // float d = dbl < dtl ? dbl : dtl;
  1524. // d = d < dtr ? d : dtr;
  1525. // d = d < dbr ? d : dbr;
  1526. // if (distanceSqr > d)
  1527. // {
  1528. // distanceSqr = d;
  1529. // closest = i;
  1530. // }
  1531. // //Debug.Log("Word at Index: " + i + " is located at (" + bl + ", " + tl + ", " + tr + ", " + br + ").");
  1532. // }
  1533. // return closest;
  1534. //}
  1535. /// <summary>
  1536. /// Function to check if a Point is contained within a Rectangle.
  1537. /// </summary>
  1538. /// <param name="m"></param>
  1539. /// <param name="a"></param>
  1540. /// <param name="b"></param>
  1541. /// <param name="c"></param>
  1542. /// <param name="d"></param>
  1543. /// <returns></returns>
  1544. private static bool PointIntersectRectangle(Vector3 m, Vector3 a, Vector3 b, Vector3 c, Vector3 d)
  1545. {
  1546. Vector3 ab = b - a;
  1547. Vector3 am = m - a;
  1548. Vector3 bc = c - b;
  1549. Vector3 bm = m - b;
  1550. float abamDot = Vector3.Dot(ab, am);
  1551. float bcbmDot = Vector3.Dot(bc, bm);
  1552. return 0 <= abamDot && abamDot <= Vector3.Dot(ab, ab) && 0 <= bcbmDot && bcbmDot <= Vector3.Dot(bc, bc);
  1553. }
  1554. /// <summary>
  1555. /// Method to convert ScreenPoint to WorldPoint aligned with Rectangle
  1556. /// </summary>
  1557. /// <param name="transform"></param>
  1558. /// <param name="screenPoint"></param>
  1559. /// <param name="cam"></param>
  1560. /// <param name="worldPoint"></param>
  1561. /// <returns></returns>
  1562. public static bool ScreenPointToWorldPointInRectangle(Transform transform, Vector2 screenPoint, Camera cam, out Vector3 worldPoint)
  1563. {
  1564. worldPoint = (Vector3)Vector2.zero;
  1565. Ray ray = RectTransformUtility.ScreenPointToRay(cam, screenPoint);
  1566. float enter;
  1567. if (!new Plane(transform.rotation * Vector3.back, transform.position).Raycast(ray, out enter))
  1568. return false;
  1569. worldPoint = ray.GetPoint(enter);
  1570. return true;
  1571. }
  1572. private struct LineSegment
  1573. {
  1574. public Vector3 Point1;
  1575. public Vector3 Point2;
  1576. public LineSegment(Vector3 p1, Vector3 p2)
  1577. {
  1578. Point1 = p1;
  1579. Point2 = p2;
  1580. }
  1581. }
  1582. /// <summary>
  1583. /// Function returning the point of intersection between a line and a plane.
  1584. /// </summary>
  1585. /// <param name="line"></param>
  1586. /// <param name="point"></param>
  1587. /// <param name="normal"></param>
  1588. /// <param name="intersectingPoint"></param>
  1589. /// <returns></returns>
  1590. private static bool IntersectLinePlane(LineSegment line, Vector3 point, Vector3 normal, out Vector3 intersectingPoint)
  1591. {
  1592. intersectingPoint = Vector3.zero;
  1593. Vector3 u = line.Point2 - line.Point1;
  1594. Vector3 w = line.Point1 - point;
  1595. float D = Vector3.Dot(normal, u);
  1596. float N = -Vector3.Dot(normal, w);
  1597. if (Mathf.Abs(D) < Mathf.Epsilon) // if line is parallel & co-planar to plane
  1598. {
  1599. if (N == 0)
  1600. return true;
  1601. else
  1602. return false;
  1603. }
  1604. float sI = N / D;
  1605. if (sI < 0 || sI > 1) // Line parallel to plane
  1606. return false;
  1607. intersectingPoint = line.Point1 + sI * u;
  1608. return true;
  1609. }
  1610. /// <summary>
  1611. /// Function returning the Square Distance from a Point to a Line.
  1612. /// </summary>
  1613. /// <param name="a"></param>
  1614. /// <param name="b"></param>
  1615. /// <param name="point"></param>
  1616. /// <returns></returns>
  1617. public static float DistanceToLine(Vector3 a, Vector3 b, Vector3 point)
  1618. {
  1619. Vector3 n = b - a;
  1620. Vector3 pa = a - point;
  1621. float c = Vector3.Dot( n, pa );
  1622. // Closest point is a
  1623. if ( c > 0.0f )
  1624. return Vector3.Dot( pa, pa );
  1625. Vector3 bp = point - b;
  1626. // Closest point is b
  1627. if (Vector3.Dot( n, bp ) > 0.0f )
  1628. return Vector3.Dot( bp, bp );
  1629. // Closest point is between a and b
  1630. Vector3 e = pa - n * (c / Vector3.Dot( n, n ));
  1631. return Vector3.Dot( e, e );
  1632. }
  1633. /// <summary>
  1634. /// Function returning the Square Distance from a Point to a Line and Direction.
  1635. /// </summary>
  1636. /// <param name="a"></param>
  1637. /// <param name="b"></param>
  1638. /// <param name="point"></param>
  1639. /// <param name="direction">-1 left, 0 in between, 1 right</param>
  1640. /// <returns></returns>
  1641. //public static float DistanceToLineDirectional(Vector3 a, Vector3 b, Vector3 point, ref int direction)
  1642. //{
  1643. // Vector3 n = b - a;
  1644. // Vector3 pa = a - point;
  1645. // float c = Vector3.Dot(n, pa);
  1646. // direction = -1;
  1647. // // Closest point is a
  1648. // if (c > 0.0f)
  1649. // return Vector3.Dot(pa, pa);
  1650. // Vector3 bp = point - b;
  1651. // direction = 1;
  1652. // // Closest point is b
  1653. // if (Vector3.Dot(n, bp) > 0.0f)
  1654. // return Vector3.Dot(bp, bp);
  1655. // // Closest point is between a and b
  1656. // Vector3 e = pa - n * (c / Vector3.Dot(n, n));
  1657. // direction = 0;
  1658. // return Vector3.Dot(e, e);
  1659. //}
  1660. /// <summary>
  1661. /// Table used to convert character to lowercase.
  1662. /// </summary>
  1663. const string k_lookupStringL = "-------------------------------- !-#$%&-()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[-]^_`abcdefghijklmnopqrstuvwxyz{|}~-";
  1664. /// <summary>
  1665. /// Table used to convert character to uppercase.
  1666. /// </summary>
  1667. const string k_lookupStringU = "-------------------------------- !-#$%&-()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[-]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~-";
  1668. /// <summary>
  1669. /// Get lowercase version of this ASCII character.
  1670. /// </summary>
  1671. public static char ToLowerFast(char c)
  1672. {
  1673. if (c > k_lookupStringL.Length - 1)
  1674. return c;
  1675. return k_lookupStringL[c];
  1676. }
  1677. /// <summary>
  1678. /// Get uppercase version of this ASCII character.
  1679. /// </summary>
  1680. public static char ToUpperFast(char c)
  1681. {
  1682. if (c > k_lookupStringU.Length - 1)
  1683. return c;
  1684. return k_lookupStringU[c];
  1685. }
  1686. /// <summary>
  1687. /// Function which returns a simple hashcode from a string.
  1688. /// </summary>
  1689. /// <returns></returns>
  1690. public static int GetSimpleHashCode(string s)
  1691. {
  1692. int hashCode = 0; // 5381;
  1693. for (int i = 0; i < s.Length; i++)
  1694. hashCode = (hashCode << 5) + hashCode ^ s[i];
  1695. return hashCode;
  1696. }
  1697. /// <summary>
  1698. /// Function which returns a simple hashcode from a string converted to lowercase.
  1699. /// </summary>
  1700. /// <returns></returns>
  1701. public static uint GetSimpleHashCodeLowercase(string s)
  1702. {
  1703. uint hashCode = 5381;
  1704. for (int i = 0; i < s.Length; i++)
  1705. hashCode = (hashCode << 5) + hashCode ^ ToLowerFast(s[i]);
  1706. return hashCode;
  1707. }
  1708. /// <summary>
  1709. /// Function to convert Hex to Int
  1710. /// </summary>
  1711. /// <param name="hex"></param>
  1712. /// <returns></returns>
  1713. public static int HexToInt(char hex)
  1714. {
  1715. switch (hex)
  1716. {
  1717. case '0': return 0;
  1718. case '1': return 1;
  1719. case '2': return 2;
  1720. case '3': return 3;
  1721. case '4': return 4;
  1722. case '5': return 5;
  1723. case '6': return 6;
  1724. case '7': return 7;
  1725. case '8': return 8;
  1726. case '9': return 9;
  1727. case 'A': return 10;
  1728. case 'B': return 11;
  1729. case 'C': return 12;
  1730. case 'D': return 13;
  1731. case 'E': return 14;
  1732. case 'F': return 15;
  1733. case 'a': return 10;
  1734. case 'b': return 11;
  1735. case 'c': return 12;
  1736. case 'd': return 13;
  1737. case 'e': return 14;
  1738. case 'f': return 15;
  1739. }
  1740. return 15;
  1741. }
  1742. /// <summary>
  1743. /// Function to convert a properly formatted string which contains an hex value to its decimal value.
  1744. /// </summary>
  1745. /// <param name="s"></param>
  1746. /// <returns></returns>
  1747. public static int StringToInt(string s)
  1748. {
  1749. int value = 0;
  1750. for (int i = 0; i < s.Length; i++)
  1751. {
  1752. value += HexToInt(s[i]) * (int)Mathf.Pow(16, (s.Length - 1) - i);
  1753. }
  1754. return value;
  1755. }
  1756. }
  1757. }