Unity Native Spline

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Linq;
using UnityEngine.Serialization;

[AddComponentMenu("Miscellaneous/Bezier Spline")]
public class Bezier3DSpline : MonoBehaviour{

	public int KnotCount { get { return curves.Length+(closed?0:1); } }
	public int CurveCount { get { return curves.Length; } }
    /// <summary> Interpolation steps per curve </summary>
    public int cacheDensity { get { return _cacheDensity; } }
    [SerializeField] protected int _cacheDensity = 60;
    /// <summary> Whether the end of the spline connects to the start of the spline </summary>
    public bool closed { get { return _closed; } }
    [SerializeField] protected bool _closed = false;
    /// <summary> Sum of all curve lengths </summary>
    public float totalLength { get { return _totalLength; } }
    [SerializeField] protected float _totalLength = 2.370671f;
    /// <summary> Curves of the spline </summary>
    [SerializeField] protected Bezier3DCurve[] curves = new Bezier3DCurve[] { new Bezier3DCurve( new Vector3(-1,0,0), new Vector3(1,0,1), new Vector3(-1,0,-1), new Vector3(1,0,0), 60)};
    /// <summary> Automatic knots don't have handles. Instead they have a percentage and adjust their handles accordingly. A percentage of 0 indicates that this is not automatic </summary>
    [SerializeField] protected List<float> autoKnot = new List<float>() { 0, 0 };
    [SerializeField] protected List<NullableQuaternion> orientations = new List<NullableQuaternion>() { new NullableQuaternion(null), new NullableQuaternion(null) };
    [SerializeField] protected Vector3[] tangentCache = new Vector3[0];

    #region Public methods

    #region Public: get

    public float DistanceToTime(float dist) {
        float t = 0f;
        for (int i = 0; i < CurveCount; i++) {
            if (curves[i].length < dist) {
                dist -= curves[i].length;
                t += 1f / CurveCount;
            }
            else {
                t += curves[i].Dist2Time(dist) / CurveCount;
                return t;
            }
        }
        return 1f;
    }

    /// <summary> Get <see cref="Bezier3DCurve"/> by index </summary>
    public Bezier3DCurve GetCurve(int i) {
        if (i >= CurveCount || i < 0) throw new System.IndexOutOfRangeException("Cuve index " + i + " out of range");
        return curves[i];
    }

    /// <summary> Return <see cref="Knot"/> info in local coordinates </summary>
    public Knot GetKnot(int i) {
        if (i == 0) {
            if (closed) return new Knot(curves[0].a, curves[CurveCount - 1].c, curves[0].b, autoKnot[i], orientations[i].NullableValue);
            else return new Knot(curves[0].a, Vector3.zero, curves[0].b, autoKnot[i], orientations[i].NullableValue);
        }
        else if (i == CurveCount) {
            return new Knot(curves[i - 1].d, curves[i - 1].c, Vector3.zero, autoKnot[i], orientations[i].NullableValue);
        }
        else {
            return new Knot(curves[i].a, curves[i - 1].c, curves[i].b, autoKnot[i], orientations[i].NullableValue);
        }
    }

    #region Public get: Forward
    /// <summary> Return forward vector at set distance along the <see cref="Bezier3DSpline"/>. </summary>
    public Vector3 GetForward(float dist) {
        return transform.TransformDirection(GetForwardLocal(dist));
    }

    /// <summary> Return forward vector at set distance along the <see cref="Bezier3DSpline"/> in local coordinates. </summary>
    public Vector3 GetForwardLocal(float dist) {
        Bezier3DCurve curve = GetCurveDistance(dist, out dist);
        return curve.GetForward(curve.Dist2Time(dist));
    }

    /// <summary> Return forward vector at set distance along the <see cref="Bezier3DSpline"/>. Uses approximation. </summary>
    public Vector3 GetForwardFast(float dist) {
        return transform.TransformDirection(GetForwardLocalFast(dist));
    }

    /// <summary> Return forward vector at set distance along the <see cref="Bezier3DSpline"/> in local coordinates. Uses approximation. </summary>
    public Vector3 GetForwardLocalFast(float dist) {
        Bezier3DCurve curve = GetCurveDistance(dist, out dist);
        return curve.GetForwardFast(curve.Dist2Time(dist));
    }
    #endregion

    #region Public get: Up
    /// <summary> Return up vector at set distance along the <see cref="Bezier3DSpline"/>. </summary>
    public Vector3 GetUp(float dist) {
        return GetUp(dist, GetForward(dist), false);
    }

    /// <summary> Return up vector at set distance along the <see cref="Bezier3DSpline"/> in local coordinates. </summary>
    public Vector3 GetUpLocal(float dist) {
        return GetUp(dist, GetForward(dist), true);
    }
    #endregion

    #region Public get: Point
    /// <summary> Return up vector at set distance along the <see cref="Bezier3DSpline"/>. </summary>
    public Vector3 GetPoint(float dist) {
        Bezier3DCurve curve = GetCurveDistance(dist, out dist);
        return transform.TransformPoint(curve.GetPoint(curve.Dist2Time(dist)));
    }

    /// <summary> Return point at lerped position where 0 = start, 1 = end </summary>
    public Vector3 GetPointLocal(float dist) {
        Bezier3DCurve curve = GetCurveDistance(dist, out dist);
        return curve.GetPoint(curve.Dist2Time(dist));
    }
    #endregion

    #region Public get: Orientation
    public Quaternion GetOrientation(float dist) {
        Vector3 forward = GetForward(dist);
        Vector3 up = GetUp(dist, forward, false);
        if (forward.sqrMagnitude != 0) return Quaternion.LookRotation(forward, up);
        else return Quaternion.identity;
    }

    public Quaternion GetOrientationFast(float dist) {
        Vector3 forward = GetForwardFast(dist);
        Vector3 up = GetUp(dist, forward, false);
        if (forward.sqrMagnitude != 0) return Quaternion.LookRotation(forward, up);
        else return Quaternion.identity;
    }

    public Quaternion GetOrientationLocal(float dist) {
        Vector3 forward = GetForwardLocal(dist);
        Vector3 up = GetUp(dist, forward, true);
        if (forward.sqrMagnitude != 0) return Quaternion.LookRotation(forward, up);
        else return Quaternion.identity;
    }

    public Quaternion GetOrientationLocalFast(float dist) {
        Vector3 forward = GetForwardLocalFast(dist);
        Vector3 up = GetUp(dist, forward, true);
        if (forward.sqrMagnitude != 0) return Quaternion.LookRotation(forward, up);
        else return Quaternion.identity;
    }
    #endregion

    #endregion

    #region Public: Set
    /// <summary> Setting spline to closed will generate an extra curve, connecting end point to start point </summary>
    public void SetClosed(bool closed) {
        if (closed != _closed) {
            _closed = closed;
            if (closed) {
                List<Bezier3DCurve> curveList = new List<Bezier3DCurve>(curves);
                curveList.Add(new Bezier3DCurve(curves[CurveCount - 1].d, -curves[CurveCount - 1].c, -curves[0].b, curves[0].a, cacheDensity));
                curves = curveList.ToArray();
            }
            else {
                List<Bezier3DCurve> curveList = new List<Bezier3DCurve>(curves);
                curveList.RemoveAt(CurveCount - 1);
                curves = curveList.ToArray();
            }
            _totalLength = GetTotalLength();
        }
    }

    /// <summary> Recache all individual curves with new step amount </summary> 
    /// <param name="density"> Number of steps per curve </param>
    public void SetCacheDensity(int steps) {
        _cacheDensity = steps;
        for (int i = 0; i < CurveCount; i++) {
            curves[i] = new Bezier3DCurve(curves[i].a, curves[i].b, curves[i].c, curves[i].d, _cacheDensity);
        }
        _totalLength = GetTotalLength();
    }

    public void RemoveKnot(int i) {
        if (i == 0) {
            Knot knot = GetKnot(1);

            List<Bezier3DCurve> curveList = new List<Bezier3DCurve>(curves);
            curveList.RemoveAt(0);
            curves = curveList.ToArray();

            autoKnot.RemoveAt(0);
            orientations.RemoveAt(0);

            SetKnot(0, knot);
        }
        else if (i == CurveCount) {

            List<Bezier3DCurve> curveList = new List<Bezier3DCurve>(curves);
            curveList.RemoveAt(i - 1);
            curves = curveList.ToArray();

            autoKnot.RemoveAt(i);
            orientations.RemoveAt(i);

            if (autoKnot[KnotCount - 1] != 0) SetKnot(KnotCount - 1, GetKnot(KnotCount - 1));
        }
        else {
            int preCurveIndex, postCurveIndex;
            GetCurveIndicesForKnot(i, out preCurveIndex, out postCurveIndex);

            Bezier3DCurve curve = new Bezier3DCurve(curves[preCurveIndex].a, curves[preCurveIndex].b, curves[postCurveIndex].c, curves[postCurveIndex].d, cacheDensity);

            curves[preCurveIndex] = curve;

            List<Bezier3DCurve> curveList = new List<Bezier3DCurve>(curves);
            curveList.RemoveAt(postCurveIndex);
            curves = curveList.ToArray();

            autoKnot.RemoveAt(i);
            orientations.RemoveAt(i);

            int preKnotIndex, postKnotIndex;
            GetKnotIndicesForKnot(i, out preKnotIndex, out postKnotIndex);

            SetKnot(preKnotIndex, GetKnot(preKnotIndex));
        }
    }

    public void AddKnot(Knot knot) {
        Bezier3DCurve curve = new Bezier3DCurve(curves[CurveCount - 1].d, -curves[CurveCount - 1].c, knot.handleIn, knot.position, cacheDensity);

        List<Bezier3DCurve> curveList = new List<Bezier3DCurve>(curves);
        curveList.Add(curve);
        curves = curveList.ToArray();

        autoKnot.Add(knot.auto);
        orientations.Add(knot.orientation);
        SetKnot(KnotCount - 1, knot);
    }

    public void InsertKnot(int i, Knot knot) {
        Bezier3DCurve curve;
        if (i == 0) curve = new Bezier3DCurve(knot.position, knot.handleOut, -curves[0].b, curves[0].a, cacheDensity);
        else if (i == CurveCount) curve = GetCurve(i - 1);
        else curve = GetCurve(i);

        List<Bezier3DCurve> curveList = new List<Bezier3DCurve>(curves);
        curveList.Insert(i, curve);
        curves = curveList.ToArray();

        autoKnot.Insert(i, knot.auto);
        orientations.Insert(i, knot.orientation);
        SetKnot(i, knot);
    }

