// // ©2015 - 2025 Candy Smith // // All rights reserved // // Redistribution of this software is strictly not allowed. // // Copy of this software can be obtained from unity asset store only. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE // // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // // THE SOFTWARE. using UnityEngine; using UnityEngine.UI; namespace WordsToolkit.Scripts.GUI { [RequireComponent(typeof(CanvasRenderer))] public class UILineRenderer : MaskableGraphic { public Vector2[] _points; public Vector2[] points { get => _points; set { _points = value; SetVerticesDirty(); // Tell Unity to redraw the UI component } } public float thickness = 10f; public bool center = true; protected override void OnPopulateMesh(VertexHelper vh) { vh.Clear(); if (_points == null || _points.Length < 2) return; for (int i = 0; i < _points.Length-1; i++) { // Create a line segment between the next two points CreateLineSegment(_points[i], _points[i+1], vh); int index = i * 5; // Add the line segment to the triangles array vh.AddTriangle(index, index+1, index+3); vh.AddTriangle(index+3, index+2, index); // These two triangles create the beveled edges // between line segments using the end point of // the last line segment and the start points of this one if (i != 0) { vh.AddTriangle(index, index-1, index-3); vh.AddTriangle(index+1, index-1, index-2); } } } /// /// Creates a rect from two points that acts as a line segment /// /// The starting point of the segment /// The endint point of the segment /// The vertex helper that the segment is added to private void CreateLineSegment(Vector3 point1, Vector3 point2, VertexHelper vh) { Vector3 offset = center ? (rectTransform.sizeDelta / 2) : Vector2.zero; // Create vertex template UIVertex vertex = UIVertex.simpleVert; vertex.color = color; // Create the start of the segment Quaternion point1Rotation = Quaternion.Euler(0, 0, RotatePointTowards(point1, point2) + 90); vertex.position = point1Rotation * new Vector3(-thickness / 2, 0); vertex.position += point1 - offset; vh.AddVert(vertex); vertex.position = point1Rotation * new Vector3(thickness / 2, 0); vertex.position += point1 - offset; vh.AddVert(vertex); // Create the end of the segment Quaternion point2Rotation = Quaternion.Euler(0, 0, RotatePointTowards(point2, point1) - 90); vertex.position = point2Rotation * new Vector3(-thickness / 2, 0); vertex.position += point2 - offset; vh.AddVert(vertex); vertex.position = point2Rotation * new Vector3(thickness / 2, 0); vertex.position += point2 - offset; vh.AddVert(vertex); // Also add the end point vertex.position = point2 - offset; vh.AddVert(vertex); } /// /// Gets the angle that a vertex needs to rotate to face target vertex /// /// The vertex being rotated /// The vertex to rotate towards /// The angle required to rotate vertex towards target private float RotatePointTowards(Vector2 vertex, Vector2 target) { return (float)(Mathf.Atan2(target.y - vertex.y, target.x - vertex.x) * (180 / Mathf.PI)); } } }