using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Serialization; // Shapes © Freya Holmér - https://twitter.com/FreyaHolmer/ // Website & Documentation - https://acegikmo.com/shapes/ namespace Shapes { /// A Polyline shape component [ExecuteAlways] [AddComponentMenu( "Shapes/Polyline" )] public class Polyline : ShapeRenderer { #if UNITY_EDITOR /// "Please use points instead of PolyPoints - this one is deprecated" [Obsolete( "Please use " + nameof(points) + " instead - this one is deprecated", error: true )] public List PolyPoints => points; #endif /// IMPORTANT: if you modify this list, you need to set meshOutOfDate to true, otherwise your changes won't apply [FormerlySerializedAs( "polyPoints" )] [SerializeField] public List points = new List() { new PolylinePoint( new Vector3( 0, 1, 0 ), Color.white ), new PolylinePoint( new Vector3( 0.86602540378f, -.5f, 0 ), Color.white ), new PolylinePoint( new Vector3( -0.86602540378f, -.5f, 0 ), Color.white ) }; // also called alignment [SerializeField] PolylineGeometry geometry = PolylineGeometry.Flat2D; /// Get or set the geometry type to use for this polyline public PolylineGeometry Geometry { get => geometry; set { geometry = value; SetIntNow( ShapesMaterialUtils.propAlignment, (int)geometry ); UpdateMaterial(); ApplyProperties(); } } [SerializeField] PolylineJoins joins = PolylineJoins.Miter; /// The type of joins to use in all corners of this polyline public PolylineJoins Joins { get => joins; set { joins = value; meshOutOfDate = true; UpdateMaterial(); } } [SerializeField] bool closed = true; /// Whether or not this polyline should form a closed loop public bool Closed { get => closed; set { closed = value; meshOutOfDate = true; } } [SerializeField] float thickness = 0.125f; /// The thickness of this polyline in the given thickness space public float Thickness { get => thickness; set => SetFloatNow( ShapesMaterialUtils.propThickness, thickness = value ); } [SerializeField] ThicknessSpace thicknessSpace = Shapes.ThicknessSpace.Meters; /// The space in which Thickness is defined public ThicknessSpace ThicknessSpace { get => thicknessSpace; set => SetIntNow( ShapesMaterialUtils.propThicknessSpace, (int)( thicknessSpace = value ) ); } /// The number of points in this polyline public int Count => points.Count; /// Get or set a polyline point by index public PolylinePoint this[ int i ] { get => points[i]; set { points[i] = value; meshOutOfDate = true; } } /// Set a polygon point position by index public void SetPointPosition( int index, Vector3 position ) { if( index < 0 || index >= Count ) throw new IndexOutOfRangeException(); PolylinePoint pp = points[index]; pp.point = position; points[index] = pp; meshOutOfDate = true; } /// Set a polygon point color by index public void SetPointColor( int index, Color color ) { if( index < 0 || index >= Count ) throw new IndexOutOfRangeException(); PolylinePoint pp = points[index]; pp.color = color; points[index] = pp; meshOutOfDate = true; } /// Set a polygon point thickness by index public void SetPointThickness( int index, float thickness ) { if( index < 0 || index >= Count ) throw new IndexOutOfRangeException(); PolylinePoint pp = points[index]; pp.thickness = thickness; points[index] = pp; meshOutOfDate = true; } /// Sets all points and their corresponding colors for this polyline public void SetPoints( IReadOnlyCollection points, IReadOnlyCollection colors = null ) { this.points.Clear(); if( colors == null ) { AddPoints( points.Select( p => new PolylinePoint( p, Color.white ) ) ); } else { if( points.Count != colors.Count ) throw new ArgumentException( "point.Count != color.Count" ); AddPoints( points.Zip( colors, ( p, c ) => new PolylinePoint( p, c ) ) ); } } /// Sets all points and their corresponding colors for this polyline public void SetPoints( IReadOnlyCollection points, IReadOnlyCollection colors = null ) { this.points.Clear(); if( colors == null ) { AddPoints( points.Select( p => new PolylinePoint( p, Color.white ) ) ); } else { if( points.Count != colors.Count ) throw new ArgumentException( "point.Count != color.Count" ); AddPoints( points.Zip( colors, ( p, c ) => new PolylinePoint( p, c ) ) ); } } /// Sets all points of this polyline public void SetPoints( IEnumerable points ) { this.points.Clear(); AddPoints( points ); } /// Adds a set of points to this polyline public void AddPoints( IEnumerable points ) { this.points.AddRange( points ); meshOutOfDate = true; } /// Adds a point to this polyline public void AddPoint( Vector3 position ) => AddPoint( new PolylinePoint( position ) ); /// Adds a point to this polyline public void AddPoint( Vector3 position, Color color ) => AddPoint( new PolylinePoint( position, color ) ); /// Adds a point to this polyline public void AddPoint( Vector3 position, Color color, float thickness ) => AddPoint( new PolylinePoint( position, color, thickness ) ); /// Adds a point to this polyline public void AddPoint( Vector3 position, float thickness ) => AddPoint( new PolylinePoint( position, Color.white, thickness ) ); /// Adds a point to this polyline public void AddPoint( PolylinePoint point ) { points.Add( point ); meshOutOfDate = true; } private protected override bool UseCamOnPreCull => true; internal override void CamOnPreCull() { if( meshOutOfDate ) { meshOutOfDate = false; UpdateMesh( force: true ); } } private protected override MeshUpdateMode MeshUpdateMode => MeshUpdateMode.SelfGenerated; private protected override void GenerateMesh() => ShapesMeshGen.GenPolylineMesh( Mesh, points, closed, joins, flattenZ: geometry == PolylineGeometry.Flat2D, useColors: true ); private protected override void SetAllMaterialProperties() { SetFloat( ShapesMaterialUtils.propThickness, thickness ); SetInt( ShapesMaterialUtils.propThicknessSpace, (int)thicknessSpace ); SetInt( ShapesMaterialUtils.propAlignment, (int)geometry ); } private protected override void ShapeClampRanges() => thickness = Mathf.Max( 0f, thickness ); private protected override Material[] GetMaterials() { if( joins.HasJoinMesh() ) return new[] { ShapesMaterialUtils.GetPolylineMat( joins )[BlendMode], ShapesMaterialUtils.GetPolylineJoinsMat( joins )[BlendMode] }; return new[] { ShapesMaterialUtils.GetPolylineMat( joins )[BlendMode] }; } // todo: this doesn't take point thickness or thickness space into account private protected override Bounds GetBounds_Internal() { if( points.Count < 2 ) return default; Vector3 min = Vector3.one * float.MaxValue; Vector3 max = Vector3.one * float.MinValue; foreach( Vector3 pt in points.Select( p => p.point ) ) { min = Vector3.Min( min, pt ); max = Vector3.Max( max, pt ); } if( geometry == PolylineGeometry.Flat2D ) min.z = max.z = 0; return new Bounds( ( max + min ) * 0.5f, ( max - min ) + Vector3.one * ( thickness * 0.5f ) ); } } }