    /// <summary> Set Knot info in local coordinates </summary>
    public void SetKnot(int i, Knot knot) {
        //If knot is set to auto, adjust handles accordingly
        orientations[i] = knot.orientation;
        autoKnot[i] = knot.auto;
        if (knot.auto != 0) AutomateHandles(i, ref knot);

        //Automate knots around this knot
        int preKnotIndex, postKnotIndex;
        GetKnotIndicesForKnot(i, out preKnotIndex, out postKnotIndex);

        Knot preKnot = new Knot();
        if (preKnotIndex != -1) {
            preKnot = GetKnot(preKnotIndex);
            if (preKnot.auto != 0) {
                int preKnotPreCurveIndex, preKnotPostCurveIndex;
                GetCurveIndicesForKnot(preKnotIndex, out preKnotPreCurveIndex, out preKnotPostCurveIndex);
                if (preKnotPreCurveIndex != -1) {
                    AutomateHandles(preKnotIndex, ref preKnot, curves[preKnotPreCurveIndex].a, knot.position);
                    curves[preKnotPreCurveIndex] = new Bezier3DCurve(curves[preKnotPreCurveIndex].a, curves[preKnotPreCurveIndex].b, preKnot.handleIn, preKnot.position, cacheDensity);
                }
                else {
                    AutomateHandles(preKnotIndex, ref preKnot, Vector3.zero, knot.position);
                }
            }
        }

        Knot postKnot = new Knot();
        if (postKnotIndex != -1) {
            postKnot = GetKnot(postKnotIndex);
            if (postKnot.auto != 0) {
                int postKnotPreCurveIndex, postKnotPostCurveIndex;
                GetCurveIndicesForKnot(postKnotIndex, out postKnotPreCurveIndex, out postKnotPostCurveIndex);
                if (postKnotPostCurveIndex != -1) {
                    AutomateHandles(postKnotIndex, ref postKnot, knot.position, curves[postKnotPostCurveIndex].d);
                    curves[postKnotPostCurveIndex] = new Bezier3DCurve(postKnot.position, postKnot.handleOut, curves[postKnotPostCurveIndex].c, curves[postKnotPostCurveIndex].d, cacheDensity);
                }
                else {
                    AutomateHandles(postKnotIndex, ref postKnot, knot.position, Vector3.zero);
                }
            }
        }

        //Get the curve indices in direct contact with knot
        int preCurveIndex, postCurveIndex;
        GetCurveIndicesForKnot(i, out preCurveIndex, out postCurveIndex);

        //Adjust curves in direct contact with the knot
        if (preCurveIndex != -1) curves[preCurveIndex] = new Bezier3DCurve(preKnot.position, preKnot.handleOut, knot.handleIn, knot.position, cacheDensity);
        if (postCurveIndex != -1) curves[postCurveIndex] = new Bezier3DCurve(knot.position, knot.handleOut, postKnot.handleIn, postKnot.position, cacheDensity);

        _totalLength = GetTotalLength();

    }

    /// <summary> Flip the spline </summary>
    public void Flip() {
        Bezier3DCurve[] curves = new Bezier3DCurve[CurveCount];
        for (int i = 0; i < CurveCount; i++) {
            curves[CurveCount - 1 - i] = new Bezier3DCurve(this.curves[i].d, this.curves[i].c, this.curves[i].b, this.curves[i].a, cacheDensity);
        }
        this.curves = curves;
        autoKnot.Reverse();
        orientations.Reverse();
    }
    #endregion

    #endregion

    public struct Knot {
        public Vector3 position;
        public Vector3 handleIn;
        public Vector3 handleOut;
        public float auto;
        public Quaternion? orientation;

        /// <summary> Constructor </summary>
        /// <param name="position">Position of the knot local to spline transform</param>
        /// <param name="handleIn">Left handle position local to knot position</param>
        /// <param name="handleOut">Right handle position local to knot position</param>
        /// <param name="automatic">Any value above 0 will result in an automatically configured knot (ignoring handle inputs)</param>
        public Knot(Vector3 position, Vector3 handleIn, Vector3 handleOut, float automatic = 0f, Quaternion? orientation = null) {
            this.position = position;
            this.handleIn = handleIn;
            this.handleOut = handleOut;
            this.auto = automatic;
            this.orientation = orientation;
        }
    }

    #region Private methods
    private Vector3 GetUp(float dist, Vector3 tangent, bool local) {
        float t = DistanceToTime(dist);
        t *= CurveCount;

        Quaternion rot_a = Quaternion.identity, rot_b = Quaternion.identity;
        int t_a = 0, t_b = 0;

        //Find preceding rotation
        for (int i = Mathf.Min((int)t, CurveCount); i >= 0; i--) {
            i = (int)Mathf.Repeat(i, KnotCount - 1);
            if (orientations[i].HasValue) {
                rot_a = orientations[i].Value;
                rot_b = orientations[i].Value;
                t_a = i;
                t_b = i;
                break;
            }
        }
        //Find proceding rotation
        for (int i = Mathf.Max((int)t + 1, 0); i < orientations.Count; i++) {
            if (orientations[i].HasValue) {
                rot_b = orientations[i].Value;
                t_b = i;
                break;
            }
        }
        t = Mathf.InverseLerp(t_a, t_b, t);
        Quaternion rot = Quaternion.Lerp(rot_a, rot_b, t);
        if (!local) rot = transform.rotation * rot;
        //Debug.Log(t_a + " / " + t_b + " / " + t);
        return Vector3.ProjectOnPlane(rot * Vector3.up, tangent).normalized;
    }

    /// <summary> Get the curve indices in direct contact with knot </summary>
    private void GetCurveIndicesForKnot(int knotIndex, out int preCurveIndex, out int postCurveIndex) {
        //Get the curve index in direct contact with, before the knot
        preCurveIndex = -1;
        if (knotIndex != 0) preCurveIndex = knotIndex - 1;
        else if (closed) preCurveIndex = CurveCount - 1;

        //Get the curve index in direct contact with, after the knot
        postCurveIndex = -1;
        if (knotIndex != CurveCount) postCurveIndex = knotIndex;
        else if (closed) postCurveIndex = 0;
    }

    /// <summary> Get the knot indices in direct contact with knot </summary>
    private void GetKnotIndicesForKnot(int knotIndex, out int preKnotIndex, out int postKnotIndex) {
        //Get the curve index in direct contact with, before the knot
        preKnotIndex = -1;
        if (knotIndex != 0) preKnotIndex = knotIndex - 1;
        else if (closed) preKnotIndex = KnotCount - 1;

        //Get the curve index in direct contact with, after the knot
        postKnotIndex = -1;
        if (knotIndex != KnotCount - 1) postKnotIndex = knotIndex + 1;
        else if (closed) postKnotIndex = 0;
    }

    private Bezier3DCurve GetCurve(float splineT, out float curveT) {
        splineT *= CurveCount;
        for (int i = 0; i < CurveCount; i++) {
            if (splineT > 1f) splineT -= 1f;
            else {
                curveT = splineT;
                return curves[i];
            }
        }
        curveT = 1f;
        return curves[CurveCount - 1];
    }

    private Bezier3DCurve GetCurveDistance(float splineDist, out float curveDist) {
        for (int i = 0; i < CurveCount; i++) {
            if (curves[i].length < splineDist) splineDist -= curves[i].length;
            else {
                curveDist = splineDist;
                return curves[i];
            }
        }
        curveDist = curves[CurveCount -1].length;
        return curves[CurveCount - 1];
    }

    /// <summary> Automate handles based on previous and next point positions </summary>
    private void AutomateHandles(int i, ref Knot knot) {
        //Terminology: Points are referred to as A B and C
        //A = prev point, B = current point, C = next point

        Vector3 prevPos;
        if (i != 0) prevPos = curves[i - 1].a;
        else if (closed) prevPos = curves[CurveCount - 1].a;
        else prevPos = Vector3.zero;

        Vector3 nextPos;
        if (i != KnotCount - 1) nextPos = curves[i].d;
        else if (closed) nextPos = curves[0].a;
        else nextPos = Vector3.zero;

        AutomateHandles(i, ref knot, prevPos, nextPos);
    }

    /// <summary> Automate handles based on previous and next point positions </summary>
    private void AutomateHandles(int i, ref Knot knot, Vector3 prevPos, Vector3 nextPos) {
        //Terminology: Points are referred to as A B and C
        //A = prev point, B = current point, C = next point
        float amount = knot.auto;

        //Calculate directional vectors
        Vector3 AB = knot.position - prevPos;
        Vector3 CB = knot.position - nextPos;
        //Calculate the across vector
        Vector3 AB_CB = (CB.normalized - AB.normalized).normalized;

        if (!closed) {
            if (i == 0) {
                knot.handleOut = CB * -amount;
            }
            else if (i == CurveCount) {
                knot.handleIn = AB * -amount;
            }
            else {
                knot.handleOut = -AB_CB * CB.magnitude * amount;
                knot.handleIn = AB_CB * AB.magnitude * amount;
            }
        }
        else {
            if (KnotCount == 2) {
                Vector3 left = new Vector3(AB.z, 0,-AB.x) * amount;
                if (i == 0) {
                    knot.handleIn = left;
                    knot.handleOut = -left;
                }
                if (i == 1) {
                    knot.handleIn = left;
                    knot.handleOut = -left;
                }
            }
            else {
            knot.handleIn = AB_CB * AB.magnitude * amount;
            knot.handleOut = -AB_CB * CB.magnitude * amount;
            }
        }
    }

    private float GetTotalLength() {
        float length = 0f;
        for (int i = 0; i < CurveCount; i++) {
            length += curves[i].length;
        }
        return length;
    }
    #endregion

    /// <summary> Unity doesn't support serialization of nullable types, so here's a custom struct that does exactly the same thing </summary>
    [Serializable]
    protected struct NullableQuaternion {
        public Quaternion Value { get { return rotation; } }
        public Quaternion? NullableValue { get { if (hasValue) return rotation; else return null; } }
        public bool HasValue { get { return hasValue; } }

        [SerializeField] private Quaternion rotation;
        [SerializeField] private bool hasValue;

        public NullableQuaternion(Quaternion? rot) {
            rotation = rot.HasValue?rot.Value:Quaternion.identity;
            hasValue = rot.HasValue;
        }

        //  User-defined conversion from nullable type to NullableQuaternion
        public static implicit operator NullableQuaternion(Quaternion? r) {
            return new NullableQuaternion(r);
        }
    }
#if UNITY_EDITOR
    void OnDrawGizmos() {
        //Set color depending on selection
        if (Array.IndexOf(UnityEditor.Selection.gameObjects, gameObject) >= 0) {
            Gizmos.color = Color.yellow;
        } else  Gizmos.color = new Color(1, 0.6f, 0f);

        //Loop through each curve in spline
        for (int i = 0; i < CurveCount; i++) {
            Bezier3DCurve curve = GetCurve(i);

            //Get curve in world space
            Vector3 a, b, c, d;
            a = transform.TransformPoint(curve.a);
            b = transform.TransformPoint(curve.b + curve.a);
            c = transform.TransformPoint(curve.c + curve.d);
            d = transform.TransformPoint(curve.d);

            int segments = 50;
            float spacing = 1f / segments;
            Vector3 prev = Bezier3DCurve.GetPoint(a, b, c, d, 0f);
            for (int k = 0; k <= segments; k++) {
                Vector3 cur = Bezier3DCurve.GetPoint(a, b, c, d, k * spacing);
                Gizmos.DrawLine(prev, cur);
                prev = cur;
            }
        }
    }
#endif
}

 

