mvaganov

Lines.cs

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