mvaganov

Lines.cs

Jul 15th, 2014 (edited)
632
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #define PULSE_COLOR
  2. using System;
  3. using UnityEngine;
  4. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. using Debug = UnityEngine.Debug;
  7.  
  8. // author: mvaganov@hotmail.com
  9. // license: Copyfree, public domain. This is free code! Great artists, steal this code!
  10. // latest version at: https://pastebin.com/raw/8m69iTut -- last updated (2020/09/27)
  11. namespace NS {
  12.     /// <summary>static functions for Unity's LineRenderer. Creates visualizations for 3D Vector math.
  13.     /// This library isn't optimized for performance, it's built to make math less invisible, even at compiled runtime.
  14.     /// </summary>
  15.     public class Lines : MonoBehaviour {
  16.         public bool autoParentLinesToGlobalObject = true;
  17.  
  18.         /// <summary>the dictionary of named lines. This structure allows Lines to create new lines without needing explicit variables</summary>
  19.         private static readonly Dictionary<string, GameObject> NamedObject = new Dictionary<string, GameObject>();
  20.         /// <summary>The singleton instance.</summary>
  21.         private static Lines _instance;
  22.  
  23.         /// <summary>
  24.         /// how close two floating point values need to be before they are considered equal in this library
  25.         /// </summary>
  26.         public const float TOLERANCE = 1f / (1 << 23); // one sixteen-millionth
  27.        
  28.         public const float ARROW_SIZE = 3, LINE_SIZE = 1f / 8, SAME_AS_START_SIZE = -1;
  29.  
  30.         public static Material _defaultMaterial;
  31.         public static Material DefaultMaterial {
  32.             get {
  33.                 if (_defaultMaterial != null) return _defaultMaterial;
  34.                 GameObject primitive = GameObject.CreatePrimitive(PrimitiveType.Plane);
  35.                 _defaultMaterial = primitive.GetComponent<MeshRenderer>().sharedMaterial;
  36.                 DestroyImmediate(primitive);
  37.                 return _defaultMaterial;
  38.             }
  39.         }
  40.  
  41.         [Tooltip("Used to draw lines. Ideally a white Sprites/Default shader."), SerializeField]
  42.         private Material lineMaterial;
  43.         public static Material LineMaterial {
  44.             get {
  45.                 Lines lines = Instance;
  46.                 if (lines.lineMaterial != null) return lines.lineMaterial;
  47.                 const string colorShaderName = "Sprites/Default";//"Unlit/Color";
  48.                 lines.lineMaterial = FindShaderMaterial(colorShaderName);
  49.                 return lines.lineMaterial;
  50.             }
  51.         }
  52.        
  53.         /// <summary>
  54.         /// intended for use when comparing whole numbers or fractional powers of 2
  55.         /// </summary>
  56.         /// <param name="a"></param>
  57.         /// <param name="b"></param>
  58.         /// <returns></returns>
  59.         public static bool EQ2(float a, float b) {
  60.             // ReSharper disable once CompareOfFloatsByEqualityOperator
  61.             return a == b;
  62.         }
  63.  
  64.         /// <summary>
  65.         /// used to check equality of two floats that are not expected to be assigned as powers of 2
  66.         /// </summary>
  67.         /// <param name="delta">the difference between two floats</param>
  68.         /// <returns></returns>
  69.         public static bool EQ(float delta) { return Mathf.Abs(delta) < TOLERANCE; }
  70.  
  71.         public static Lines Instance {
  72.             get {
  73.                 if (_instance) return _instance;
  74.                 return _instance = FindComponentInstance<Lines>();
  75.             }
  76.         }
  77.  
  78.         public static T FindComponentInstance<T>() where T : Component {
  79.             T instance;
  80.             if ((instance = FindObjectOfType(typeof(T)) as T) != null) return instance;
  81.             GameObject g = new GameObject($"<{typeof(T).Name}>");
  82.             instance = g.AddComponent<T>();
  83.             return instance;
  84.         }
  85.  
  86.         private void Start() {
  87.             if (_instance == null || _instance == this) return;
  88.             Debug.LogWarning("<Lines> should be a singleton. Deleting extra");
  89.             Destroy(this);
  90.         }
  91.  
  92.         /// <param name="name"></param>
  93.         /// <param name="createIfNotFound">if true, this function will not return null</param>
  94.         /// <returns>a line object with the given name. can return null if no such object has been made yet with this function</returns>
  95.         public static GameObject Get(string name, bool createIfNotFound = false) {
  96.             if ((NamedObject.TryGetValue(name, out GameObject go) && go) || !createIfNotFound) return go;
  97.             go = NamedObject[name] = MakeLineRenderer(ref go).gameObject;
  98.             go.name = name;
  99.             return go;
  100.         }
  101.  
  102.         /// <summary></summary>
  103.         /// <returns>an unnamed, unmanaged Line object</returns>
  104.         public static Wire MakeWire() {
  105.             GameObject go = null;
  106.             MakeLineRenderer(ref go);
  107.             Wire line = go.GetComponent<Wire>();
  108.             if (!line) { line = go.AddComponent<Wire>(); line.RefreshSource(); }
  109.             return line;
  110.         }
  111.        
  112.         /// <summary>looks for a line object with the given name and returns it</summary>
  113.         /// <param name="name"></param>
  114.         /// <param name="createIfNotFound"></param>
  115.         /// <returns>a line object with the given name, or null if createIfNotFound is false and the object doesn't exist</returns>
  116.         public static Wire Make(string name, bool createIfNotFound = true) {
  117.             GameObject go = Get(name, createIfNotFound);
  118.             if (go == null) return null;
  119.             Wire line = go.GetComponent<Wire>();
  120.             if (!line) { line = go.AddComponent<Wire>(); line.RefreshSource(); }
  121.             return line;
  122.         }
  123.  
  124.         /// <summary>
  125.         /// use this if name collisions are OK because instances are being managed
  126.         /// </summary>
  127.         /// <param name="name"></param>
  128.         /// <returns></returns>
  129.         public static Wire MakeForcedUnregistered(string name = "<unnamed>") {
  130.             GameObject go = null;
  131.             go = MakeLineRenderer(ref go).gameObject;
  132.             go.name = name;
  133.             Wire line = go.AddComponent<Wire>(); line.RefreshSource();
  134.             return line;
  135.         }
  136.         public enum End { Normal, Arrow, ArrowBothEnds };
  137.         /// <summary>cached calculations. used to validate if a line needs to be re-calculated</summary>
  138.         public class Wire : MonoBehaviour {
  139.             public enum Kind { None, Line, Arc, Orbital, SpiralSphere, Box, Quaternion, CartesianPlane, Rectangle, Disabled }
  140.             private Kind _kind;
  141.             private Vector3[] _points;
  142.             private Vector3 _normal;
  143.             private Quaternion _rotation;
  144.             private int _count;
  145.             private float _startSize, _endSize, _angle;
  146.             private End _lineEnds;
  147.             public LineRenderer lr;
  148. #if UNITY_EDITOR
  149.             /// <summary>
  150.             /// Where the code is that created this <see cref="Wire"/>. Not present in deployed builds.
  151.             /// </summary>
  152.             // ReSharper disable once NotAccessedField.Global
  153.             public string sourceCode;
  154. #endif
  155.             public int NumCapVertices {
  156.                 get => lr.numCapVertices;
  157.                 set => lr.numCapVertices = value;
  158.             }
  159.  
  160.             public void RefreshSource() {
  161. #if UNITY_EDITOR
  162.                 StackTrace stackTrace = new StackTrace(true);
  163.                 StackFrame f = stackTrace.GetFrame(2);
  164.                 string path = f.GetFileName();
  165.                 if (path == null) return;
  166.                 int fileIndex = path.LastIndexOf(System.IO.Path.DirectorySeparatorChar);
  167.                 sourceCode = $"{path.Substring(fileIndex+1)}:{f.GetFileLineNumber().ToString()}";
  168. #endif
  169.             }
  170.            
  171.             public Kind kind {
  172.                 get => _kind;
  173.                 set {
  174.                     // special cleanup for Quaternions
  175.                     if (_kind == Kind.Quaternion && value != Kind.Quaternion) {
  176.                         DisposeOfChildWires();
  177.                     }
  178.                     // special cleanup for CartesianPlanes
  179.                     if (_kind == Kind.CartesianPlane && value != Kind.CartesianPlane) {
  180.                         DisposeOfChildWires();
  181.                     }
  182.                     // special cleanup for Rectangles
  183.                     if (_kind == Kind.Rectangle && value != Kind.Rectangle) {
  184.                         DisposeOfChildWires();
  185.                     }
  186.                     _kind = value;
  187.                 }
  188.             }
  189.  
  190.             private void DisposeOfChildWires() {
  191.                 Wire[] obj = ChildWires(_count, false);
  192.                 if (obj != null) {
  193.                     Array.ForEach(obj, w => { w.transform.SetParent(null); Destroy(w.gameObject); });
  194.                 }
  195.             }
  196.  
  197.             private static bool SameArrayOfVectors(Vector3[] a, Vector3[] b) {
  198.                 if (ReferenceEquals(a, b)) { return true; }
  199.                 if (a == null || b == null || a.Length != b.Length) { return false; }
  200.                 for (int i = 0; i < a.Length; ++i) { if (a[i] != b[i]) return false; }
  201.                 return true;
  202.             }
  203.             private bool IsLine(Vector3[] points, float startSize, float endSize, End lineEnds) {
  204.                 return kind == Kind.Line && SameArrayOfVectors(_points, points)
  205.                     && EQ(startSize - _startSize) && EQ(endSize - _endSize) && _lineEnds == lineEnds;
  206.             }
  207.             private void SetLine(Vector3[] points, float startSize, float endSize, End lineEnds) {
  208.                 kind = Kind.Line;
  209.                 if (points != null) {
  210.                     _points = new Vector3[points.Length]; Array.Copy(points, _points, points.Length);
  211.                 }
  212.                 _points = null;
  213.                 _startSize = startSize; _endSize = endSize; _lineEnds = lineEnds;
  214.             }
  215.             private bool IsArc(Vector3 start, Vector3 normal, Vector3 center, float angle, float startSize, float endSize, End lineEnds, int pointCount) {
  216.                 return kind == Kind.Arc && _points != null && _points.Length == 1 && _points[0] == start && _count == pointCount
  217.                     && _normal == normal && EQ(startSize - _startSize) && EQ(endSize - _endSize) && _lineEnds == lineEnds
  218.                     && transform.position == center && _normal == normal && EQ(_angle - angle);
  219.             }
  220.             private void SetArc(Vector3 start, Vector3 normal, Vector3 center, float angle, float startSize, float endSize, End lineEnds, int pointCount) {
  221.                 kind = Kind.Arc;
  222.                 _points = new Vector3[] { start }; _count = pointCount;
  223.                 _startSize = startSize; _endSize = endSize; _lineEnds = lineEnds;
  224.                 transform.position = center; _normal = normal; _angle = angle;
  225.             }
  226.             private bool IsOrbital(Vector3 start, Vector3 end, Vector3 center, float startSize, float endSize, End lineEnds, int pointCount) {
  227.                 return kind == Kind.Orbital && _points != null && _points.Length == 2 && _count == pointCount
  228.                     && _points[0] == start && _points[1] == end
  229.                     && EQ(startSize - _startSize) && EQ(endSize - _endSize) && _lineEnds == lineEnds
  230.                     && transform.position == center;
  231.             }
  232.             private void SetOrbital(Vector3 start, Vector3 end, Vector3 center = default, float startSize = LINE_SIZE, float endSize = SAME_AS_START_SIZE,
  233.                 End lineEnds = default, int pointCount = -1) {
  234.                 kind = Kind.Orbital;
  235.                 _points = new Vector3[] { start, end }; _count = pointCount;
  236.                 _startSize = startSize; _endSize = endSize; _lineEnds = lineEnds;
  237.                 transform.position = center;
  238.             }
  239.             private bool IsSpiralSphere(Vector3 center, float radius, float lineSize, Quaternion rotation) {
  240.                 return kind == Kind.SpiralSphere
  241.                     && EQ(_startSize - lineSize) && EQ(_endSize - lineSize)
  242.                     && transform.position == center && EQ(_angle - radius)
  243.                     && (_rotation.Equals(rotation) || _rotation == rotation);
  244.             }
  245.             private void SetSpiralSphere(Vector3 center, float radius, float lineSize, Quaternion rotation) {
  246.                 kind = Kind.SpiralSphere;
  247.                 _startSize = _endSize = lineSize;
  248.                 transform.position = center; _angle = radius; _rotation = rotation;
  249.             }
  250.             private bool IsBox(Vector3 center, Vector3 size, Quaternion rotation, float lineSize) {
  251.                 Transform t = transform;
  252.                 return kind == Kind.Box
  253.                     && EQ(_startSize - lineSize) && EQ(_endSize - lineSize)
  254.                     && t.position == center
  255.                     && t.localScale == size && t.rotation == rotation;
  256.             }
  257.             private void SetBox(Vector3 center, Vector3 size, Quaternion rotation, float lineSize) {
  258.                 Transform t = transform;
  259.                 kind = Kind.Box;
  260.                 _startSize = _endSize = lineSize;
  261.                 t.position = center;
  262.                 t.localScale = size;
  263.                 t.rotation = rotation;
  264.             }
  265.             private bool IsQuaternion(float an, Vector3 ax, Vector3 position, Vector3[] startPoints, Quaternion orientation, float lineSize) {
  266.                 return kind == Kind.Quaternion && SameArrayOfVectors(_points, startPoints)
  267.                     && EQ(_startSize - lineSize) && EQ(_endSize - lineSize)
  268.                     && transform.position == position && _normal == ax && EQ(_angle - an) && _count == startPoints.Length
  269.                     && (_rotation.Equals(orientation) || _rotation == orientation); // quaternions can't easily be tested for equality because of floating point errors
  270.             }
  271.             private void SetQuaternion(float an, Vector3 ax, Vector3 position, Vector3[] startPoints, Quaternion orientation, float lineSize) {
  272.                 kind = Kind.Quaternion;
  273.                 if (ReferenceEquals(startPoints, DefaultQuaternionVisualizationPoints)) {
  274.                     _points = DefaultQuaternionVisualizationPoints;
  275.                 } else {
  276.                     _points = new Vector3[startPoints.Length]; Array.Copy(startPoints, _points, startPoints.Length);
  277.                 }
  278.                 _startSize = _endSize = lineSize;
  279.                 transform.position = position; _normal = ax; _angle = an; _count = startPoints.Length;
  280.                 _rotation = orientation;
  281.             }
  282.             private bool IsCartesianPlane(Vector3 center, Quaternion rotation, float lineSize, float extents, float increment) {
  283.                 return kind == Kind.CartesianPlane && EQ(_startSize - extents) && EQ(_endSize - lineSize) && EQ(_angle - increment) &&
  284.                        (_rotation.Equals(rotation) || _rotation == rotation) && transform.position == center;
  285.             }
  286.             private void SetCartesianPlane(Vector3 center, Quaternion rotation, float lineSize, float extents, float increment) {
  287.                 kind = Kind.CartesianPlane; _startSize = extents; _endSize = lineSize; _angle = increment;
  288.                 _rotation = rotation; transform.position = center;
  289.                 _count = CartesianPlaneChildCount(extents, increment, out _);
  290.             }
  291.             private bool IsRectangle(Vector3 origin, Vector2 offset2d, Vector2 halfSize, float lineSize, Quaternion rotation) {
  292.                 return kind == Kind.Rectangle && origin == transform.position && EQ(_startSize-lineSize) && (_rotation.Equals(rotation) || _rotation == rotation) &&
  293.                        EQ(_normal.x-offset2d.x) && EQ(_normal.y-offset2d.y) && EQ(_normal.z-halfSize.x) && EQ(_endSize-halfSize.y);
  294.             }
  295.             private void SetRectangle(Vector3 origin, Vector2 offset2d, Vector2 halfSize, float lineSize, Quaternion rotation) {
  296.                 kind = Kind.Rectangle; transform.position = origin; _startSize = lineSize; _rotation = rotation;
  297.                 _normal.x = offset2d.x; _normal.y = offset2d.y; _normal.z = halfSize.x; _endSize = halfSize.y; _count = 4;
  298.             }
  299.            
  300.             public Wire Line(Vector3 start, Vector3 end, Color color = default, float startSize = LINE_SIZE, float endSize = SAME_AS_START_SIZE) {
  301.                 return Line(new Vector3[] { start, end }, color, End.Normal, startSize, endSize);
  302.             }
  303.             public Wire Arrow(Ray ray, Color color = default, float startSize = LINE_SIZE, float endSize = SAME_AS_START_SIZE) {
  304.                 return Line(new Vector3[] { ray.origin, ray.origin+ray.direction }, color, End.Arrow, startSize, endSize);
  305.             }
  306.             public Wire Arrow(Vector3 vector, Color color = default, float startSize = LINE_SIZE, float endSize = SAME_AS_START_SIZE) {
  307.                 return Line(new Vector3[] { Vector3.zero, vector }, color, End.Arrow, startSize, endSize);
  308.             }
  309.             public Wire Arrow(Vector3 start, Vector3 end, Color color = default, float startSize = LINE_SIZE, float endSize = SAME_AS_START_SIZE) {
  310.                 return Line(new Vector3[] { start, end }, color, End.Arrow, startSize, endSize);
  311.             }
  312.  
  313.             public Wire Line(Vector3 vector, Color color = default, float startSize = LINE_SIZE, float endSize = SAME_AS_START_SIZE) {
  314.                 return Line(new Vector3[] { Vector3.zero, vector }, color, End.Normal, startSize, endSize);
  315.             }
  316.             public Wire Line(Vector3[] points, Color color = default, End lineEnds = default, float startSize = LINE_SIZE, float endSize = SAME_AS_START_SIZE) {
  317.                 if (!IsLine(points, startSize, endSize, lineEnds)) {
  318.                     SetLine(points, startSize, endSize, lineEnds);
  319.                     if (!lr) { lr = MakeLineRenderer(gameObject); }
  320.                     lr = MakeLine(lr, points, color, startSize, endSize, lineEnds);
  321.                 } //else { Debug.Log("don't need to recalculate line "+name); }
  322.                 if (lr) { SetColor(lr, color); }
  323.                 return this;
  324.             }
  325.             public Wire Arc(float angle, Vector3 normal, Vector3 firstPoint, Vector3 center = default, Color color = default,
  326.                 End lineEnds = default, int pointCount = -1, float startSize = LINE_SIZE, float endSize = SAME_AS_START_SIZE) {
  327.                 if (pointCount < 0) { pointCount = (int)(24 * angle / 180f) + 1; }
  328.                 if (!IsArc(firstPoint, normal, center, angle, startSize, endSize, End.Normal, pointCount)) {
  329.                     SetArc(firstPoint, normal, center, angle, startSize, endSize, End.Normal, pointCount);
  330.                     if (!lr) { lr = MakeLineRenderer(gameObject); }
  331.                     Vector3[] linePoints = null;
  332.                     WriteArc(ref linePoints, pointCount, normal, firstPoint, angle, center);
  333.                     lr = MakeLine(lr, linePoints, color, startSize, endSize, lineEnds);
  334.                 } //else { Debug.Log("don't need to recalculate arc "+name);  }
  335.                 if(EQ2(angle, 360)) { lr.loop = true; }
  336.                 SetColor(lr, color);
  337.                 return this;
  338.             }
  339.             public Wire Circle(Vector3 center = default, Vector3 normal = default, Color color = default,
  340.                 float radius = 1, float lineSize = LINE_SIZE, int pointCount = -1) {
  341.                 if(EQ2(radius, 0)) { return Line(null, color, End.Normal, lineSize, lineSize); }
  342.                 if (normal == default) { normal = Vector3.up; }
  343.                 Vector3 firstPoint = Vector3.zero;
  344.                 if (kind == Kind.Arc && this._normal == normal && _points != null && _points.Length > 0) {
  345.                     float firstRad = _points[0].magnitude;
  346.                     if (EQ2(firstRad, radius)) {
  347.                         firstPoint = _points[0];
  348.                     } else {
  349.                         firstPoint = _points[0] * (radius / firstRad);
  350.                     }
  351.                 }
  352.                 if (firstPoint == Vector3.zero) {
  353.                     firstPoint = Vector3.right;
  354.                     if (normal != Vector3.up && normal != Vector3.forward && normal != Vector3.back) {
  355.                         firstPoint = Vector3.Cross(normal, Vector3.forward).normalized;
  356.                     }
  357.                     firstPoint *= radius;
  358.                 }
  359.                 return Arc(360, normal, firstPoint, center, color, End.Normal, pointCount, lineSize, lineSize);
  360.             }
  361.             public Wire Orbital(Vector3 sphereCenter, Vector3 start, Vector3 end,
  362.                 Color color = default, End lineEnds = default, float startSize = LINE_SIZE, float endSize = SAME_AS_START_SIZE, int pointCount = -1) {
  363.                 if (!IsOrbital(start, end, sphereCenter, startSize, endSize, lineEnds, pointCount)) {
  364.                     SetOrbital(start, end, sphereCenter, startSize, endSize, lineEnds, pointCount);
  365.                     Vector3[] linePoints = null;
  366.                     WriteArcOnSphere(ref linePoints, pointCount, sphereCenter, start, end);
  367.                     if (!lr) { lr = MakeLineRenderer(gameObject); }
  368.                     lr = MakeLine(lr, linePoints, color, startSize, endSize, lineEnds);
  369.                 } //else { Debug.Log("don't need to recalculate orbital " + name); }
  370.                 SetColor(lr, color);
  371.                 return this;
  372.             }
  373.             public Wire SpiralSphere(Color color = default, Vector3 center = default, float radius = 1, Quaternion rotation = default, float lineSize = LINE_SIZE) {
  374.                 GameObject go = gameObject;
  375.                 if (!IsSpiralSphere(center, radius, lineSize, rotation)) {
  376.                     SetSpiralSphere(center, radius, lineSize, rotation);
  377.                     lr = MakeSpiralSphere(ref go, radius, center, rotation, color, lineSize);
  378.                 } //else { Debug.Log("don't need to recalculate spiral sphere " + name); }
  379.                 SetColor(lr, color);
  380.                 return this;
  381.             }
  382.             public Wire Box(Vector3 size, Vector3 center = default, Quaternion rotation = default, Color color = default, float lineSize = LINE_SIZE) {
  383.                 GameObject go = gameObject;
  384.                 if (!IsBox(center, size, rotation, lineSize)) {
  385.                     SetBox(center, size, rotation, lineSize);
  386.                     lr = MakeBox(ref go, center, size, rotation, color, lineSize);
  387.                 } //else { Debug.Log("don't need to recalculate box " + name); }
  388.                 SetColor(lr, color);
  389.                 return this;
  390.             }
  391.             private static readonly Vector3[] DefaultQuaternionVisualizationPoints = new Vector3[] { Vector3.forward, Vector3.up };
  392.             public Wire Quaternion(Quaternion q, Color color, Vector3 position = default, Vector3[] startPoints = null,
  393.                 Quaternion orientation = default, int arcPoints = -1, float lineSize = LINE_SIZE) {
  394.                 GameObject go = gameObject;
  395.                 q.ToAngleAxis(out float an, out Vector3 ax);
  396.                 if (startPoints == null) { startPoints = DefaultQuaternionVisualizationPoints; }
  397.                 if (!IsQuaternion(an, ax, position, startPoints, orientation, lineSize)) {
  398.                     SetQuaternion(an, ax, position, startPoints, orientation, lineSize);
  399.                     Wire[] childWires = ChildWires(startPoints.Length, true);
  400.                     MakeQuaternion(ref go, childWires, ax, an, position, color, orientation, arcPoints, lineSize, ARROW_SIZE, startPoints);
  401.                     lr = go.GetComponent<LineRenderer>();
  402.                 } //else { Debug.Log("don't need to recalculate quaternion " + name); }
  403.                 SetColor(lr, color);
  404.                 return this;
  405.             }
  406.             private Wire[] ChildWires(int objectCount, bool createIfNoneExist) {
  407.                 Wire[] wireObjs = null;
  408.                 const string _name = "__";
  409.                 if (transform.childCount >= objectCount) {
  410.                     int childrenWithWire = 0;
  411.                     Transform[] children = new Transform[transform.childCount];
  412.                     for (int i = 0; i < children.Length; ++i) { children[i] = transform.GetChild(i); }
  413.                     Array.ForEach(children, (child) => {
  414.                         if (child.name.Contains(_name) && child.GetComponent<Wire>() != null) { ++childrenWithWire; }
  415.                     });
  416.                     if (childrenWithWire >= objectCount) {
  417.                         wireObjs = new Wire[objectCount];
  418.                         int validLine = 0;
  419.                         for (int i = 0; i < children.Length && validLine < wireObjs.Length; ++i) {
  420.                             Wire w;
  421.                             if (children[i].name.Contains(_name) && (w =children[i].GetComponent<Wire>()) != null)
  422.                                 wireObjs[validLine++] = w;
  423.                         }
  424.                     }
  425.                 }
  426.                 if (wireObjs == null && createIfNoneExist) {
  427.                     wireObjs = new Wire[objectCount];
  428.                     for (int i = 0; i < wireObjs.Length; ++i) {
  429.                         Wire wireObject = MakeWire();
  430.                         wireObject.name = _name + name + i;
  431.                         wireObject.transform.SetParent(transform);
  432.                         wireObjs[i] = wireObject;
  433.                     }
  434.                 }
  435.                 return wireObjs;
  436.             }
  437.             public Wire CartesianPlane(Vector3 center, Quaternion rotation, Color color = default, float lineSize = LINE_SIZE, float extents = 1, float increment = 0.25f) {
  438.                 bool colorIsSet = false;
  439.                 if (!IsCartesianPlane(center, rotation, lineSize, extents, increment)) {
  440.                     SetCartesianPlane(center, rotation, lineSize, extents, increment);
  441.                     Vector3 up = rotation * Vector3.up;
  442.                     Vector3 right = rotation * Vector3.right;
  443.                     Wire[] wires = ChildWires(_count, true);
  444.                     MakeCartesianPlane(center, up, right, wires, color, lineSize, extents, increment);
  445.                     colorIsSet = true;
  446.                     Vector3 normal = Vector3.Cross(right, up).normalized;
  447.                     if (!lr) { lr = MakeLineRenderer(gameObject); }
  448.                     Vector3[] points = new Vector3[]{center, center + normal * increment};
  449.                     lr = MakeLine(lr, points, color, lineSize, lineSize, End.Arrow);
  450.                 } //else { Debug.Log("don't need to recalculate quaternion " + name); }
  451.                 if (!colorIsSet) {
  452.                     SetColor(lr, color);
  453.                     Wire[] wires = ChildWires(_count, true);
  454.                     Array.ForEach(wires, w => SetColor(w.lr, color));
  455.                 }
  456.                 return this;
  457.             }
  458.             public Wire Rectangle(Vector3 origin, Vector2 halfSize, Color a_color = default, Quaternion rotation = default, Vector2 offset2d = default, float lineSize = LINE_SIZE) {
  459.                 //if(halfSize == default) { halfSize = Vector2.one / 2; }
  460.                 bool colorIsSet = false;
  461.                 if (!IsRectangle(origin, offset2d, halfSize, lineSize, rotation)) {
  462.                     SetRectangle(origin, offset2d, halfSize, lineSize, rotation);
  463.                     Wire[] wires = ChildWires(_count, true);
  464.                     MakeRectangle(wires, origin, offset2d, halfSize, lineSize, a_color, rotation);
  465.                     colorIsSet = true;
  466.                     if (!lr) { lr = MakeLineRenderer(gameObject); }
  467.                     lr.startWidth = lr.endWidth = 0;
  468.                 } //else { Debug.Log("don't need to recalculate quaternion " + name); }
  469.                 if (!colorIsSet) {
  470.                     //SetColor(lr, a_color);
  471.                     Wire[] wires = ChildWires(_count, true);
  472.                     Array.ForEach(wires, w => SetColor(w.lr, a_color));
  473.                 }
  474.                 return this;
  475.             }
  476.         }
  477.  
  478.         /// <summary>
  479.         /// Make the specified Line.
  480.         /// example usage:
  481.         /// <para><code>
  482.         /// /* GameObject forwardLine should be a member variable */
  483.         /// Lines.Make (ref forwardLine, transform.position,
  484.         ///             transform.position + transform.forward, Color.blue, 0.1f, 0);
  485.         /// //This makes a long thin triangle, pointing forward.
  486.         /// </code></para>
  487.         /// </summary>
  488.         /// <param name="lineObject">GameObject host of the LineRenderer</param>
  489.         /// <param name="start">Start, an absolute world-space coordinate</param>
  490.         /// <param name="end">End, an absolute world-space coordinate</param>
  491.         /// <param name="color"></param>
  492.         /// <param name="startSize">How wide the line is at the start</param>
  493.         /// <param name="endSize">How wide the line is at the end</param>
  494.         public static LineRenderer Make(ref GameObject lineObject, Vector3 start, Vector3 end,
  495.             Color color = default, float startSize = LINE_SIZE, float endSize = SAME_AS_START_SIZE) {
  496.             LineRenderer lr = MakeLineRenderer(ref lineObject);
  497.             SetLine(lr, color, startSize, endSize);
  498.             lr.positionCount = 2;
  499.             lr.SetPosition(0, start); lr.SetPosition(1, end);
  500.             return lr;
  501.         }
  502.  
  503.         /// <summary>Make the specified Line from a list of points</summary>
  504.         /// <returns>The LineRenderer hosting the line</returns>
  505.         /// <param name="lineObject">GameObject host of the LineRenderer</param>
  506.         /// <param name="color">Color of the line</param>
  507.         /// <param name="points">List of absolute world-space coordinates</param>
  508.         /// <param name="pointCount">Number of the points used points list</param>
  509.         /// <param name="startSize">How wide the line is at the start</param>
  510.         /// <param name="endSize">How wide the line is at the end</param>
  511.         public static LineRenderer Make(ref GameObject lineObject, IList<Vector3> points, int pointCount,
  512.             Color color = default, float startSize = LINE_SIZE, float endSize = SAME_AS_START_SIZE) {
  513.             LineRenderer lr = MakeLineRenderer(ref lineObject);
  514.             return Make(lr, points, pointCount, color, startSize, endSize);
  515.         }
  516.  
  517.         public static LineRenderer Make(LineRenderer lr, IList<Vector3> points, int pointCount,
  518.             Color color = default, float startSize = LINE_SIZE, float endSize = SAME_AS_START_SIZE) {
  519.             SetLine(lr, color, startSize, endSize);
  520.             return MakeLine(lr, points, pointCount);
  521.         }
  522.  
  523.         public static LineRenderer MakeLine(LineRenderer lr, IList<Vector3> points, int pointCount) {
  524.             lr.positionCount = pointCount;
  525.             for (int i = 0; i < pointCount; ++i) { lr.SetPosition(i, points[i]); }
  526.             return lr;
  527.         }
  528.  
  529.         public static LineRenderer MakeLine(ref GameObject go, Vector3[] points, Color color, float startSize,
  530.             float endSize, End lineEnds) {
  531.             LineRenderer lr = MakeLineRenderer(ref go);
  532.             return MakeLine(lr, points, color, startSize, endSize, lineEnds);
  533.         }
  534.         public static LineRenderer MakeLine(LineRenderer lr, Vector3[] points, Color color, float startSize, float endSize, End lineEnds) {
  535.             if (EQ2(endSize, SAME_AS_START_SIZE)) { endSize = startSize; }
  536.             if(points == null) { lr = Make(lr, null, 0, color, startSize, endSize); return lr; }
  537.             if (lineEnds == End.Arrow || lineEnds == End.ArrowBothEnds) {
  538.                 Keyframe[] keyframes = CalculateArrowKeyframes(points, points.Length, out var line, startSize, endSize);
  539.                 lr = MakeArrow(lr, line, line.Length, color, startSize, endSize);
  540.                 lr.widthCurve = new AnimationCurve(keyframes);
  541.                 if (lineEnds == End.ArrowBothEnds) {
  542.                     ReverseLineInternal(ref lr);
  543.                     Vector3[] p = new Vector3[lr.positionCount];
  544.                     lr.GetPositions(p);
  545.                     lr = MakeArrow(lr, p, p.Length, color, endSize, startSize, ARROW_SIZE, lr.widthCurve.keys);
  546.                     ReverseLineInternal(ref lr);
  547.                 }
  548.             } else {
  549.                 lr = Make(lr, points, points.Length, color, startSize, endSize);
  550.                 FlattenKeyFrame(lr);
  551.             }
  552.             if(lr.loop && lineEnds != End.Normal) { lr.loop = false; }
  553.             return lr;
  554.         }
  555.        
  556.         public static void FlattenKeyFrame(LineRenderer lr) {
  557.             AnimationCurve widthCurve = lr.widthCurve;
  558.             Keyframe[] keys = widthCurve.keys;
  559.             if (keys != null && keys.Length > 2) {
  560.                 lr.widthCurve = new AnimationCurve(new Keyframe[]{keys[0], keys[keys.Length-1]});
  561.             }
  562.         }
  563.        
  564.         public static LineRenderer MakeLineRenderer(ref GameObject lineObject) {
  565.             if (!lineObject) {
  566.                 lineObject = new GameObject();
  567.                 if (Instance.autoParentLinesToGlobalObject) {
  568.                     lineObject.transform.SetParent(_instance.transform);
  569.                 }
  570.             }
  571.             return MakeLineRenderer(lineObject);
  572.         }
  573.  
  574.         public static LineRenderer MakeLineRenderer(GameObject lineObject) {
  575.             LineRenderer lr = lineObject.GetComponent<LineRenderer>();
  576.             if (!lr) { lr = lineObject.AddComponent<LineRenderer>(); }
  577.             return lr;
  578.         }
  579.  
  580.         public static LineRenderer SetLine(LineRenderer lr, Color color, float startSize, float endSize) {
  581.             lr.startWidth = startSize;
  582.             if (EQ2(endSize, SAME_AS_START_SIZE)) { endSize = startSize;}
  583.             lr.endWidth = endSize;
  584.             SetColor(lr, color);
  585.             return lr;
  586.         }
  587.  
  588.         public static Material FindShaderMaterial(string shaderName) {
  589.             Shader s = Shader.Find(shaderName);
  590.             if (!s) {
  591.                 throw new Exception("Missing shader: " + shaderName
  592.                     + ". Please make sure it is in the \"Resources\" folder, "
  593.                     + "or used by at least one other object in the scene. Or, "
  594.                     + " manually assign the line material to a Lines GameObject.");
  595.             }
  596.             return new Material(s);
  597.         }
  598.  
  599.         public static void SetColor(LineRenderer lr, Color color) {
  600.             bool needsMaterial = lr.material == null || lr.material.name.StartsWith("Default-Material");
  601.             if (needsMaterial) { lr.material = LineMaterial; }
  602.             if (color == default) { color = Color.magenta; }
  603. #if PULSE_COLOR
  604.             long t = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
  605.             long duration = 500;
  606.             long secComponent = t % duration;
  607.             float a = Mathf.Abs((2f * secComponent - duration) / duration);
  608.             Color.RGBToHSV(color, out float h, out float  s, out float v);
  609.             color = Color.HSVToRGB(h, s + (a * .25f), v + (a * .25f));
  610. #endif
  611.             lr.material.color = color;
  612.         }
  613.  
  614.         /// <summary>Write 2D arc in 3D space, into given Vector3 array</summary>
  615.         /// <param name="points">Will host the list of coordinates</param>
  616.         /// <param name="pointCount">How many vertices to make &gt; 1</param>
  617.         /// <param name="normal">The surface-normal of the arc's plane</param>
  618.         /// <param name="firstPoint">Arc start, rotate about Vector3.zero</param>
  619.         /// <param name="angle">2D angle. Tip: Vector3.Angle(v1, v2)</param>
  620.         /// <param name="offset">How to translate the arc</param>
  621.         /// <param name="startIndex"></param>
  622.         public static void WriteArc(ref Vector3[] points, int pointCount,
  623.             Vector3 normal, Vector3 firstPoint, float angle = 360, Vector3 offset = default, int startIndex = 0) {
  624.             if (pointCount < 0) {
  625.                 pointCount = (int)Mathf.Abs(24 * angle / 180f) + 1;
  626.             }
  627.             if (pointCount < 0 || pointCount >= 32767) { throw new Exception($"bad point count value: {pointCount}"); }
  628.             if (points == null) { points = new Vector3[pointCount]; }
  629.             if (startIndex >= points.Length) return;
  630.             points[startIndex] = firstPoint;
  631.             Quaternion q = Quaternion.AngleAxis(angle / (pointCount - 1), normal);
  632.             for (int i = startIndex + 1; i < startIndex + pointCount; ++i) { points[i] = q * points[i - 1]; }
  633.             if (offset != Vector3.zero)
  634.                 for (int i = startIndex; i < startIndex + pointCount; ++i) { points[i] += offset; }
  635.         }
  636.  
  637.         /// <summary>
  638.         /// Make the specified arc line in 3D space. Example usage: <para><code>
  639.         /// /* GameObject turnArc should be a member variable */
  640.         /// Lines.MakeArc(ref turnArc, Vector3.Angle(transform.forward, direction),
  641.         ///     10, Vector3.Cross(transform.forward, direction),
  642.         ///     transform.forward, transform.position, Color.green, 0.1f, 0);
  643.         /// // makes a curve showing the turn from transform.forward to direction
  644.         /// </code></para>
  645.         /// </summary>
  646.         /// <returns>The LineRenderer hosting the line</returns>
  647.         /// <param name="lineObj">GameObject host of the LineRenderer</param>
  648.         /// <param name="color">Color of the line</param>
  649.         /// <param name="center">Center of arc</param>
  650.         /// <param name="normal">surface-normal of arc's plane</param>
  651.         /// <param name="firstPoint">Arc start, rotate about Vector3.zero</param>
  652.         /// <param name="angle">2D angle. Tip: Vector3.Angle(v1, v2)</param>
  653.         /// <param name="pointCount">How many vertices to make &gt; 1</param>
  654.         /// <param name="startSize">How wide the line is at the start</param>
  655.         /// <param name="endSize">How wide the line is at the end</param>
  656.         public static LineRenderer MakeArc(ref GameObject lineObj,
  657.             float angle, int pointCount, Vector3 normal, Vector3 firstPoint,
  658.             Vector3 center = default, Color color = default, float startSize = LINE_SIZE, float endSize = SAME_AS_START_SIZE) {
  659.             Vector3[] points = null;
  660.             WriteArc(ref points, pointCount, normal, firstPoint, angle, center);
  661.             return Make(ref lineObj, points, pointCount, color, startSize, endSize);
  662.         }
  663.  
  664.         public static LineRenderer MakeLineOnSphere(ref GameObject lineObj, Vector3 sphereCenter, Vector3 start, Vector3 end,
  665.             Color color = default, float startSize = LINE_SIZE, float endSize = SAME_AS_START_SIZE, int pointCount = 24) {
  666.             Vector3[] points = null;
  667.             WriteArcOnSphere(ref points, pointCount, sphereCenter, start, end);
  668.             return Make(ref lineObj, points, pointCount, color, startSize, endSize);
  669.         }
  670.  
  671.         public static void WriteArcOnSphere(ref Vector3[] points, int pointCount, Vector3 sphereCenter, Vector3 start, Vector3 end) {
  672.             Vector3 axis;
  673.             if (start == -end) {
  674.                 axis = (start != Vector3.up && end != Vector3.up) ? Vector3.up : Vector3.right;
  675.             } else {
  676.                 axis = Vector3.Cross(start, end).normalized;
  677.             }
  678.             Vector3 a = start - sphereCenter, b = end - sphereCenter;
  679.             float aRad = a.magnitude, bRad = b.magnitude, angle = 0;
  680.             if (EQ2(aRad, 0) && EQ2(bRad, 0)) {
  681.                 a /= aRad; b /= bRad;
  682.                 angle = Vector3.Angle(a, b);
  683.                 if (float.IsNaN(angle)) { angle = 0; }
  684.             }
  685.             WriteArc(ref points, pointCount, axis, a, angle, Vector3.zero);
  686.             float radDelta = bRad - aRad;
  687.             for (int i = 0; i < points.Length; ++i) {
  688.                 points[i] = points[i] * ((i * radDelta / points.Length) + aRad);
  689.                 points[i] += sphereCenter;
  690.             }
  691.         }
  692.  
  693.         /// <summary>Makes a circle with a 3D line</summary>
  694.         /// <returns>The LineRenderer hosting the line</returns>
  695.         /// <param name="lineObj">GameObject host of the LineRenderer</param>
  696.         /// <param name="color">Color of the line</param>
  697.         /// <param name="center">Absolute world-space 3D coordinate</param>
  698.         /// <param name="normal">Which way the circle is facing</param>
  699.         /// <param name="radius"></param>
  700.         /// <param name="pointCount">How many points to use for the circle. If zero, will do 24*PI*r</param>
  701.         /// <param name="lineSize">The width of the line</param>
  702.         public static LineRenderer MakeCircle(ref GameObject lineObj, Vector3 center, Vector3 normal,
  703.             Color color = default, float radius = 1, int pointCount = 0, float lineSize = LINE_SIZE) {
  704.             Vector3[] points = null;
  705.             WriteCircle(ref points, center, normal, radius, pointCount);
  706.             LineRenderer lr = Lines.Make(ref lineObj, points, points.Length, color, lineSize, lineSize);
  707.             lr.loop = true;
  708.             return lr;
  709.         }
  710.  
  711.         public static int WriteCircle(ref Vector3[] points, Vector3 center, Vector3 normal, float radius = 1, int pointCount = 0) {
  712.             if (pointCount == 0) {
  713.                 pointCount = (int)Mathf.Round(24 * 3.14159f * radius + 0.5f);
  714.                 if (points != null) {
  715.                     pointCount = Mathf.Min(points.Length, pointCount);
  716.                 }
  717.             }
  718.             Vector3 crossDir = (normal == Vector3.up || normal == Vector3.down) ? Vector3.forward : Vector3.up;
  719.             Vector3 r = Vector3.Cross(normal, crossDir).normalized;
  720.             WriteArc(ref points, pointCount, normal, r * radius, 360, center);
  721.             return pointCount;
  722.         }
  723.  
  724.         public static LineRenderer MakeSphere(string name, float radius = 1,
  725.             Vector3 center = default, Color color = default, float lineSize = LINE_SIZE) {
  726.             GameObject go = Get(name, true);
  727.             return MakeSphere(ref go, radius, center, color, lineSize);
  728.         }
  729.         /// <returns>a line renderer in the shape of a sphere made of 3 circles, for the x.y.z axis</returns>
  730.         /// <param name="lineObj">Line object.</param>
  731.         /// <param name="radius">Radius.</param>
  732.         /// <param name="center">Center.</param>
  733.         /// <param name="color">Color.</param>
  734.         /// <param name="lineSize">Line size.</param>
  735.         public static LineRenderer MakeSphere(ref GameObject lineObj, float radius = 1,
  736.             Vector3 center = default, Color color = default, float lineSize = LINE_SIZE) {
  737.             Vector3[] circles = new Vector3[24 * 3];
  738.             Lines.WriteArc(ref circles, 24, Vector3.forward, Vector3.up, 360, center, 24 * 0);
  739.             Lines.WriteArc(ref circles, 24, Vector3.right, Vector3.up, 360, center, 24 * 1);
  740.             Lines.WriteArc(ref circles, 24, Vector3.up, Vector3.forward, 360, center, 24 * 2);
  741.             if (EQ2(radius, 1)) { for (int i = 0; i < circles.Length; ++i) { circles[i] *= radius; } }
  742.             return Lines.Make(ref lineObj, circles, circles.Length, color, lineSize, lineSize);
  743.         }
  744.  
  745.         public static LineRenderer MakeBox(ref GameObject lineObj, Vector3 center,
  746.             Vector3 size, Quaternion rotation, Color color = default, float lineSize = LINE_SIZE) {
  747.             Vector3 y = Vector3.up / 2 * size.y;
  748.             Vector3 x = Vector3.right / 2 * size.x;
  749.             Vector3 z = Vector3.forward / 2 * size.z;
  750.             Vector3[] line = new Vector3[] {
  751.                  z+y-x, -z+y-x, -z-y-x, -z-y+x, -z+y+x,  z+y+x,  z-y+x,  z-y-x,
  752.                  z+y-x,  z+y+x,  z-y+x, -z-y+x, -z+y+x, -z+y-x, -z-y-x,  z-y-x
  753.             };
  754.             for (int i = 0; i < line.Length; ++i) { line[i] = rotation * line[i] + center; }
  755.             LineRenderer lr = Make(ref lineObj, line, line.Length, color, lineSize, lineSize);
  756.             lr.numCornerVertices = 4;
  757.             return lr;
  758.         }
  759.  
  760.         public static LineRenderer MakeMapPin(string name, Color c = default, float size = 1, float lineSize = LINE_SIZE) {
  761.             GameObject go = Get(name, true);
  762.             return MakeMapPin(ref go, c, size, lineSize);
  763.         }
  764.         private static Vector3[] _mapPinPointsBase;
  765.         /// <summary>Draws a "map pin", which shows a visualization for direction and orientation</summary>
  766.         /// <returns>The LineRenderer hosting the map pin line. The LineRenderer's transform can be adjusted!</returns>
  767.         /// <param name="lineObj">Line object.</param>
  768.         /// <param name="c">C: color</param>
  769.         /// <param name="size">Size: radius of the map pin</param>
  770.         /// <param name="lineSize">Line width.</param>
  771.         public static LineRenderer MakeMapPin(ref GameObject lineObj, Color c = default, float size = 1, float lineSize = LINE_SIZE) {
  772.             const float epsilon = 1 / 1024.0f;
  773.             if (_mapPinPointsBase == null) {
  774.                 Vector3 pos = Vector3.zero, forward = Vector3.forward * size, right = Vector3.right * size, up = Vector3.up;
  775.                 const float startAngle = (360.0f / 4) - (360.0f / 32);
  776.                 Vector3 v = Quaternion.AngleAxis(startAngle, up) * forward;
  777.                 Lines.WriteArc(ref _mapPinPointsBase, 32, up, v, 360, pos);
  778.                 Vector3 tip = pos + forward * Mathf.Sqrt(2);
  779.                 _mapPinPointsBase[0] = _mapPinPointsBase[_mapPinPointsBase.Length - 1];
  780.                 int m = (32 * 5 / 8);
  781.                 _mapPinPointsBase[++m] = _mapPinPointsBase[m] + (tip - _mapPinPointsBase[m]) * (1 - epsilon);
  782.                 _mapPinPointsBase[++m] = tip;
  783.                 int n = (32 * 7 / 8) + 1;
  784.                 while (n < 32) { _mapPinPointsBase[++m] = _mapPinPointsBase[n++]; }
  785.                 Vector3 side = pos + right;
  786.                 _mapPinPointsBase[++m] = _mapPinPointsBase[m] + (side - _mapPinPointsBase[m]) * (1 - epsilon);
  787.                 _mapPinPointsBase[++m] = pos + right;
  788.                 _mapPinPointsBase[++m] = pos + right * epsilon;
  789.                 _mapPinPointsBase[++m] = pos;
  790.                 _mapPinPointsBase[++m] = pos + up *(size * (1 - epsilon));
  791.                 _mapPinPointsBase[++m] = pos + up * size;
  792.             }
  793.             LineRenderer lr = Lines.Make(ref lineObj, _mapPinPointsBase, _mapPinPointsBase.Length, c, lineSize, lineSize);
  794.             lr.useWorldSpace = false;
  795.             return lr;
  796.         }
  797.  
  798.         public static LineRenderer SetMapPin(string name, Transform t, Color c = default, float size = 1, float lineWidth = LINE_SIZE) {
  799.             GameObject go = Get(name, true);
  800.             return SetMapPin(ref go, t, c, size, lineWidth);
  801.         }
  802.         /// <summary>Draws a "map pin", which shows a visualization for direction and orientation</summary>
  803.         /// <returns>The LineRenderer hosting the map pin line</returns>
  804.         /// <param name="lineObj">Line object.</param>
  805.         /// <param name="t">t: the transform to attach the map pin visualisation to</param>
  806.         /// <param name="c">C: color</param>
  807.         /// <param name="size">Size: radius of the map pin</param>
  808.         /// <param name="lineWidth">Line width.</param>
  809.         public static LineRenderer SetMapPin(ref GameObject lineObj, Transform t, Color c = default, float size = 1, float lineWidth = LINE_SIZE) {
  810.             LineRenderer line_ = MakeMapPin(ref lineObj, c, size, lineWidth);
  811.             Transform transform = line_.transform;
  812.             transform.SetParent(t);
  813.             transform.localPosition = Vector3.zero;
  814.             transform.localRotation = Quaternion.identity;
  815.             return line_;
  816.         }
  817.  
  818.         public static Vector3 GetForwardVector(Quaternion q) {
  819.             return new Vector3(2 * (q.x * q.z + q.w * q.y),
  820.                 2 * (q.y * q.z + q.w * q.x),
  821.                 1 - 2 * (q.x * q.x + q.y * q.y));
  822.         }
  823.         public static Vector3 GetUpVector(Quaternion q) {
  824.             return new Vector3(2 * (q.x * q.y + q.w * q.z),
  825.                 1 - 2 * (q.x * q.x + q.z * q.z),
  826.                 2 * (q.y * q.z + q.w * q.x));
  827.         }
  828.         public static Vector3 GetRightVector(Quaternion q) {
  829.             return new Vector3(1 - 2 * (q.y * q.y + q.z * q.z),
  830.                 2 * (q.x * q.y + q.w * q.z),
  831.                 2 * (q.x * q.z + q.w * q.y));
  832.         }
  833.  
  834.         /// <example>CreateSpiralSphere(transform.position, 0.5f, transform.up, transform.forward, 16, 8);</example>
  835.         /// <summary>creates a line spiraled onto a sphere</summary>
  836.         /// <param name="center"></param>
  837.         /// <param name="radius"></param>
  838.         /// <param name="rotation"></param>
  839.         /// <param name="sides"></param>
  840.         /// <param name="rotations"></param>
  841.         /// <returns></returns>
  842.         public static Vector3[] CreateSpiralSphere(Vector3 center = default, float radius = 1,
  843.             Quaternion rotation = default, float sides = 12, float rotations = 6) {
  844.             List<Vector3> points = new List<Vector3>(); // List instead of Array because sides and rotations are floats!
  845.             Vector3 axis = Vector3.up;
  846.             Vector3 axisFace = Vector3.right;
  847.             if (EQ2(sides, 0) && EQ2(rotations, 0)) {
  848.                 float iter = 0;
  849.                 float increment = 1f / (rotations * sides);
  850.                 points.Add(center + axis * radius);
  851.                 do {
  852.                     iter += increment;
  853.                     Quaternion faceTurn = Quaternion.AngleAxis(iter * 360 * rotations, axis);
  854.                     Vector3 newFace = faceTurn * axisFace;
  855.                     Quaternion q = Quaternion.LookRotation(newFace);
  856.                     Vector3 right = GetUpVector(q);
  857.                     Vector3 r = right * radius;
  858.                     q = Quaternion.AngleAxis(iter * 180, newFace);
  859.                     r = q * r;
  860.                     r = rotation * r;
  861.                     Vector3 newPoint = center + r;
  862.                     points.Add(newPoint);
  863.                 }
  864.                 while (iter < 1);
  865.             }
  866.             return points.ToArray();
  867.         }
  868.  
  869.         /// <returns>a line renderer in the shape of a spiraling sphere, spiraling about the Vector3.up axis</returns>
  870.         /// <param name="lineObj">Line object.</param>
  871.         /// <param name="radius">Radius.</param>
  872.         /// <param name="center">Center.</param>
  873.         /// <param name="rotation"></param>
  874.         /// <param name="color">Color.</param>
  875.         /// <param name="lineSize">LineSize.</param>
  876.         public static LineRenderer MakeSpiralSphere(ref GameObject lineObj, float radius = 1,
  877.             Vector3 center = default, Quaternion rotation = default, Color color = default, float lineSize = LINE_SIZE) {
  878.             Vector3[] vertices = CreateSpiralSphere(center, radius, rotation, 24, 3);
  879.             return Make(ref lineObj, vertices, vertices.Length, color, lineSize, lineSize);
  880.         }
  881.        
  882.         public static LineRenderer MakeArrow(ref GameObject lineObject, Vector3 start, Vector3 end,
  883.             Color color = default, float startSize = LINE_SIZE, float endSize = SAME_AS_START_SIZE, float arrowHeadSize = ARROW_SIZE) {
  884.             return MakeArrow(ref lineObject, new Vector3[] { start, end }, 2, color, startSize, endSize, arrowHeadSize);
  885.         }
  886.  
  887.         public static LineRenderer MakeArrow(ref GameObject lineObject, IList<Vector3> points, int pointCount,
  888.             Color color = default, float startSize = LINE_SIZE, float endSize = SAME_AS_START_SIZE,
  889.             float arrowHeadSize = ARROW_SIZE, Keyframe[] lineKeyFrames = null) {
  890.             LineRenderer lr = MakeLineRenderer(ref lineObject);
  891.             return MakeArrow(lr, points, pointCount, color, startSize, endSize, arrowHeadSize, lineKeyFrames);
  892.         }
  893.  
  894.         public static LineRenderer MakeArrow(LineRenderer lr, IList<Vector3> points, int pointCount,
  895.                 Color color = default, float startSize = LINE_SIZE, float endSize = SAME_AS_START_SIZE,
  896.                 float arrowHeadSize = ARROW_SIZE, Keyframe[] lineKeyFrames = null) {
  897.             Keyframe[] keyframes = CalculateArrowKeyframes(points, pointCount, out Vector3[] line, startSize, endSize, arrowHeadSize, lineKeyFrames);
  898.             Make(lr, line, line.Length, color, startSize, endSize);
  899.             lr.widthCurve = new AnimationCurve(keyframes);
  900.             return lr;
  901.         }
  902.  
  903.         public static Keyframe[] CalculateArrowKeyframes(IList<Vector3> points, int pointCount, out Vector3[] line,
  904.             float startSize = LINE_SIZE, float endSize = SAME_AS_START_SIZE, float arrowHeadSize = ARROW_SIZE, Keyframe[] lineKeyFrames = null) {
  905.             float arrowSize = endSize * arrowHeadSize;
  906.             int lastGoodIndex = 0;
  907.             Vector3 arrowheadBase = Vector3.zero;
  908.             const float distanceBetweenArrowBaseAndWidePoint = 1.0f / 512;
  909.             Vector3 delta, dir = Vector3.zero;
  910.             // find where, in the list of points, to place the arrowhead
  911.             float dist = 0;
  912.             int lastPoint = pointCount - 1;
  913.             for (int i = lastPoint; i > 0; --i) { // go backwards (from the pointy end)
  914.                 float d = Vector3.Distance(points[i], points[i - 1]);
  915.                 dist += d;
  916.                 // if the arrow direction hasn't been calculated and sufficient distance for the arrowhead has been passed
  917.                 if (dir == Vector3.zero && dist >= arrowSize) {
  918.                     // calculate w,here the arrowheadBase should be (requires 2 points) based on the direction of this segment
  919.                     lastGoodIndex = i - 1;
  920.                     delta = points[i] - points[i - 1];
  921.                     dir = delta.normalized;
  922.                     float extraFromLastGoodIndex = dist - arrowSize;
  923.                     arrowheadBase = points[lastGoodIndex] + dir * extraFromLastGoodIndex;
  924.                 }
  925.             }
  926.             // if the line is not long enough for an arrow head, make the whole thing an arrowhead
  927.             if (dist <= arrowSize) {
  928.                 line = new Vector3[] { points[0], points[lastPoint] };
  929.                 return new Keyframe[] { new Keyframe(0, arrowSize), new Keyframe(1, 0) };
  930.             }
  931.             delta = points[lastPoint] - arrowheadBase;
  932.             dir = delta.normalized;
  933.             Vector3 arrowheadWidest = arrowheadBase + dir * (dist * distanceBetweenArrowBaseAndWidePoint);
  934.             line = new Vector3[lastGoodIndex + 4];
  935.             for (int i = 0; i <= lastGoodIndex; i++) {
  936.                 line[i] = points[i];
  937.             }
  938.             line[lastGoodIndex + 3] = points[lastPoint];
  939.             line[lastGoodIndex + 2] = arrowheadWidest;
  940.             line[lastGoodIndex + 1] = arrowheadBase;
  941.             Keyframe[] keyframes;
  942.             float arrowHeadBaseStart = 1 - arrowSize / dist;
  943.             float arrowHeadBaseWidest = 1 - (arrowSize / dist - distanceBetweenArrowBaseAndWidePoint);
  944.             if (lineKeyFrames == null) {
  945.                 keyframes = new Keyframe[] {
  946.                     new Keyframe(0, startSize), new Keyframe(arrowHeadBaseStart, endSize),
  947.                     new Keyframe(arrowHeadBaseWidest, arrowSize), new Keyframe(1, 0)
  948.                 };
  949.             } else {
  950.                 // count how many there are after arrowHeadBaseStart.
  951.                 int validCount = lineKeyFrames.Length;
  952.                 for (int i = 0; i < lineKeyFrames.Length; ++i) {
  953.                     float t = lineKeyFrames[i].time;
  954.                     if (t > arrowHeadBaseStart) { validCount = i; break; }
  955.                 }
  956.                 // those are irrelevant now. they'll be replaced by the 3 extra points
  957.                 keyframes = new Keyframe[validCount + 3];
  958.                 for (int i = 0; i < validCount; ++i) { keyframes[i] = lineKeyFrames[i]; }
  959.                 keyframes[validCount + 0] = new Keyframe(arrowHeadBaseStart, endSize);
  960.                 keyframes[validCount + 1] = new Keyframe(arrowHeadBaseWidest, arrowSize);
  961.                 keyframes[validCount + 2] = new Keyframe(1, 0);
  962.             }
  963.             return keyframes;
  964.         }
  965.  
  966.         public static LineRenderer MakeArrowBothEnds(ref GameObject lineObject, Vector3 start, Vector3 end,
  967.             Color color = default, float startSize = LINE_SIZE, float endSize = SAME_AS_START_SIZE, float arrowHeadSize = ARROW_SIZE) {
  968.             return MakeArrowBothEnds(ref lineObject, new Vector3[] { end, start }, 2, color, startSize, endSize, arrowHeadSize);
  969.         }
  970.         public static LineRenderer MakeArrowBothEnds(ref GameObject lineObject, IList<Vector3> points, int pointCount,
  971.             Color color = default, float startSize = LINE_SIZE, float endSize = SAME_AS_START_SIZE, float arrowHeadSize = ARROW_SIZE) {
  972.             LineRenderer lr = MakeArrow(ref lineObject, points, pointCount, color, startSize, endSize, arrowHeadSize, null);
  973.             ReverseLineInternal(ref lr);
  974.             Vector3[] p = new Vector3[lr.positionCount];
  975.             lr.GetPositions(p);
  976.             lr = MakeArrow(ref lineObject, p, p.Length, color, endSize, startSize, arrowHeadSize, lr.widthCurve.keys);
  977.             ReverseLineInternal(ref lr);
  978.             return lr;
  979.         }
  980.         public static LineRenderer ReverseLineInternal(ref LineRenderer lr) {
  981.             Vector3[] p = new Vector3[lr.positionCount];
  982.             lr.GetPositions(p);
  983.             Array.Reverse(p);
  984.             lr.SetPositions(p);
  985.             AnimationCurve widthCurve = lr.widthCurve;
  986.             if (widthCurve != null && widthCurve.length > 1) {
  987.                 Keyframe[] kf = new Keyframe[widthCurve.keys.Length];
  988.                 Keyframe[] okf = widthCurve.keys;
  989.                 Array.Copy(okf, kf, okf.Length); //for(int i = 0; i<kf.Length; ++i) { kf[i]=okf[i]; }
  990.                 Array.Reverse(kf);
  991.                 for (int i = 0; i < kf.Length; ++i) { kf[i].time = 1 - kf[i].time; }
  992.                 lr.widthCurve = new AnimationCurve(kf);
  993.             }
  994.             return lr;
  995.         }
  996.  
  997.         public static LineRenderer MakeArcArrow(ref GameObject lineObj,
  998.             float angle, int pointCount, Vector3 arcPlaneNormal = default, Vector3 firstPoint = default,
  999.             Vector3 center = default, Color color = default, float startSize = LINE_SIZE, float endSize = SAME_AS_START_SIZE, float arrowHeadSize = ARROW_SIZE) {
  1000.             if (arcPlaneNormal == default) { arcPlaneNormal = Vector3.up; }
  1001.             if (center == default && firstPoint == default) { firstPoint = Vector3.right; }
  1002.             Vector3[] points = null;
  1003.             WriteArc(ref points, pointCount, arcPlaneNormal, firstPoint, angle, center);
  1004.             return MakeArrow(ref lineObj, points, pointCount, color, startSize, endSize, arrowHeadSize);
  1005.         }
  1006.  
  1007.         public static LineRenderer MakeArcArrowBothEnds(ref GameObject lineObj,
  1008.             float angle, int pointCount, Vector3 arcPlaneNormal = default, Vector3 firstPoint = default,
  1009.             Vector3 center = default, Color color = default, float startSize = LINE_SIZE, float endSize = SAME_AS_START_SIZE, float arrowHeadSize = ARROW_SIZE) {
  1010.             LineRenderer lr = MakeArcArrow(ref lineObj, angle, pointCount, arcPlaneNormal, firstPoint, center, color, startSize, endSize, arrowHeadSize);
  1011.             ReverseLineInternal(ref lr);
  1012.             Vector3[] p = new Vector3[lr.positionCount];
  1013.             lr.GetPositions(p);
  1014.             lr = MakeArrow(ref lineObj, p, p.Length, color, endSize, startSize, arrowHeadSize, lr.widthCurve.keys);
  1015.             ReverseLineInternal(ref lr);
  1016.             return lr;
  1017.         }
  1018.  
  1019.         public static LineRenderer MakeArcArrow(ref GameObject lineObject, Vector3 start, Vector3 end, Color color = default, float angle = 90, Vector3 upNormal = default,
  1020.             float startSize = LINE_SIZE, float endSize = SAME_AS_START_SIZE, float arrowHeadSize = ARROW_SIZE, int pointCount = 0) {
  1021.             Vector3[] arc;
  1022.             if (end == start || Mathf.Abs(angle) >= 360) {
  1023.                 arc = new Vector3[] { start, end };
  1024.             } else {
  1025.                 if (upNormal == default) { upNormal = Vector3.up; }
  1026.                 if (pointCount == 0) { pointCount = Mathf.Max((int)(angle * 24 / 180) + 1, 2); }
  1027.                 arc = new Vector3[pointCount];
  1028.                 Vector3 delta = end - start;
  1029.                 float dist = delta.magnitude;
  1030.                 Vector3 dir = delta / dist;
  1031.                 Vector3 right = Vector3.Cross(upNormal, dir).normalized;
  1032.                 WriteArc(ref arc, pointCount, right, -upNormal, angle);
  1033.                 Vector3 arcDelta = arc[arc.Length - 1] - arc[0];
  1034.                 float arcDist = arcDelta.magnitude;
  1035.                 float angleDiff = Vector3.Angle(arcDelta / arcDist, delta / dist);
  1036.                 Quaternion turn = Quaternion.AngleAxis(angleDiff, right);
  1037.                 float ratio = dist / arcDist;
  1038.                 for (int i = 0; i < arc.Length; ++i) { arc[i] = (turn * arc[i]) * ratio; }
  1039.                 Vector3 offset = start - arc[0];
  1040.                 for (int i = 0; i < arc.Length; ++i) { arc[i] += offset; }
  1041.             }
  1042.             return MakeArrow(ref lineObject, arc, arc.Length, color, startSize, endSize, arrowHeadSize);
  1043.         }
  1044.  
  1045.         public static void MakeQuaternion(ref GameObject axisObj, Wire[] childWire, Vector3 axis, float angle,
  1046.             Vector3 position = default, Color color = default, Quaternion orientation = default,
  1047.             int arcPoints = -1, float lineSize = LINE_SIZE, float arrowHeadSize = ARROW_SIZE, Vector3[] startPoint = null) {
  1048.             if (childWire.Length != startPoint.Length) { throw new Exception("angleObj and startPoint should be parallel arrays"); }
  1049.             while (angle >= 180) { angle -= 360; }
  1050.             while (angle < -180) { angle += 360; }
  1051.             Vector3 axisRotated = orientation * axis;
  1052.             MakeArrow(ref axisObj, position - axisRotated, position + axisRotated, color, lineSize, lineSize, arrowHeadSize);
  1053.             for (int i = 0; i < childWire.Length; ++i) {
  1054.                 Wire aObj = childWire[i];
  1055.                 aObj.Arc(angle, axisRotated, startPoint[i], position, color, End.Arrow, arcPoints, lineSize);
  1056.                 //MakeArcArrow(ref aObj, angle, arcPoints, axisRotated, startPoint[i], position, color, lineSize, lineSize, arrowHeadSize);
  1057.                 childWire[i] = aObj;
  1058.             }
  1059.         }
  1060.  
  1061.         // code to draw quaternion with a single line renderer is commented out because it doesn't work right yet.
  1062.         ///// <summary>draws a quaternion with a single line renderer. the visual is very buggy for small angles. Keyframes are finicky.</summary>
  1063.         ///// <param name="lineObject"></param>
  1064.         ///// <param name="axis"></param>
  1065.         ///// <param name="angle"></param>
  1066.         ///// <param name="color"></param>
  1067.         ///// <param name="start"></param>
  1068.         ///// <param name="forwardNormal"></param>
  1069.         ///// <param name="startSize"></param>
  1070.         ///// <param name="endSize"></param>
  1071.         ///// <param name="arrowHeadSize"></param>
  1072.         ///// <param name="pointCount"></param>
  1073.         ///// <returns></returns>
  1074.         //public static LineRenderer MakeQuaternion(ref GameObject lineObject, Vector3 axis, float angle = 90, Color color = default,
  1075.         //  Vector3 start = default, Vector3 forwardNormal = default,
  1076.         //  float startSize = LINE_SIZE, float endSize = LINE_SIZE, float arrowHeadSize = ARROW_SIZE, int pointCount = 0) {
  1077.         //  Vector3[] arc;
  1078.         //  if(forwardNormal == default) {
  1079.         //      if(axis == Vector3.forward || axis == Vector3.back) { forwardNormal = Vector3.up; }
  1080.         //      else { forwardNormal = Vector3.forward; }
  1081.         //  }
  1082.         //  if(pointCount == 0) { pointCount = Mathf.Max((int)((angle * 24) / 180)+1, 2); }
  1083.         //  //Debug.Log(angle + " " + pointCount);
  1084.         //  arc = new Vector3[pointCount+6];
  1085.         //  Vector3 right = Vector3.Cross(forwardNormal, axis).normalized;
  1086.         //  WriteArc(ref arc, pointCount, axis, right, angle);
  1087.         //  Array.Reverse(arc, 0, pointCount);
  1088.         //  for(int i = 0; i < arc.Length; ++i) {
  1089.         //      arc[i] = arc[i] * .5f + start;
  1090.         //  }
  1091.         //  const float epsilon = (1f / 256);
  1092.         //  arc[pointCount + 0] = arc[pointCount - 1] + (arc[pointCount-1]-arc[pointCount-2]).normalized * epsilon;
  1093.         //  arc[pointCount + 1] = start;
  1094.         //  arc[pointCount + 2] = start - axis / 4;
  1095.         //  arc[pointCount + 3] = start - axis /3;
  1096.         //  arc[pointCount + 4] = start - axis /2;
  1097.         //  arc[pointCount + 5] = start + axis /2;
  1098.         //  LineRenderer lr = MakeArrowBothEnds(ref lineObject, arc, arc.Length, color, startSize, endSize, arrowHeadSize);
  1099.         //  // calculate where to lower the width of the bridge connecting the axis and angle
  1100.         //  float connectionSegmentStart = 0, connectionSegmentEnd = 0, totalDistance = 0;
  1101.         //  int angleBeginIndex = arc.Length - 6;
  1102.         //  int axisEndIndex = arc.Length - 3;
  1103.         //  for(int i = 1; i < angleBeginIndex; ++i) {
  1104.         //      float d = Vector3.Distance(arc[i], arc[i-1]);
  1105.         //      connectionSegmentStart += d;
  1106.         //  }
  1107.         //  connectionSegmentEnd += connectionSegmentStart;
  1108.         //  for(int i = angleBeginIndex; i < axisEndIndex; ++i) {
  1109.         //      float d = Vector3.Distance(arc[i], arc[i-1]);
  1110.         //      connectionSegmentEnd += d;
  1111.         //  }
  1112.         //  totalDistance += connectionSegmentEnd;
  1113.         //  for(int i = axisEndIndex; i < arc.Length; ++i) {
  1114.         //      float d = Vector3.Distance(arc[i], arc[i-1]);
  1115.         //      totalDistance += d;
  1116.         //  }
  1117.         //  //Debug.Log(connectionSegmentStart+" < "+ connectionSegmentEnd+" < "+ totalDistance);
  1118.         //  AnimationCurve ac = lr.widthCurve;
  1119.         //  Keyframe endOfArc = new Keyframe((connectionSegmentStart - epsilon) / totalDistance, startSize);//, 0, -float.MaxValue);
  1120.         //  Keyframe beginningOfBridge = new Keyframe((connectionSegmentStart + epsilon) / totalDistance, 0, -float.MaxValue, 0);
  1121.         //  Keyframe endOfBridge = new Keyframe(connectionSegmentEnd / totalDistance, 0, 0, float.MaxValue);
  1122.         //  Keyframe beginningOfAxis = new Keyframe(connectionSegmentEnd / totalDistance + epsilon, endSize);//, float.MaxValue, 0);
  1123.         //  ac.AddKey(endOfArc);
  1124.         //  ac.AddKey(beginningOfBridge);
  1125.         //  ac.AddKey(endOfBridge);
  1126.         //  ac.AddKey(beginningOfAxis);
  1127.         //  lr.widthCurve = ac;
  1128.         //  return lr;
  1129.         //}
  1130.         //public static LineRenderer MakeQuaternion(ref GameObject lineObject, Quaternion q, Color color = default,
  1131.         //  Vector3 start = default, Vector3 forwardNormal = default,
  1132.         //  float startSize = LINE_SIZE, float endSize = LINE_SIZE, float arrowHeadSize = ARROW_SIZE, int pointCount = 0) {
  1133.         //  Vector3 axis;
  1134.         //  float angle;
  1135.         //  q.ToAngleAxis(out angle, out axis);
  1136.         //  return MakeQuaternion(ref lineObject, axis, angle, color, start, forwardNormal, startSize, endSize, arrowHeadSize, pointCount);
  1137.         //}
  1138.  
  1139.         private static int CartesianPlaneChildCount(float extents, float increment, out int linesPerDomainHalf) {
  1140.             linesPerDomainHalf = (int) (extents / increment);
  1141.             if (Mathf.Abs(linesPerDomainHalf - (extents / increment)) < Lines.TOLERANCE) --linesPerDomainHalf;
  1142.             return 2 + linesPerDomainHalf * 4;
  1143.         }
  1144.         public static void MakeCartesianPlane(Vector3 center, Vector3 up, Vector3 right, Wire[] wires, Color color = default, float lineWidth = LINE_SIZE,
  1145.             float size = .5f, float increments = 0.125f, Vector3 offset = default) {
  1146.             // prep data structures
  1147.             int wireCount = CartesianPlaneChildCount(size, increments, out int thinLines);
  1148.             while(wires.Length < wireCount){ throw new Exception($"can't make {wireCount} wires with {wires.Length} slots"); }
  1149.             Vector3[] endPoints = new Vector3[2];
  1150.             // prep math
  1151.             Vector3 minX = right * -size, maxX = right * size;
  1152.             Vector3 minY = up * -size, maxY = up * size;
  1153.             Vector3 p = center + offset;
  1154.             int index = 1;
  1155.             float thinLineWidth = lineWidth / 4;
  1156.             // draw the X and Y axis
  1157.             endPoints[0] = p + minX; endPoints[1] = p + maxX; wires[0].Line(endPoints, color, End.Arrow, lineWidth);
  1158.             endPoints[0] = p + minY; endPoints[1] = p + maxY; wires[1].Line(endPoints, color, End.Arrow, lineWidth);
  1159.             // positiveY
  1160.             for (int i = 0; i < thinLines; ++i) {
  1161.                 Vector3 delta = up * (increments * (i+1));
  1162.                 endPoints[0] = p + minX + delta; endPoints[1] = p + maxX + delta;
  1163.                 wires[++index].Line(endPoints, color, End.Normal, thinLineWidth);
  1164.             }
  1165.             // negativeY
  1166.             for (int i = 0; i < thinLines; ++i) {
  1167.                 Vector3 delta =-up * (increments * (i+1));
  1168.                 endPoints[0] = p + minX + delta; endPoints[1] = p + maxX + delta;
  1169.                 wires[++index].Line(endPoints, color, End.Normal, thinLineWidth);
  1170.             }
  1171.             // positiveX
  1172.             for (int i = 0; i < thinLines; ++i) {
  1173.                 Vector3 delta = right * (increments * (i+1));
  1174.                 endPoints[0] = p + minY + delta; endPoints[1] = p + maxY + delta;
  1175.                 wires[++index].Line(endPoints, color, End.Normal, thinLineWidth);
  1176.             }
  1177.             // negativeX
  1178.             for (int i = 0; i < thinLines; ++i) {
  1179.                 Vector3 delta =-right * (increments * (i+1));
  1180.                 endPoints[0] = p + minY + delta; endPoints[1] = p + maxY + delta;
  1181.                 wires[++index].Line(endPoints, color, End.Normal, thinLineWidth);
  1182.             }
  1183.         }
  1184.  
  1185.         public static void WriteRectangle(Vector3[] out_corner, Vector3 origin, Quaternion rotation, Vector3 halfSize, Vector2 position2D) {
  1186.             out_corner[0] = (position2D + new Vector2(-halfSize.x, halfSize.y));
  1187.             out_corner[1] = (position2D + new Vector2( halfSize.x, halfSize.y));
  1188.             out_corner[2] = (position2D + new Vector2( halfSize.x,-halfSize.y));
  1189.             out_corner[3] = (position2D + new Vector2(-halfSize.x,-halfSize.y));
  1190.             for (int i = 0; i < 4; ++i) { out_corner[i] = rotation * out_corner[i] + origin; }
  1191.         }
  1192.         public static void MakeRectangle(Wire[] wires, Vector3 origin, Vector2 position2D, Vector2 halfSize, float lineSize, Color a_color, Quaternion rotation) {
  1193.             Vector3[] corners = new Vector3[4];
  1194.             WriteRectangle(corners, origin, rotation, halfSize, position2D);
  1195.             for (int i = 0; i < corners.Length; ++i) {
  1196.                 Vector3 a = corners[i];
  1197.                 Vector3 b = corners[(i + 1) % corners.Length];
  1198.                 wires[i].Line(a, b, a_color, lineSize).NumCapVertices = 4;
  1199.             }
  1200.         }
  1201.        
  1202.         /// <param name="rectTransform">rectangle to draw on. should have RawImage (or no Renderer at all)</param>
  1203.         public static Texture2D GetRawImageTexture(RectTransform rectTransform) {
  1204.             UnityEngine.UI.RawImage rImg = rectTransform.GetComponent<UnityEngine.UI.RawImage>();
  1205.             if (rImg == null) { rImg = rectTransform.gameObject.AddComponent<UnityEngine.UI.RawImage>(); }
  1206.             if (rImg == null) { throw new System.Exception("unable to create a RawImage on " + rectTransform.name + ", does it already have another renderer?"); }
  1207.             Texture2D img = rImg.texture as Texture2D;
  1208.             if (img == null)
  1209.             {
  1210.                 Rect r = rectTransform.rect;
  1211.                 img = new Texture2D((int)r.width, (int)r.height);
  1212.                 img.SetPixels(0, 0, (int)r.width, (int)r.height, new Color[(int)(r.width * r.height)]); // set pixels to the default color, which is clear
  1213.                 rImg.texture = img;
  1214.             }
  1215.             return img;
  1216.         }
  1217.        
  1218.         /// <param name="rectTransform">rectangle to draw on. should have RawImage (or no Renderer at all)</param>
  1219.         /// <param name="color">what color to fill. if no value given, will set entire texture to <see cref="UnityEngine.Color.clear"/></param>
  1220.         public static void FillTexture(RectTransform rectTransform, Color color = default(Color)) {
  1221.             Texture2D tex = GetRawImageTexture(rectTransform);
  1222.             Color[] colors = new Color[tex.height*tex.width];
  1223.             if(color != default(Color)) {
  1224.                 for (int i = 0; i < colors.Length; ++i) { colors[i] = color; }
  1225.             }
  1226.             tex.SetPixels(colors);
  1227.         }
  1228.    
  1229.         /// <param name="rectTransform">rectangle to draw on. should have RawImage (or no Renderer at all)</param>
  1230.         /// <param name="start">(0,0) is lower left</param>
  1231.         /// <param name="end"></param>
  1232.         /// <param name="color"></param>
  1233.         public static void DrawLine(RectTransform rectTransform, Vector2 start, Vector2 end, Color color) {
  1234.             DrawLine(rectTransform, (int)start.x, (int)start.y, (int)end.x, (int)end.y, color);
  1235.         }
  1236.        
  1237.         /// <param name="rectTransform">rectangle to draw on. should have RawImage (or no Renderer at all)</param>
  1238.         /// <param name="x0">0 is left</param>
  1239.         /// <param name="y0">0 is bottom</param>
  1240.         /// <param name="x1"></param>
  1241.         /// <param name="y1"></param>
  1242.         /// <param name="col"></param>
  1243.         public static void DrawLine(RectTransform rectTransform, int x0, int y0, int x1, int y1, Color col) {
  1244.             Texture2D img = GetRawImageTexture(rectTransform);
  1245.             DrawLine(img, x0, y0, x1, y1, col);
  1246.             img.Apply();
  1247.         }
  1248.    
  1249.         /// <summary>draws an un-aliased single-pixel line on the given texture with the given color</summary>ne
  1250.         /// <param name="texture"></param>
  1251.         /// <param name="x0">0 is left</param>
  1252.         /// <param name="y0">0 is bottom</param>
  1253.         /// <param name="x1"></param>
  1254.         /// <param name="y1"></param>
  1255.         /// <param name="color"></param>
  1256.         public static void DrawLine(Texture2D texture, int x0, int y0, int x1, int y1, Color color) {
  1257.             int dy = y1 - y0;
  1258.             int dx = x1 - x0;
  1259.             int stepY, stepX;
  1260.             if (dy < 0) { dy = -dy; stepY = -1; } else { stepY = 1; }
  1261.             if (dx < 0) { dx = -dx; stepX = -1; } else { stepX = 1; }
  1262.             dy <<= 1;
  1263.             dx <<= 1;
  1264.             float fraction;
  1265.             texture.SetPixel(x0, y0, color);
  1266.             if (dx > dy) {
  1267.                 fraction = dy - (dx >> 1);
  1268.                 while (Mathf.Abs(x0 - x1) > 1) {
  1269.                     if (fraction >= 0) {
  1270.                         y0 += stepY;
  1271.                         fraction -= dx;
  1272.                     }
  1273.                     x0 += stepX;
  1274.                     fraction += dy;
  1275.                     texture.SetPixel(x0, y0, color);
  1276.                 }
  1277.             } else {
  1278.                 fraction = dx - (dy >> 1);
  1279.                 while (Mathf.Abs(y0 - y1) > 1) {
  1280.                     if (fraction >= 0) {
  1281.                         x0 += stepX;
  1282.                         fraction -= dy;
  1283.                     }
  1284.                     y0 += stepY;
  1285.                     fraction += dx;
  1286.                     texture.SetPixel(x0, y0, color);
  1287.                 }
  1288.             }
  1289.         }
  1290.     }
  1291. }
RAW Paste Data