FOCUS ANDROID DEBUG BRIDGE QUICK TIPS

 To load apps onto your Focus device, we suggest using Android Debug Bridge (adb) or your favorite command line tool. For more information refer to the Android Debug Bridge User Guide.

 

  You can use Terminal from your desktop or from within Android Studio to navigate to and utilize the debug bridge. (NOTE: On a Mac in order to use adb, you must type “./adb” and then the command each time).

For example to show connected devices:

  “adb devices”

  To install an app on a connected device:

“adb install [directory location of apk]”

 If an app is already installed, and you wish to replace it:

“adb install -r [directory location of apk]”

To uninstall an app from a connected device:

“adb uninstall com.CompanyName.AppName”

Even more Tips:

# set and start launcher:
adb shell cmd package set-home-activity com.htc.mobilevr.launcher/com.htc.vr.unity.WVRUnityVRActivity
adb shell am start -n com.htc.mobilevr.launcher/com.htc.vr.unity.WVRUnityVRActivity

 

# reset launcher back to the Android phone launcher:
adb shell cmd package set-home-activity com.android.launcher/com.android.launcher2.Launcher
adb shell am start -n com.android.launcher/com.android.launcher2.Launcher

 

# run an app:
adb shell am start -n <package-name>/<activity-name>
# e.g. adb shell am start -n com.htc.bowshot_3DOF/com.htc.vr.unity.WVRUnityVRActivity

 

# list 3rd party installed packages:
adb shell cmd package list packages -3

 

# find activity of an installed package: (from an apk: aapt dump badging yourapp.apk)
adb shell cmd package resolve-activity –brief <package-name>

 

# uninstall a package e.g. Vysor:
adb uninstall com.koushikdutta.vysor

 

# turn bluetooth on:
adb root
adb shell service call bluetooth_manager 6

# use 8 to turn off

# for wifi use: adb shell svc wifi enable # or disable
adb unroot

 

# power down:
adb shell reboot -p

 

# check battery level:
adb shell “dumpsys battery | grep level”

 

# adb over wifi (setup wifi connection first):
# find ip address:
adb shell “ifconfig wlan0 | grep ‘inet addr:'”

# restart adb over wifi:
adb tcpip 5555

# connect over wifi: (you can now disconnect usb cable)
adb connect <ip address>:5555

# to revert to usb:
adb usb # or just reboot

CODE REFERENCE

Unity Native Spline

 

 

 

SPHERE EXPANDER – FROM Fundamental Flower

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using ProceduralToolkit;
using UnityEngine.UI;
using System;
using ArgosTweenSpacePuppy;
using VRTK;
using System.IO;
using UnityEngine.EventSystems;
using System.Linq;

public class Sphere_Expander : MonoBehaviour
{
    public bool bSeparate_Faces = false;
    public bool bDrawTetrahedron = false;

    public int totalVerts = 0;
    public int filteredVerts = 0;

    public int vSort_Count_0;
    public int vSort_Count_1;
    public int vSort_Count_2;

    private ArgosMeshDraft   aMD_TetraTerrean;
    private ArgosMeshDraft   aMD_TT_Sleeve;
    private ArgosMeshDraft[] aMD_tt_Faces = new ArgosMeshDraft[4];
    private GameObject[]     tt_Faces = new GameObject[4];
    private ArgosMeshDraft   aMD_Spheres;
    private ArgosMeshDraft   cylinder_MD;
    private ArgosMeshDraft   scythe_MD;
    private GameObject       scythe_GO;
    public  Color            col_edge_Cylinder;
    public  Color            col_Sphere;
    private GameObject[]     spheres = new GameObject[4];
    private Vector3[]        sphere_pos = new Vector3[4];
    public  GameObject       meshPF;
    public  GameObject       tetra_Vert_GOPF;
    private GameObject[]     tetra_Verts_GO = new GameObject[4];
    private GameObject[]     tetra_Edges    = new GameObject[6];
    private GameObject       TetraTerrien_GO;
    private GameObject       TT_Sleeve_GO;

    public float[] lengths = new float[256];
    public float[] hyperbola_of_R = new float[256];

    public int all_Shape_Depth = 4;

    Vector3[] vC = new Vector3[4];

    public class Vector_Sort
    {
        public Vector3 vert;
        public float dist = 0;
        public int vIDX = 0;
    }

    internal class PlaneHelper
    {
        /// <summary>
        /// Returns a value indicating what side (positive/negative) of a plane a point is
        /// </summary>
        /// <param name="point">The point to check with</param>
        /// <param name="plane">The plane to check against</param>
        /// <returns>Greater than zero if on the positive side, less than zero if on the negative size, 0 otherwise</returns>
        public static float ClassifyPoint(ref Vector3 point, ref Plane plane)
        {
            return point.x * plane.Normal.x + point.y * plane.Normal.y + point.z * plane.Normal.z + plane.D;
        }

        /// <summary>
        /// Returns the perpendicular distance from a point to a plane
        /// </summary>
        /// <param name="point">The point to check</param>
        /// <param name="plane">The place to check</param>
        /// <returns>The perpendicular distance from the point to the plane</returns>
        public static float PerpendicularDistance(ref Vector3 point, ref Plane plane)
        {
            // dist = (ax + by + cz + d) / sqrt(a*a + b*b + c*c)
            return Mathf.Abs((plane.Normal.x * point.x + plane.Normal.y * point.y + plane.Normal.z * point.z)
                                    / Mathf.Sqrt(plane.Normal.x * plane.Normal.x + plane.Normal.y * plane.Normal.y + plane.Normal.z * plane.Normal.z));
        }
    }

    public struct Plane 
    {
        #region Public Fields

        public float D;
        public Vector3 Normal;

        #endregion Public Fields

        #region Constructors

        public Plane(Vector4 value)
            : this(new Vector3(value.x, value.y, value.z), value.w)
        {

        }

        public Plane(Vector3 normal, float d)
        {
            Normal = normal;
            D = d;
        }

        public Plane(Vector3 a, Vector3 b, Vector3 c)
        {
            Vector3 ab = b - a;
            Vector3 ac = c - a;

            Vector3 cross = Vector3.Cross(ab, ac);
            Normal = Vector3.Normalize(cross);
            D = -(Vector3.Dot(Normal, a));
        }

        public Plane(float a, float b, float c, float d)
            : this(new Vector3(a, b, c), d)
        {

        }

        #endregion Constructors

        public override string ToString()
        {
            return string.Format("{{Normal:{0} D:{1}}}", Normal, D);
        }
    }

    public enum TT_TYPE
    {
        SPHERICAL,
        HYPERBOLIC,
        OTHER,
        NONE,
    }
    public TT_TYPE ttType = TT_TYPE.SPHERICAL;

    public bool bSpheresON = true;
    public bool bTetraTerrean_On = false;

    [Range(0, 50f)]
    public float tetra_Radius;
    private float tetra_Radius_Last = 0;

    [Range(0, 100f)]
    public float scale_TT= 100f;
 
    [Range(0, 500f)]
    public float sphere_Radius;
    private float sphere_Radius_Last = 0;

    [Range(0, 0.5f)]
    public float cylinder_Radius;

    private Vector3[] tetra_Verts  = new Vector3[4];
    private Vector3[] face_Centers = new Vector3[4];

    [Space(12)]
    [Header("Hyperbolic Settings")]
    [Space(12)]

    [Range(0, 1f)]
    public float base_Distance;

    [Range(0, 1f)]
    public float tangent_Base;

    [Range(0, 1f)]
    public float tangent_H;

    [Range(0, 1f)]
    public float smidge;

    public GameObject H_Editor_Label;
    public GameObject C_Editor_Label;
    public GameObject B_Editor_Label;
    public GameObject I_Editor_Label;
    public GameObject T1_Editor_Label;
    public GameObject T2_Editor_Label;
    private int frameCount = 1;
    private Vector3 vCHn, vIHn, vT1n, vT2n;
    private Vector3 vIH;

    StreamWriter sWrite;
    public bool bPrint_Trace = false;

    private Vector3[] beeS = new Vector3[4];

    StreamWriter sLineVectors;

    private ArgosMeshDraft aMD_Nurbs_Mesh;
    GameObject pQuad_CARBON_LINE_go;

    private ArgosMeshDraft aMD_PQUAD_TMP;

    public enum RESOLUTION
    {
        RES_LOW,
        RES_MED,
        RES_HIGH,
    }

    public RESOLUTION rESOLUTION = RESOLUTION.RES_LOW;
    private RESOLUTION resolution_Last = RESOLUTION.RES_LOW;

    private int nRESOLUTION = 7;

    private List<Vector3> lst_mesh_v = new List<Vector3>();
    public float[] spline_lengths = new float[256];

    [Range(0, 10f)]
    public float short_Tan_mag = 1;

    [Range(0, 10f)]
    public float long_Tan_mag = 1;

    void Start()
    {
        sLineVectors = new StreamWriter("sLineVectors.txt");

        aMD_TetraTerrean = new ArgosMeshDraft();
        aMD_Spheres = new ArgosMeshDraft();
        aMD_Spheres.Add(MeshDraft.Sphere(1,16,16));
        cylinder_MD = new ArgosMeshDraft();
        aMD_TT_Sleeve = new ArgosMeshDraft();

        scythe_MD = new ArgosMeshDraft();
        scythe_GO = Instantiate(meshPF, transform);
        scythe_GO.name = "SCYTHE_OUTLINE";

        aMD_Nurbs_Mesh = new ArgosMeshDraft();
        pQuad_CARBON_LINE_go = Instantiate(meshPF, transform);
        pQuad_CARBON_LINE_go.name = "pQuad_CARBON_LINE_go";

        pQuad_CARBON_LINE_go.GetComponent<MeshRenderer>().enabled = false;//so we can see the TT

        sWrite = new StreamWriter("Hyperbolic_Params.txt");

        for (int i = 0; i<4; i++)
        {
            spheres[i] = Instantiate(meshPF, transform);
            spheres[i].name = "sphere_" + i.ToString();
            SetColor_Element(col_Sphere, spheres[i]);
            spheres[i].GetComponent<MeshFilter>().mesh = aMD_Spheres.ToMeshInternal();

            tetra_Verts_GO[i] = Instantiate(tetra_Vert_GOPF, transform);
            tetra_Verts_GO[i].name = "TetVERT_" + i.ToString();

            tt_Faces[i] = Instantiate(meshPF, transform);
            tt_Faces[i].name = "TT_Face_" + i.ToString();
            aMD_tt_Faces[i] = new ArgosMeshDraft();
        }
        for(int i = 0; i<6; i++)
        {
            tetra_Edges[i] = Instantiate(meshPF, transform);
            tetra_Edges[i].name = "edge_" + i.ToString();
            SetColor_Element(col_edge_Cylinder, tetra_Edges[i]);
            tetra_Edges[i].GetComponent<MeshFilter>().mesh = cylinder_MD.ToMeshInternal();
        }
        Set_Tetra_Verts(tetra_Radius);
        TetraTerrien_GO = Instantiate(meshPF, transform);
        TetraTerrien_GO.name = "TetraTerrien_GO";
        aMD_TetraTerrean.Clear();
        //Create_TETRA_TERREAN(all_Shape_Depth);
        //TetraTerrien_GO.GetComponent<MeshFilter>().mesh = aMD_TetraTerrean.ToMeshInternal();

        TT_Sleeve_GO = Instantiate(meshPF, transform);
        TT_Sleeve_GO.name = "TT_Sleeve_GO";
        aMD_TT_Sleeve.Clear();

        aMD_PQUAD_TMP = new ArgosMeshDraft();
    }

    /// <image url="$(SolutionDir)\EMB\Quad_Nurb.png" scale="0.15" /> 


    public void PQuad_SetUp_VertsAndTans()
    {
        Vector3 v0;
        Vector3 v1;
        Vector3 v2;
        Vector3 v3;
        Vector3 t01;
        Vector3 t10;
        Vector3 t13;
        Vector3 t31;
        Vector3 t23;
        Vector3 t32;
        Vector3 t20;
        Vector3 t02;

        v0 = tetra_Verts[2];
        v1 = v0;
        v1.y = -v1.y;

        v2 = tetra_Verts[3];
        v3 = v2;
        v3.y = -v3.y;

        //short_Tan_mag;
        //long_Tan_mag;

        Vector3[] vTansNorm = new Vector3[4];

        vTansNorm[0] = -v0.normalized;
        vTansNorm[1] = -v1.normalized;
        vTansNorm[2] = -v2.normalized;
        vTansNorm[3] = -v3.normalized;

        t01 = v0 + vTansNorm[0] * short_Tan_mag;
        t02 = v0 + vTansNorm[0] * long_Tan_mag;

        t10 = v1 + vTansNorm[1] * short_Tan_mag;
        t13 = v1 + vTansNorm[1] * long_Tan_mag;

        t23 = v2 + vTansNorm[2] * short_Tan_mag;
        t20 = v2 + vTansNorm[2] * long_Tan_mag;

        t32 = v3 + vTansNorm[3] * short_Tan_mag;
        t31 = v3 + vTansNorm[3] * long_Tan_mag;

        P_Quad_Generate(v0, v1, v2, v3,
                       t01, t10, t13, t31,
                       t23, t32, t20, t02);
    }


    public void P_Quad_Generate(Vector3 v0, Vector3 v1, Vector3 v2, Vector3 v3,
                       Vector3 t01, Vector3 t10, Vector3 t13, Vector3 t31,
                       Vector3 t23, Vector3 t32, Vector3 t20, Vector3 t02)
    {
        Set_Resolution();

        lst_mesh_v.Clear();
        aMD_Nurbs_Mesh.Clear();

        float u = 0;
        float v = 0;
        float du = 1 / (float)nRESOLUTION;
        float dv = 1 / (float)nRESOLUTION;

        Vector3 t0, t1;
        Vector3 p0, p1;
        Vector3 p;

        for (int i = 0; i < nRESOLUTION + 1; i++)
        {
            t0 = GetPointOnBezierCurve(t02, t01, t10, t13, get_Adjusted(u, ref spline_lengths));
            p0 = GetPointOnBezierCurve(v0, t01, t10, v1, get_Adjusted(u, ref spline_lengths));
            t1 = GetPointOnBezierCurve(t20, t23, t32, t31, get_Adjusted(u, ref spline_lengths));
            p1 = GetPointOnBezierCurve(v2, t23, t32, v3, get_Adjusted(u, ref spline_lengths));

            Compute_Dist(ref spline_lengths, v0, t02, t20, v2);

            v = 0f;
            for (int j = 0; j < nRESOLUTION + 1; j++)
            {
                p = GetPointOnBezierCurve(p0, t0, t1, p1, get_Adjusted(v, ref spline_lengths));
                lst_mesh_v.Add(p);
                v += dv;
            }
            u += du;
        }

        int stride = nRESOLUTION + 1;
        int k = 0;
        for (int i = 0; i < nRESOLUTION; i++)
        {
            for (int j = 0; j < nRESOLUTION; j++)
            {
                aMD_Nurbs_Mesh.Add(MeshDraft.Quad(lst_mesh_v[k], lst_mesh_v[k + 1], lst_mesh_v[k + stride + 1], lst_mesh_v[k + stride]));
                k++;
            }
            k++;
        }

        Quaternion q;
        float angle = 120;

        ArgosMeshDraft amdTmp = new ArgosMeshDraft();

        amdTmp.Copy_MeshDraft(aMD_Nurbs_Mesh);

        amdTmp.Rotate(Quaternion.AngleAxis(angle, Vector3.up));

        aMD_Nurbs_Mesh.Add(amdTmp);

        amdTmp.Rotate(Quaternion.AngleAxis(angle, Vector3.up));

        aMD_Nurbs_Mesh.Add(amdTmp);

        aMD_PQUAD_TMP.Clear();

        subdivide_PQUAD_TRIANGLE(tetra_Verts[2], tetra_Verts[3], tetra_Verts[1], all_Shape_Depth, spheres[2].transform.localPosition, 3, true);

        aMD_Nurbs_Mesh.Add(aMD_PQUAD_TMP);

        aMD_PQUAD_TMP.Rotate(Quaternion.AngleAxis(180, Vector3.forward));

        aMD_Nurbs_Mesh.Add(aMD_PQUAD_TMP);

        pQuad_CARBON_LINE_go.GetComponent<MeshFilter>().mesh = aMD_Nurbs_Mesh.ToMeshInternal();
        pQuad_CARBON_LINE_go.GetComponent<MeshFilter>().mesh.RecalculateNormals(60);

    }

    void subdivide_PQUAD_TRIANGLE(Vector3 v1, Vector3 v2, Vector3 v3, int depth, Vector3 sector_Foc, int nID, bool bFlipNorm)
    {
        Vector3 v12, v23, v31;
        Vector3 v12_n, v23_n, v31_n;

        if (depth == 0)
        {
            if (bFlipNorm)
            {
                addTriangle_UV_Tag_PQUAD(v1, v3, v2, (float)nID);
            }
            else
            {
                //if (nID == 1)
                //{
                //    vTest[i] = v1;
                //    vTest2[i] = v2;
                //    i++;
                //}
                addTriangle_UV_Tag_PQUAD(v3, v2, v1, (float)nID);
            }
            return;
        }

        v12 = (v1 + v2) / 2.0f;
        v23 = (v2 + v3) / 2.0f;
        v31 = (v3 + v1) / 2.0f;

        v12_n = Sphere_Surf(v12, sector_Foc); //sector_Foc
        v23_n = Sphere_Surf(v23, sector_Foc);
        v31_n = Sphere_Surf(v31, sector_Foc);

        //v12_n = v12; //sector_Foc
        //v23_n = v23;
        //v31_n = v31;

        /* recursively subdivide new triangles */
        subdivide_PQUAD_TRIANGLE(v1, v12_n, v31_n, depth - 1, sector_Foc, 1, bFlipNorm);
        subdivide_PQUAD_TRIANGLE(v12_n, v2, v23_n, depth - 1, sector_Foc, 2, bFlipNorm);
        subdivide_PQUAD_TRIANGLE(v31_n, v23_n, v3, depth - 1, sector_Foc, 3, bFlipNorm);
        subdivide_PQUAD_TRIANGLE(v23_n, v31_n, v12_n, depth - 1, sector_Foc, 4, bFlipNorm);
    }

    private void Create_Top_Bottom(int depth)
    {
        //sleeve(tetra_Verts[1], tetra_Verts[2], tetra_Verts[3], spheres[2].transform.localPosition);
        subdivide_CarbonLine(tetra_Verts[1], tetra_Verts[3], tetra_Verts[2], depth, spheres[2].transform.localPosition, 3, false);
        List<Vector_Sort>[] vsortList = new List<Vector_Sort>[3];

        vsortList[0] = new List<Vector_Sort>();
        vsortList[1] = new List<Vector_Sort>();
        vsortList[2] = new List<Vector_Sort>();

        int[] id = new int[] { 0, 1, 2, 0, 2, 3, 0, 3, 1 };
        Vector3 vert;

        for (int j = 0; j < 3; j++)
        {
            Plane p = new Plane(tetra_Verts[id[3 * j]], tetra_Verts[id[3 * j + 1]], tetra_Verts[id[3 * j + 2]]);
            p.D = p.D * 0.999f;

            Vector_Sort vs;

            filteredVerts = 0;

            for (int i = 0; i < aMD_TT_Sleeve.vertices.Count; i++)
            {
                vert = aMD_TT_Sleeve.vertices[i];
                float res = PlaneHelper.ClassifyPoint(ref vert, ref p);

                if (res > 0)
                {
                    filteredVerts++;
                    vs = new Vector_Sort();
                    vs.vert = vert;
                    vs.vIDX = i;
                    vsortList[j].Add(vs);
                }
            }
            RemoveDuplicates(vsortList[j]);
            for (int i = 0; i < vsortList[j].Count; i++)
            {
                vsortList[j][i].dist = (tetra_Verts[j + 1] - vsortList[j][i].vert).magnitude;
            }

            var ordered = from element in vsortList[j]
                          orderby element.dist
                          select element;

            vsortList[j] = ordered.ToList<Vector_Sort>();
        }

        for (int i = 0; i < aMD_TT_Sleeve.vertices.Count; i++)
        {
            aMD_TT_Sleeve.vertices[i] = Sphere_Surf(aMD_TT_Sleeve.vertices[i], spheres[2].transform.localPosition);
        }

        Plane p2 = new Plane(Vector3.up, 0);
        int vertCount = aMD_TT_Sleeve.vertices.Count;

        ArgosMeshDraft amdTemp = new ArgosMeshDraft();

        amdTemp.Add(aMD_TT_Sleeve);
        aMD_TT_Sleeve.FlipTriangles();

        aMD_TT_Sleeve.Add(amdTemp);
        aMD_TT_Sleeve.FlipNormals();
        float d;

        for (int i = 0; i < vertCount; i++)//FLIP
        {
            vert = aMD_TT_Sleeve.vertices[i];
            d = PlaneHelper.ClassifyPoint(ref vert, ref p2);
            aMD_TT_Sleeve.vertices[i] = vert - 2f * d * Vector3.up;
        }

        List<Vector3> vInnerLower = new List<Vector3>();
        List<Vector3> vInnerUpper = new List<Vector3>();

        List<Vector3> vPrint = new List<Vector3>();

        for (int j = 0; j < 3; j++)
        {
            for (int i = 0; i < vsortList[j].Count; i++)
            {
                vert = aMD_TT_Sleeve.vertices[vsortList[j][i].vIDX];
                //vCurr.y = 0;
                d = PlaneHelper.ClassifyPoint(ref vert, ref p2);

                if (j == 0)
                {
                    vPrint.Add(vert - 2f * d * Vector3.up);
                }

                vInnerLower.Add(vert);
                vInnerUpper.Add(vert - 2f * d * Vector3.up);
            }
        }
        aMD_TT_Sleeve.Add(MeshDraft.Band(vInnerUpper, vInnerLower));
        aMD_TT_Sleeve.Add(MeshDraft.Band(vInnerLower, vInnerUpper));

        Generate_Line_List(vPrint);

        vSort_Count_0 = vsortList[0].Count;
        vSort_Count_1 = vsortList[1].Count;
        vSort_Count_2 = vsortList[2].Count;
    }

    private float get_Adjusted(float t, ref float[] lengths)
    {
        int i = 0;
        while (i < 256 && lengths[i] < t)
        {
            i++;
        }
        return (float)i / 256;
    }

    private void Compute_Dist(ref float[] lengths, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
    {
        float t = 0.0f;
        float dt = 1f / 256;

        Vector3 vB = GetPointOnBezierCurve(p0, p1, p2, p3, t);
        Vector3 vB_Last = vB;
        float running_Len = 0;

        for (int i = 0; i < 256; i++)
        {
            vB = GetPointOnBezierCurve(p0, p1, p2, p3, t);
            running_Len += (vB - vB_Last).magnitude;
            lengths[i] = running_Len;
            vB_Last = vB;
            t += dt;
        }
        for (int i = 0; i < 256; i++)
        {
            lengths[i] /= running_Len;
        }
    }

    private void Set_Resolution()
    {
        if (rESOLUTION != resolution_Last)
        {
            if (rESOLUTION == RESOLUTION.RES_LOW)
            {
                nRESOLUTION = 7;
            }
            else if (rESOLUTION == RESOLUTION.RES_MED)
            {
                nRESOLUTION = 18;
            }
            else if (rESOLUTION == RESOLUTION.RES_HIGH)
            {
                nRESOLUTION = 36;
            }
        }
        resolution_Last = rESOLUTION;
    }


    //void OnDrawGizmos()
    //{
    //    Gizmos.DrawIcon(H_Editor_Label.transform.position, "H.png", true);
    //    Gizmos.DrawIcon(C_Editor_Label.transform.position, "C.png", true);
    //    Gizmos.DrawIcon(I_Editor_Label.transform.position, "I.png", true);
    //    Gizmos.DrawIcon(B_Editor_Label.transform.position, "B.png", true);
    //    Gizmos.DrawIcon(T1_Editor_Label.transform.position, "T1.png", true);
    //    Gizmos.DrawIcon(T2_Editor_Label.transform.position, "T2.png", true);
    //    Gizmos.color = Color.green;
    //    Gizmos.DrawLine(B_Editor_Label.transform.position, T1_Editor_Label.transform.position);
    //    Gizmos.DrawLine(H_Editor_Label.transform.position, T2_Editor_Label.transform.position);
    //}

    private void OnApplicationQuit()
    {
        sWrite.Close();
    }

    /// <image url="$(SolutionDir)\EMB\hyperbole2.png" scale="0.35"></image>

    private void build_Reference_Hyperbolic_Spline(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 vI)
    {
        //p4 = H
        float t = 0.0f;
        float dt = 1f / 256;

        Vector3 vB = GetPointOnBezierCurve(p0, p1, p2, p3, t);
        Vector3 vB_Last = vB;
        float running_Len = 0;

        float   vI_Len = vI.magnitude;
        Vector3 vIn    = vI.normalized;

        for (int i = 0; i < 256; i++)
        {
            vB = GetPointOnBezierCurve(p0, p1, p2, p3, t);
            running_Len += (vB - vB_Last).magnitude;
            lengths[i] = running_Len;
            hyperbola_of_R[i] = Vector3.Dot(vB, vIn);
            vB_Last = vB;
            t += dt;
        }
        for (int i = 0; i < 256; i++)
        {
            lengths[i] /= running_Len;
        }
    }

    public void Print_Trace(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 vI)
    {
        for (int i = 0; i < 256; i++)
        {
            sWrite.WriteLine(i.ToString() + "\t " + " -\t " + hyperbola_of_R[i].ToString("F2"));
        }
        sWrite.WriteLine("p0 Base " + "\t " + " -\t " + p0.ToString("F2"));
        sWrite.WriteLine("p1 Tangent_0 " + "\t " + " -\t " + p1.ToString("F2"));
        sWrite.WriteLine("p2 Tangent_1 " + "\t " + " -\t " + p2.ToString("F2"));
        sWrite.WriteLine("p3 H " + "\t " + " -\t " + p3.ToString("F2"));
        sWrite.WriteLine("vI " + "\t " + " -\t " + vI.ToString("F2"));
    }

    private void SetColor_Element(Color col, GameObject go)
    {
        float alpha = col.a;
        float alpha_out = alpha / 3f;

        Color cola = col;
        cola.a = alpha_out;

        go.GetComponent<MeshRenderer>().material.SetColor("_EmissionColor", cola);
        go.GetComponent<MeshRenderer>().material.SetColor("_Color", col);
    }

    public void Set_Tetra_Verts(float radius)
    {
        float tetrahedralAngle = Mathf.PI * -19.471220333f / 180;
        float segmentAngle = Mathf.PI * 2 / 3;
        float currentAngle = 0f;

        Vector3 v = new Vector3(0, radius, 0);
        tetra_Verts[0] = v;
        for (var i = 1; i < 4; i++)
        {
            tetra_Verts[i] = PTUtils.PointOnSphere(radius, currentAngle, tetrahedralAngle);
            currentAngle += segmentAngle;
        }

        face_Centers[0] = (tetra_Verts[0] + tetra_Verts[1] + tetra_Verts[2]) / 3f;
        face_Centers[1] = (tetra_Verts[0] + tetra_Verts[2] + tetra_Verts[3]) / 3f;
        face_Centers[2] = (tetra_Verts[1] + tetra_Verts[2] + tetra_Verts[3]) / 3f;
        face_Centers[3] = (tetra_Verts[0] + tetra_Verts[1] + tetra_Verts[3]) / 3f;

        float edge_length = (tetra_Verts[0] - tetra_Verts[1]).magnitude;
        edge_length = radius * 2f * Mathf.Sqrt(2f) / Mathf.Sqrt(3);

        cylinder_MD.Add(MeshDraft.Cylinder_Z(cylinder_Radius, 5, edge_length));

        //tetra_Edges[0].transform.localPosition = tetra_Verts[0];
        //tetra_Edges[0].transform.localRotation = Quaternion.LookRotation(tetra_Verts[1] - tetra_Verts[0]);
        //tetra_Edges[0].GetComponent<MeshFilter>().mesh = cylinder_MD.ToMeshInternal();

        //tetra_Edges[1].transform.localPosition = tetra_Verts[1];
        //tetra_Edges[1].transform.localRotation = Quaternion.LookRotation(tetra_Verts[2] - tetra_Verts[1]);
        //tetra_Edges[1].GetComponent<MeshFilter>().mesh = cylinder_MD.ToMeshInternal();

        //tetra_Edges[2].transform.localPosition = tetra_Verts[2];
        //tetra_Edges[2].transform.localRotation = Quaternion.LookRotation(tetra_Verts[3] - tetra_Verts[2]);
        //tetra_Edges[2].GetComponent<MeshFilter>().mesh = cylinder_MD.ToMeshInternal();

        //tetra_Edges[3].transform.localPosition = tetra_Verts[3];
        //tetra_Edges[3].transform.localRotation = Quaternion.LookRotation(tetra_Verts[0] - tetra_Verts[3]);
        //tetra_Edges[3].GetComponent<MeshFilter>().mesh = cylinder_MD.ToMeshInternal();

        //tetra_Edges[4].transform.localPosition = tetra_Verts[1];
        //tetra_Edges[4].transform.localRotation = Quaternion.LookRotation(tetra_Verts[3] - tetra_Verts[1]);
        //tetra_Edges[4].GetComponent<MeshFilter>().mesh = cylinder_MD.ToMeshInternal();

        //tetra_Edges[5].transform.localPosition = tetra_Verts[2];
        //tetra_Edges[5].transform.localRotation = Quaternion.LookRotation(tetra_Verts[0] - tetra_Verts[2]);
        //tetra_Edges[5].GetComponent<MeshFilter>().mesh = cylinder_MD.ToMeshInternal();
    }

    private void Create_Scythe(int depth)
    {


        subdivide_CarbonLine(tetra_Verts[1], tetra_Verts[3], tetra_Verts[2], depth, spheres[2].transform.localPosition, 3, false);

    }

    void Update() 
    {
        PQuad_SetUp_VertsAndTans();

        cylinder_MD.Clear();
        scythe_MD.Clear();
        Set_Tetra_Verts(tetra_Radius);

        if (sphere_Radius_Last != sphere_Radius)
        {
            float face_dist;
            float tetra_Height;
            Vector3 face_Norm;
            float a, b;

            aMD_Spheres.Clear();
            aMD_Spheres.Add(MeshDraft.Sphere(sphere_Radius, 36, 32));
            for (int i = 0; i < 4; i++)
            {
                face_dist = face_Centers[i].magnitude;
                tetra_Height = 1.33333f * tetra_Radius;
                a = 2 * Mathf.Sqrt(2) / 3;
                b = Mathf.Sqrt(sphere_Radius * sphere_Radius - tetra_Radius * tetra_Radius * 8f / 9f);
                face_Norm = face_Centers[i].normalized;

                spheres[i].transform.localPosition = face_Norm * (tetra_Radius / 3 + b);
                spheres[i].transform.localRotation = Quaternion.LookRotation(sphere_pos[i]);
                spheres[i].GetComponent<MeshFilter>().mesh = aMD_Spheres.ToMeshInternal();
                tetra_Verts_GO[i].transform.localPosition = tetra_Verts[i];
            }
        }
        sphere_Radius_Last = sphere_Radius;
        tetra_Radius_Last = tetra_Radius;
        if (bSpheresON)
        {
            ShowSpheres(true);
        }
        else
        {
            ShowSpheres(false);
        }

        aMD_TetraTerrean.Clear();

        for (int i = 0; i < 4; i++)
        {
            aMD_tt_Faces[i].Clear();
        }

        Create_TETRA_TERREAN(all_Shape_Depth);
        if (bSeparate_Faces)
        {
            for (int i = 0; i < 4; i++)
            {
                tt_Faces[i].GetComponent<MeshFilter>().mesh = aMD_tt_Faces[i].ToMeshInternal();
            }
        }
        else
        {
            aMD_TetraTerrean.FlipTriangles();
            aMD_TetraTerrean.FlipNormals();

            TetraTerrien_GO.GetComponent<MeshFilter>().mesh = aMD_TetraTerrean.ToMeshInternal();
            TetraTerrien_GO.GetComponent<MeshFilter>().mesh.RecalculateNormals(60);
        }

        //Create_Scythe(all_Shape_Depth);

        //aMD_TT_Sleeve.Clear();
        //Create_TETRA_TERREAN_Sleeve(all_Shape_Depth);
        //TT_Sleeve_GO.GetComponent<MeshFilter>().mesh = aMD_TT_Sleeve.ToMeshInternal();
        //TT_Sleeve_GO.GetComponent<MeshFilter>().mesh.RecalculateNormals(60);

        sphere_Radius_Last = sphere_Radius;
        tetra_Radius_Last  = tetra_Radius;
    }

    private void ShowSpheres(bool bOn)
    {
        for(int i = 0; i<4; i++)
        {
            spheres[i].GetComponent<MeshRenderer>().enabled = bOn;
        }
    }

    public void Calc_Tetra_Verts(float radius)
    {
        float tetrahedralAngle = Mathf.PI * -19.471220333f / 180;
        float segmentAngle = Mathf.PI * 2 / 3;
        float currentAngle = 0f;

        Vector3 v = new Vector3(0, radius, 0);
        sphere_pos[0] = v;
        for (var i = 1; i < 4; i++)
        {
            sphere_pos[i] = PTUtils.PointOnSphere(radius, currentAngle, tetrahedralAngle);
            currentAngle += segmentAngle;
        }
    }

    private Vector3 Sphere_Surf(Vector3 vP0, Vector3 sector_Focus)
    {
        Vector3 l = vP0.normalized;

        Vector3 OminC = -sector_Focus;
        float rsqr = sphere_Radius * sphere_Radius;

        float dotLOminC = Vector3.Dot(l, OminC);
        float Omag = OminC.magnitude;

        float d = -dotLOminC - Mathf.Sqrt(dotLOminC * dotLOminC - Omag * Omag + sphere_Radius * sphere_Radius);

        return l * d;
    }

    private Vector3 Hyperbolic(Vector3 vP0, Vector3 face_Center)
    {
        Vector3 vFCn = face_Center.normalized;
        Vector3 dotV = Vector3.Dot(vP0, vFCn)* vFCn;
        Vector3 radV = vP0 - dotV;
        Vector3 vP0n = vP0.normalized;
        float   radial_mag = radV.magnitude;
        float   perc = (radial_mag / vIH.magnitude)*255;
        float   gVal = 0f;

        //if (perc < 256)
        //{
        gVal = hyperbola_of_R[(int)perc];
        //}

        return  (gVal*vFCn + radV);
    }
     
    void Extrude_1(Vector3 v1, Vector3 v2, Vector3 v3, int nID, Vector3 sector_Foc)
    {
        Vector3 nSect = tetra_Verts[0].normalized;
        float spar = (1f / 9f) * sphere_Radius;

        float d1, d2, d3;

        d1 = Vector3.Dot(v1, nSect);
        d2 = Vector3.Dot(v2, nSect);
        d3 = Vector3.Dot(v3, nSect);

        Vector3 ob1 = (spar - d1) * nSect;
        Vector3 ob2 = (spar - d2) * nSect;
        Vector3 ob3 = (spar - d3) * nSect;

        if (nID == 0)
        {
            addTriangle(v1 + 2 * ob1, v2 + 2 * ob2, v3 + 2 * ob3);
        }
        else
        {
            ob1 = PosCheck(nSect, d1, (d1 / spar), v1 - d1 * nSect);
            ob2 = PosCheck(nSect, d2, (d2 / spar), v2 - d2 * nSect);
            ob3 = PosCheck(nSect, d3, (d3 / spar), v3 - d3 * nSect);
            addTriangle(v1 + ob1, v2 + ob2, v3 + ob3);
        }
    }

    private Vector3 PosCheck(Vector3 v1, float d, float multiplier, Vector3 vHorz)
    {
        return Vector3.zero;
    }

    public static void RemoveDuplicates(List<Vector_Sort> list)
    {
        if (list == null)
        {
            return;
        }
        int i = 1;
        while (i < list.Count)
        {
            int j = 0;
            bool remove = false;
            while (j < i && !remove)
            {
                if (list[i].vert.Equals(list[j].vert))
                {
                    remove = true;
                }
                j++;
            }
            if (remove)
            {
                list.RemoveAt(i);
            }
            else
            {
                i++;
            }
        }
    }

    private void Create_TETRA_TERREAN_Sleeve(int depth)
    {
        //sleeve(tetra_Verts[1], tetra_Verts[2], tetra_Verts[3], spheres[2].transform.localPosition);
        subdivide_CarbonLine(tetra_Verts[1], tetra_Verts[3], tetra_Verts[2], depth, spheres[2].transform.localPosition, 3, false);
        List<Vector_Sort>[] vsortList = new List<Vector_Sort>[3];

        vsortList[0] = new List<Vector_Sort>();
        vsortList[1] = new List<Vector_Sort>();
        vsortList[2] = new List<Vector_Sort>();

        int[] id = new int [] { 0, 1, 2, 0, 2, 3, 0, 3, 1 }; 
        Vector3   vert;

        for (int j = 0; j < 3; j++)
        {
            Plane p = new Plane(tetra_Verts[id[3*j]], tetra_Verts[id[3*j+1]], tetra_Verts[id[3*j+2]]);
            p.D = p.D * 0.999f;
            
            Vector_Sort vs;

            filteredVerts = 0;

            for (int i = 0; i < aMD_TT_Sleeve.vertices.Count; i++)
            {
                vert = aMD_TT_Sleeve.vertices[i];
                float res = PlaneHelper.ClassifyPoint(ref vert, ref p);

                if (res > 0)
                {
                    filteredVerts++;
                    vs = new Vector_Sort();
                    vs.vert = vert;
                    vs.vIDX = i;
                    vsortList[j].Add(vs);
                }
            }
            RemoveDuplicates(vsortList[j]);
            for (int i = 0; i < vsortList[j].Count; i++)
            {
                vsortList[j][i].dist = (tetra_Verts[j+1] - vsortList[j][i].vert).magnitude;
            }

            var ordered = from element in vsortList[j]
                      orderby element.dist
                      select element;

            vsortList[j] = ordered.ToList<Vector_Sort>();
        }

        for(int i = 0; i < aMD_TT_Sleeve.vertices.Count; i++)
        {
            aMD_TT_Sleeve.vertices[i] = Sphere_Surf(aMD_TT_Sleeve.vertices[i], spheres[2].transform.localPosition);
        }

        Plane p2 = new Plane(Vector3.up, 0);
        int vertCount = aMD_TT_Sleeve.vertices.Count;

        ArgosMeshDraft amdTemp = new ArgosMeshDraft();

        amdTemp.Add(aMD_TT_Sleeve);
        aMD_TT_Sleeve.FlipTriangles();
        
        aMD_TT_Sleeve.Add(amdTemp);
        aMD_TT_Sleeve.FlipNormals();
        float d;

        for(int i = 0; i < vertCount; i++)//FLIP
        {
            vert = aMD_TT_Sleeve.vertices[i];
            d = PlaneHelper.ClassifyPoint(ref vert, ref p2);
            aMD_TT_Sleeve.vertices[i] = vert - 2f * d * Vector3.up;
        }

        List<Vector3> vInnerLower = new List<Vector3>();
        List<Vector3> vInnerUpper = new List<Vector3>();

        List<Vector3> vPrint = new List<Vector3>();

        for (int j = 0; j < 3; j++)
        {
            for (int i = 0; i < vsortList[j].Count; i++)
            {
                vert = aMD_TT_Sleeve.vertices[vsortList[j][i].vIDX];
                //vCurr.y = 0;
                d = PlaneHelper.ClassifyPoint(ref vert, ref p2);

                if(j==0)
                {
                    vPrint.Add(vert - 2f * d * Vector3.up);
                }

                vInnerLower.Add(vert);
                vInnerUpper.Add(vert - 2f * d * Vector3.up);
            }
        }
        aMD_TT_Sleeve.Add(MeshDraft.Band(vInnerUpper, vInnerLower));
        aMD_TT_Sleeve.Add(MeshDraft.Band(vInnerLower, vInnerUpper));

        Generate_Line_List(vPrint);

        vSort_Count_0 = vsortList[0].Count;
        vSort_Count_1 = vsortList[1].Count;
        vSort_Count_2 = vsortList[2].Count;
    }


    bool bwritten = false;
    void Generate_Line_List(List<Vector3> vPrint)
    {
        if (!bwritten)
        {
            vPrint.Insert(0, tetra_Verts[1]);
            vPrint.Add(tetra_Verts[2]);
            //vPrint[0] = tetra_Verts[1];
            //vPrint[vPrint.Count - 1] = tetra_Verts[2];

            for (int i = 0; i < vPrint.Count-1; i++)
            {
                sLineVectors.Write("new Vector3(" + vPrint[i].x.ToString("F3") + "f, " + vPrint[i].y.ToString("F3") + "f, " + vPrint[i].z.ToString("F3") + "f), " +
                                   "new Vector3(" + vPrint[i+1].x.ToString("F3") + "f, " + vPrint[i+1].y.ToString("F3") + "f, " + vPrint[i+1].z.ToString("F3") + "f), ");
            }

            Quaternion q;
            float angle = 120f;
            Vector3 axis = Vector3.up;

            q = Quaternion.AngleAxis(angle, axis);
            Vector3 vRota;
            Vector3 vRota2;

            for (int i = 0; i< vPrint.Count-1; i++)
            {
                vRota  = q * vPrint[i];
                vRota2 = q * vPrint[i+1];
                sLineVectors.Write("new Vector3(" + vRota.x.ToString("F3") + "f, " + vRota.y.ToString("F3") + "f, " + vRota.z.ToString("F3") + "f), " +
                    "new Vector3(" + vRota2.x.ToString("F3") + "f, " + vRota2.y.ToString("F3") + "f, " + vRota2.z.ToString("F3") + "f), ");


            }
            angle = 240;
            q = Quaternion.AngleAxis(angle, axis);
            for (int i = 0; i < vPrint.Count - 1; i++)
            {
                vRota = q * vPrint[i];
                vRota2 = q * vPrint[i + 1];
                sLineVectors.Write("new Vector3(" + vRota.x.ToString("F3") + "f, " + vRota.y.ToString("F3") + "f, " + vRota.z.ToString("F3") + "f), " +
                    "new Vector3(" + vRota2.x.ToString("F3") + "f, " + vRota2.y.ToString("F3") + "f, " + vRota2.z.ToString("F3") + "f), ");

            }
            /////////////////////////UPPERS//////////////////////

            List<Vector3> vlUppers = new List<Vector3>();
            axis = tetra_Verts[1].normalized;
            angle = 120;
            q = Quaternion.AngleAxis(angle, axis);
            for (int i = 0; i < vPrint.Count - 1; i++)
            {
                vRota = q * vPrint[i];
                vRota2 = q * vPrint[i + 1];

                vlUppers.Add(vRota);
                vlUppers.Add(vRota2);

                sLineVectors.Write("new Vector3(" + vRota.x.ToString("F3") + "f, " + vRota.y.ToString("F3") + "f, " + vRota.z.ToString("F3") + "f), " +
                    "new Vector3(" + vRota2.x.ToString("F3") + "f, " + vRota2.y.ToString("F3") + "f, " + vRota2.z.ToString("F3") + "f), ");

            }

            axis = Vector3.up;
            angle = 120;
            q = Quaternion.AngleAxis(angle, axis);
            for (int i = 0; i < vlUppers.Count - 1; i++)
            {
                vRota = q * vlUppers[i];
                vRota2 = q * vlUppers[i + 1];
                sLineVectors.Write("new Vector3(" + vRota.x.ToString("F3") + "f, " + vRota.y.ToString("F3") + "f, " + vRota.z.ToString("F3") + "f), " +
                    "new Vector3(" + vRota2.x.ToString("F3") + "f, " + vRota2.y.ToString("F3") + "f, " + vRota2.z.ToString("F3") + "f), ");
            }
            angle = 240;
            q = Quaternion.AngleAxis(angle, axis);
            for (int i = 0; i < vlUppers.Count - 1; i++)
            {
                vRota = q * vlUppers[i];
                vRota2 = q * vlUppers[i + 1];
                sLineVectors.Write("new Vector3(" + vRota.x.ToString("F3") + "f, " + vRota.y.ToString("F3") + "f, " + vRota.z.ToString("F3") + "f), " +
                    "new Vector3(" + vRota2.x.ToString("F3") + "f, " + vRota2.y.ToString("F3") + "f, " + vRota2.z.ToString("F3") + "f), ");
            }
        }
        sLineVectors.Close();
        bwritten = true;
    }

    void sleeve(Vector3 v1, Vector3 v2, Vector3 v3, Vector3 sector_Foc)
    {
        Vector3 vCurr;

        //v1.y = v2.y = v3.y = 0;

        List<Vector3> vInnerLower = new List<Vector3>();
        List<Vector3> vInnerUpper = new List<Vector3>();

        Vector3 vUp = Vector3.up;

        Vector3[] vAlpha = new[] { v1, v2, v3 };
        Vector3[] vOmega = new[] { v2, v3, v1 };

        float t = 0;
        float dt = 1 / 60f;
        float d;
        float el;
        for (int j = 0; j < 3; j++)
        {
            t = 0;
            for (int i = 0; i < 60; i++)
            {
                vCurr = Sphere_Surf(Vector3.Lerp(vAlpha[j], vOmega[j], t), sector_Foc);
                //vCurr.y = 0;
                d  =  vCurr.y;
                el = Mathf.Sqrt(tetra_Radius * tetra_Radius - d * d); 

                //vInnerLower.Add(vCurr + el*vUp);
                //vInnerUpper.Add(vCurr - el*vUp);

                vInnerLower.Add(vCurr);
                vInnerUpper.Add(vCurr - 2f * d * vUp);

                t += dt;
            }
        }
        aMD_TT_Sleeve.Add(MeshDraft.Band(vInnerUpper, vInnerLower));
        aMD_TT_Sleeve.Add(MeshDraft.Band(vInnerLower, vInnerUpper));
    }


    Vector3[] vTest = new Vector3[1024];
    Vector3[] vTest2 = new Vector3[1024];
    int i = 0;
    void subdivide_CarbonLine(Vector3 v1, Vector3 v2, Vector3 v3, int depth, Vector3 sector_Foc, int nID, bool bFlipNorm)
    {
        Vector3 v12, v23, v31;
        Vector3 v12_n, v23_n, v31_n;

        if (depth == 0)
        {
            if (bFlipNorm)
            {
                addTriangle_UV_Tag_CarbonLine(v1, v3, v2, (float)nID);
            }
            else
            {
                //if (nID == 1)
                //{
                //    vTest[i] = v1;
                //    vTest2[i] = v2;
                //    i++;
                //}
                addTriangle_UV_Tag_CarbonLine(v1, v2, v3, (float)nID);
            }
            return;
        }

        v12 = (v1 + v2) / 2.0f;
        v23 = (v2 + v3) / 2.0f;
        v31 = (v3 + v1) / 2.0f;

        //v12_n = Sphere_Surf(v12, sector_Foc); //sector_Foc
        //v23_n = Sphere_Surf(v23, sector_Foc);
        //v31_n = Sphere_Surf(v31, sector_Foc);

        v12_n = v12; //sector_Foc
        v23_n = v23;
        v31_n = v31;

        /* recursively subdivide new triangles */
        subdivide_CarbonLine(v1, v12_n, v31_n, depth - 1, sector_Foc, 1, bFlipNorm);
        subdivide_CarbonLine(v12_n, v2, v23_n, depth - 1, sector_Foc, 2, bFlipNorm);
        subdivide_CarbonLine(v31_n, v23_n, v3, depth - 1, sector_Foc, 3, bFlipNorm);
        subdivide_CarbonLine(v23_n, v31_n, v12_n, depth - 1, sector_Foc, 4, bFlipNorm);
    }

    private void Create_TETRA_TERREAN(int depth)
    {
        float scl = scale_TT / 100f;

        if (ttType == TT_TYPE.SPHERICAL)
        {
            subdivide(tetra_Verts[0], tetra_Verts[1], tetra_Verts[2], depth, spheres[0].transform.localPosition, 0, true);
            subdivide(tetra_Verts[0], tetra_Verts[2], tetra_Verts[3], depth, spheres[1].transform.localPosition, 1, true);
            subdivide(tetra_Verts[0], tetra_Verts[3], tetra_Verts[1], depth, spheres[3].transform.localPosition, 2, true);
            subdivide(tetra_Verts[1], tetra_Verts[3], tetra_Verts[2], depth, spheres[2].transform.localPosition, 3, true);
            //subdivide(tetra_Verts[0], tetra_Verts[1], tetra_Verts[2], depth, spheres[0].transform.localPosition, 0, false);//Inside
            //subdivide(tetra_Verts[0], tetra_Verts[2], tetra_Verts[3], depth, spheres[1].transform.localPosition, 1, false);
            //subdivide(tetra_Verts[0], tetra_Verts[3], tetra_Verts[1], depth, spheres[3].transform.localPosition, 2, false);
        }
        else if(ttType == TT_TYPE.HYPERBOLIC)
        {
            Vector3[] fc = new Vector3[4];

            fc[0] = (tetra_Verts[0] + tetra_Verts[1] + tetra_Verts[2]) / 3f;
            fc[1] = (tetra_Verts[0] + tetra_Verts[2] + tetra_Verts[3]) / 3f;
            fc[2] = (tetra_Verts[1] + tetra_Verts[3] + tetra_Verts[2]) / 3f;
            fc[3] = (tetra_Verts[0] + tetra_Verts[3] + tetra_Verts[1]) / 3f;

            beeS[0] = base_Distance * tetra_Radius * fc[0].normalized;
            beeS[1] = base_Distance * tetra_Radius * fc[1].normalized;
            beeS[2] = base_Distance * tetra_Radius * fc[2].normalized;
            beeS[3] = base_Distance * tetra_Radius * fc[3].normalized;

            subdivide(tetra_Verts[0], tetra_Verts[1], tetra_Verts[2], depth, fc[0], 0, false);
            subdivide(tetra_Verts[0], tetra_Verts[2], tetra_Verts[3], depth, fc[1], 1, false);
            subdivide(tetra_Verts[1], tetra_Verts[3], tetra_Verts[2], depth, fc[2], 2, false);
            subdivide(tetra_Verts[0], tetra_Verts[3], tetra_Verts[1], depth, fc[3], 3, false);

            Scale_Radially();
        }
    }

    private void Scale_Radially()
    {
        float ratio = 0;
        Vector3 v;
        float fidx;
        float len;

        for (int i = 0; i<aMD_TetraTerrean.vertices.Count; i++)
        {  
            fidx  = aMD_TetraTerrean.uv[i].x;

            ratio = aMD_TetraTerrean.vertices[i].magnitude / tetra_Radius;

            aMD_TetraTerrean.vertices[i] *= ((smidge * ratio) + (1 - smidge));
        }
    }

    void subdivide(Vector3 v1, Vector3 v2, Vector3 v3, int depth, Vector3 sector_Foc, int nID, bool bFlipNorm)
    {
        Vector3 v12, v23, v31;
        Vector3 v12_n, v23_n, v31_n;

        if (depth == 0)
        {
            if (ttType == TT_TYPE.SPHERICAL || ttType == TT_TYPE.HYPERBOLIC)
            {
                if (bFlipNorm)
                {
                    addTriangle_UV_Tag(v1, v3, v2, (float)nID);
                }
                else
                {
                    addTriangle_UV_Tag(v1, v2, v3, (float)nID);
                }
            }
            else if (ttType == TT_TYPE.OTHER)
            {
                Extrude_1(v1, v2, v3, nID, sector_Foc);
            }
            return;
        }

        v12 = (v1 + v2) / 2.0f;
        v23 = (v2 + v3) / 2.0f;
        v31 = (v3 + v1) / 2.0f;

        //intrude midpoints
        if (ttType == TT_TYPE.SPHERICAL)
        {
            v12_n = Sphere_Surf(v12, sector_Foc); //sector_Foc
            v23_n = Sphere_Surf(v23, sector_Foc);
            v31_n = Sphere_Surf(v31, sector_Foc);
        }
        else if(ttType == TT_TYPE.HYPERBOLIC)
        {
            v12_n = Hyperbolic(v12, sector_Foc); //sector_Foc
            v23_n = Hyperbolic(v23, sector_Foc);
            v31_n = Hyperbolic(v31, sector_Foc);
        }
        else if (ttType == TT_TYPE.OTHER)
        {
            v12_n = Extrude_1(v12, sector_Foc); //sector_Foc
            v23_n = Extrude_1(v23, sector_Foc);
            v31_n = Extrude_1(v31, sector_Foc);
        }
        else
        {
            v12_n = Vector3.zero;
            v23_n = Vector3.zero;
            v31_n = Vector3.zero;
        }
        /* recursively subdivide new triangles */
        subdivide(v1,    v12_n, v31_n, depth - 1, sector_Foc, nID, bFlipNorm);
        subdivide(v12_n, v2,    v23_n, depth - 1, sector_Foc, nID, bFlipNorm);
        subdivide(v31_n, v23_n, v3,    depth - 1, sector_Foc, nID, bFlipNorm);
        subdivide(v23_n, v31_n, v12_n, depth - 1, sector_Foc, nID, bFlipNorm);
    }

    private Vector3 Extrude_1(Vector3 vP0, Vector3 sector_Focus)
    {
        Vector3 l = vP0.normalized;

        Vector3 OminC = -sector_Focus;
        float rsqr = sphere_Radius * sphere_Radius;

        float dotLOminC = Vector3.Dot(l, OminC);
        float Omag = OminC.magnitude;

        float d = -dotLOminC - Mathf.Sqrt(dotLOminC * dotLOminC - Omag * Omag + sphere_Radius * sphere_Radius);

        return (l * d);
    }

    void addTriangle(Vector3 v0, Vector3 v1, Vector3 v2)
    {
        aMD_TetraTerrean.Add(MeshDraft.Triangle(v0, v1, v2));
    }

    void addTriangle_UV_Tag(Vector3 v0, Vector3 v1, Vector3 v2, float uvTag)
    {
        if (bSeparate_Faces)
        {
            aMD_tt_Faces[(int)uvTag].Add(MeshDraft.Triangl_UV_Tag(v0, v1, v2, uvTag));
        }
        else
        {
            aMD_TetraTerrean.Add(MeshDraft.Triangl_UV_Tag(v0, v1, v2, uvTag));
        }
    }

    void addTriangle_UV_Tag_CarbonLine(Vector3 v0, Vector3 v1, Vector3 v2, float uvTag)
    {
        aMD_TT_Sleeve.Add(MeshDraft.Triangl_UV_Tag(v0, v1, v2, uvTag));
    }

    void addTriangle_Skythe(Vector3 v0, Vector3 v1, Vector3 v2, float uvTag)
    {
        scythe_MD.Add(MeshDraft.Triangl_UV_Tag(v0, v1, v2, uvTag));
    }

    void addTriangle_UV_Tag_PQUAD(Vector3 v0, Vector3 v1, Vector3 v2, float uvTag)
    {
        aMD_PQUAD_TMP.Add(MeshDraft.Triangl_UV_Tag(v0, v1, v2, uvTag));
    }

    public Vector3 GetPointOnBezierCurve(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
    {
        float u = 1f - t;
        float t2 = t * t;
        float u2 = u * u;
        float u3 = u2 * u;
        float t3 = t2 * t;

        Vector3 result =
            (u3) * p0 +
            (3f * u2 * t) * p1 +
            (3f * u * t2) * p2 +
            (t3) * p3;

        return result;
    }

    /// <image url="$(SolutionDir)\EMB\hyperbole.png" scale="0.45"></image>
    /// 



}







/// <image url="$(SolutionDir)\EMB\CD.png" scale="0.16803" /> 


//int countDown = 10;
//void Update() // Original
//{
//    H_Editor_Label.transform.position  = tetra_Verts_GO[0].transform.position;
//    C_Editor_Label.transform.position  = transform.position;

//    Vector3 v = (tetra_Verts_GO[0].transform.localPosition + tetra_Verts_GO[1].transform.localPosition + tetra_Verts_GO[2].transform.localPosition) /3;

//    I_Editor_Label.transform.position = v + transform.position;
//    B_Editor_Label.transform.position = transform.position + base_Distance*tetra_Radius*v.normalized;

//    vCHn = (H_Editor_Label.transform.localPosition - C_Editor_Label.transform.localPosition).normalized;
//    vIH  = (H_Editor_Label.transform.localPosition - I_Editor_Label.transform.localPosition);
//    vIHn = vIH.normalized;

//    vT1n = Vector3.Dot(vCHn, vIHn) * vIHn;
//    vT2n = vCHn;

//    T1_Editor_Label.transform.localPosition = (vT1n * tangent_Base * tetra_Radius + B_Editor_Label.transform.localPosition);
//    T2_Editor_Label.transform.localPosition = (H_Editor_Label.transform.localPosition - vT2n * tangent_H * tetra_Radius);

//    if(frameCount-- == 0 || bPrint_Trace)
//    {
//        frameCount = 10;
//        build_Reference_Hyperbolic_Spline(B_Editor_Label.transform.localPosition,  T1_Editor_Label.transform.localPosition, 
//                                          T2_Editor_Label.transform.localPosition, H_Editor_Label.transform.localPosition,
//                                          I_Editor_Label.transform.localPosition);

//        if(--countDown == 0 || bPrint_Trace)
//        {
//            Print_Trace(B_Editor_Label.transform.localPosition, T1_Editor_Label.transform.localPosition,
//                                          T2_Editor_Label.transform.localPosition, H_Editor_Label.transform.localPosition,
//                                          I_Editor_Label.transform.localPosition);             
//        }
//    }
//    bPrint_Trace = false;
//    aMD_Spheres.Clear();
//    aMD_Spheres.Add(MeshDraft.Sphere(sphere_Radius, 36, 32));
//    cylinder_MD.Clear();

//    Set_Tetra_Verts(tetra_Radius);

//    float face_dist;
//    float tetra_Height;
//    Vector3 face_Norm;
//    float a, b;

//    if (sphere_Radius_Last != sphere_Radius)
//    {
//        for (int i = 0; i < 4; i++)
//        {
//            face_dist = face_Centers[i].magnitude;
//            tetra_Height = 1.33333f * tetra_Radius;
//            a = 2 * Mathf.Sqrt(2) / 3;
//            b = Mathf.Sqrt(sphere_Radius * sphere_Radius - tetra_Radius * tetra_Radius * 8f / 9f);
//            face_Norm = face_Centers[i].normalized;

//            spheres[i].transform.localPosition = face_Norm * (tetra_Radius / 3 + b);
//            spheres[i].transform.localRotation = Quaternion.LookRotation(sphere_pos[i]);
//            spheres[i].GetComponent<MeshFilter>().mesh = aMD_Spheres.ToMeshInternal();
//            tetra_Verts_GO[i].transform.localPosition = tetra_Verts[i];
//        }
//    }
//    sphere_Radius_Last = sphere_Radius;
//    tetra_Radius_Last = tetra_Radius;
//    //if ((sphere_Radius_Last != sphere_Radius || tetra_Radius_Last != tetra_Radius) && bTetraTerrean_On)
//    //{
//    aMD_TetraTerrean.Clear();

//    for (int i = 0; i < 4; i++)
//    {
//        aMD_tt_Faces[i].Clear();
//    }

//    Create_TETRA_TERREAN(all_Shape_Depth);
//    if (bSeparate_Faces)
//    {
//        for (int i = 0; i < 4; i++)
//        {
//            tt_Faces[i].GetComponent<MeshFilter>().mesh = aMD_tt_Faces[i].ToMeshInternal();
//        }
//    }
//    else
//    {
//        TetraTerrien_GO.GetComponent<MeshFilter>().mesh = aMD_TetraTerrean.ToMeshInternal();
//        TetraTerrien_GO.GetComponent<MeshFilter>().mesh.RecalculateNormals(60);
//    }

//    aMD_TT_Sleeve.Clear();
//    Create_TETRA_TERREAN_Sleeve();
//    TT_Sleeve_GO.GetComponent<MeshFilter>().mesh = aMD_TT_Sleeve.ToMeshInternal();
//    TT_Sleeve_GO.GetComponent<MeshFilter>().mesh.RecalculateNormals(60);

//    sphere_Radius_Last = sphere_Radius;
//    tetra_Radius_Last  = tetra_Radius;

//    if (bSpheresON)
//    {
//        ShowSpheres(true);
//    }
//    else
//    {
//        ShowSpheres(false);
//    }
//}

 

Unexpected Result

    public void OnShow_Differential_Rings()
    {
        if (amd == null)
        {
            amd = new ArgosMeshDraft();
        }
        else
        {
            amd.Clear();
        }

        GetComponent<MeshRenderer>().enabled = true;
        setColor(orbitColor);

        List<Vector3> vInnerLower = new List<Vector3>();
        List<Vector3> vOuterLower = new List<Vector3>();
        List<Vector3> vInnerUpper = new List<Vector3>();
        List<Vector3> vOuterUpper = new List<Vector3>();

        List<Vector3> vDisk = new List<Vector3>();

        float op = Mathf.PI * 2f;
        float delta_theta = op / 180;
        float theta = 0;

        Vector3 vPos_Lower;
        Vector3 vPos_Upper;
        Vector3 vNorm;
        float w_by_2 = cylinder_Width * 0.66666f;//to differentiate from Helix
        float rad = cylinder_Radius;

        Vector3 vZero = (cylinder_Offset + cylinder_Height / 2) * Vector3.up;
        vDisk.Add(vZero);

        for (int i = 0; i < 180; i++)
        {
            vPos_Lower = rad * Mathf.Cos(theta) * Vector3.right + rad * Mathf.Sin(theta) * Vector3.forward;

            float theta_Mod = theta % (Mathf.PI*2f/3f);
            float lout = rad;

            if (theta_Mod < 60)
            {
                lout = (1f - 0.5f * theta_Mod / (Mathf.PI/ 3f));
            }
            else
            {
                lout = (0.5f + 0.5f * (theta_Mod - (Mathf.PI / 3f)) / (Mathf.PI / 3f));
            }
            vPos_Lower *= lout;
            vPos_Upper = vPos_Lower;
            vNorm = vPos_Lower.normalized;
            vPos_Lower += cylinder_Offset * Vector3.up;

            vInnerLower.Add(vPos_Lower - w_by_2 * vNorm);
            vOuterLower.Add(vPos_Lower + w_by_2 * vNorm);

            vPos_Lower += (cylinder_Height / 2) * Vector3.up;
            vDisk.Add(vPos_Lower - w_by_2 * vNorm);

            vPos_Upper += cylinder_Height * Vector3.up + cylinder_Offset * Vector3.up;

            vInnerUpper.Add(vPos_Upper - w_by_2 * vNorm);
            vOuterUpper.Add(vPos_Upper + w_by_2 * vNorm);

            theta += delta_theta;
        }
        amd.Add(MeshDraft.Band(vOuterLower, vInnerLower));
        amd.Add(MeshDraft.Band(vInnerUpper, vOuterUpper));
        amd.Add(MeshDraft.Band(vInnerLower, vInnerUpper));
        amd.Add(MeshDraft.Band(vOuterUpper, vOuterLower));

        if (bAdd_Disk)
        {
            amd.Add(MeshDraft.TriangleFan(vDisk));
        }
        GetComponent<MeshFilter>().mesh = amd.ToMeshInternal();
    }