/// <summary>
        /// Update the parameters if anything changes
        /// </summary>
        void Update()
        {
            // early out if no updating is requested
            if (!AutoUpdate)
            {
                return;
            }
            // create a new mesh if the divisions have changed
            if (m_PreviousDivisions != Divisions)
            {
                CreateMeshRibbon();
            }

            // recompute vertex positions if helix has changed
            if (!Helix.AreEquivalent(m_PreviousHelix, Helix))
            {
                RecalculateVertexPositions();
            }
            // compute uvs if the scale has changed
            if (!AutoVScale && (m_PreviousVScale != VScale))
            {
                RecalculateUVCoordinates();
            }
            // store old values
            m_PreviousHelix     = new Helix(m_PreviousHelix);
            m_PreviousDivisions = Divisions;
            m_PreviousVScale    = VScale;
        }
Example #2
0
 /// <summary>
 /// Display a wire ribbon handle.
 /// </summary>
 /// <returns>A new helix with any modifications applied.</returns>
 /// <param name="baseId">Base identifier. Each handle has its own unique hash based off this value.</param>
 /// <param name="helix">Helix.</param>
 /// <param name="origin">Origin.</param>
 /// <param name="orientation">Orientation.</param>
 /// <param name="scale">Scale.</param>
 /// <param name="color">Color.</param>
 public static Helix WireRibbon(
     int baseId, Helix helix, Vector3 origin, Quaternion orientation, Vector3 scale, Color color
     )
 {
     return(DoHelixHandle(
                baseId, helix, origin, orientation, scale, color, s_SmoothDivisionCount, FillMode.Wire, ShapeMode.Ribbon
                ));
 }
Example #3
0
 /// <summary>
 /// Display a solid helix handle.
 /// </summary>
 /// <returns>A new helix with any modifications applied.</returns>
 /// <param name="baseId">Base identifier. Each handle has its own unique hash based off this value.</param>
 /// <param name="helix">Helix.</param>
 /// <param name="origin">Origin.</param>
 /// <param name="orientation">Orientation.</param>
 /// <param name="scale">Scale.</param>
 /// <param name="color">Color.</param>
 public static Helix SolidHelix(
     int baseId, Helix helix, Vector3 origin, Quaternion orientation, Vector3 scale, Color color
     )
 {
     return(DoHelixHandle(
                baseId, helix, origin, orientation, scale, color, s_SmoothDivisionCount, FillMode.Solid, ShapeMode.Helix
                ));
 }
        /// <summary>
        /// Creates the mesh ribbon.
        /// </summary>
        private void CreateMeshRibbon()
        {
            // validate data
            Divisions = Mathf.Max(Divisions, 1);
            // compute vertex positions, normals, and uv coordinates
            Vector3[] vertices = new Vector3[2 * (Divisions + 1)];
            Vector3[] normals  = new Vector3[vertices.Length];
            Vector2[] uv       = new Vector2[vertices.Length];
            // cache divisions
            float oneHalfLengthOverDivisions = 0.5f * Helix.Length / Divisions;
            float oneHalfActualUVScale       = 0.5f / VScale / Divisions;

            for (int i = 0; i < vertices.Length; i += 2)
            {
                // compute vertex positions
                float parameter = (float)(i) * oneHalfLengthOverDivisions;
                vertices[i]     = Helix.Evaluate(parameter);
                vertices[i + 1] = Helix.EvaluateOpposite(parameter);
                // compute uvs
                uv[i]     = new Vector2(1f, (float)(i) * oneHalfActualUVScale);
                uv[i + 1] = new Vector2(0f, uv[i].y);
            }
            // compute triangles
            int[] triangles      = new int[Divisions * 6];
            bool  isEvenTriangle = true;
            int   stepCount      = 3;

            for (int i = 0; i < triangles.Length; i += stepCount)
            {
                int start = i / stepCount;
                if (isEvenTriangle)
                {
                    triangles[i]     = start;
                    triangles[i + 1] = start + 1;
                    triangles[i + 2] = start + 2;
                }
                else
                {
                    triangles[i]     = start + 2;
                    triangles[i + 1] = start + 1;
                    triangles[i + 2] = start;
                }
                isEvenTriangle = !isEvenTriangle;
            }
            // create the mesh
            MeshFilter.mesh.Clear();
            MeshFilter.mesh.vertices  = vertices;
            MeshFilter.mesh.normals   = normals;
            MeshFilter.mesh.triangles = triangles;
            MeshFilter.mesh.uv        = uv;
            MeshFilter.mesh.RecalculateNormals();
            MeshFilter.mesh.RecalculateBounds();
            // scale uvs if required
            if (AutoVScale)
            {
                RecalculateUVCoordinates();
            }
        }
Example #5
0
 /// <summary>
 /// Initializes a new instance of the <see cref="Helix"/> class.
 /// </summary>
 /// <param name="helix">Helix.</param>
 /// <exception cref='System.ArgumentNullException'>
 /// Is thrown when a <see langword="null" /> Helix is passed.
 /// </exception>
 public Helix(Helix helix)
 {
     if (helix == null)
     {
         throw new System.ArgumentNullException("helix");
     }
     else
     {
         this.Length = helix.Length;
         this.AutoSmoothCurveTangents = helix.AutoSmoothCurveTangents;
         Twist = helix.Twist;
         Width = helix.Width;
     }
 }
        /// <summary>
        /// Recompute vertex positions, as when the helix changes.
        /// </summary>
        private void RecalculateVertexPositions()
        {
            Vector3[] vertices = MeshFilter.mesh.vertices;
            float     oneHalfLengthOverDivisions = 0.5f * Helix.Length / Divisions;

            for (int i = 0; i < vertices.Length; i += 2)
            {
                float parameter = (float)(i) * oneHalfLengthOverDivisions;
                vertices[i]     = Helix.Evaluate(parameter);
                vertices[i + 1] = Helix.EvaluateOpposite(parameter);
            }
            MeshFilter.mesh.vertices = vertices;
            if (AutoVScale)
            {
                RecalculateUVCoordinates();
            }
        }
Example #7
0
        /// <summary>
        /// Display a helix handle.
        /// </summary>
        /// <returns>A new helix with any modifications applied.</returns>
        /// <param name="baseId">Base identifier. Each handle has its own unique hash based off this value.</param>
        /// <param name="helix">Helix.</param>
        /// <param name="origin">Origin.</param>
        /// <param name="orientation">Orientation.</param>
        /// <param name="scale">Scale.</param>
        /// <param name="color">Color.</param>
        /// <param name="divisions">Divisions.</param>
        /// <param name="fillMode">Fill mode.</param>
        /// <param name="shapeMode">Shape mode.</param>
        private static Helix DoHelixHandle(
            int baseId,
            Helix helix,
            Vector3 origin,
            Quaternion orientation,
            Vector3 scale,
            Color color,
            int divisions,
            FillMode fillMode,
            ShapeMode shapeMode
            )
        {
            // early out if the scale is too small
            if (scale.sqrMagnitude < s_RequiredMinHandleChange)
            {
                Debug.LogWarning("Scale vector for helix handle is too close to 0. Handle is disabled");
                return(null);
            }
            // create copy of helix
            helix = new Helix(helix);
            // store the handle matrix
            Matrix4x4 oldMatrix = Handles.matrix;

            Handles.matrix *= Matrix4x4.TRS(origin, orientation, scale);
            // set the helix handle's color
            Color oldColor = Handles.color;

            Handles.color = color;
            // validate the number of divisions
            divisions = Mathf.Max(1, divisions);
            // create a handle to adjust length
            DoHelixLengthHandle(baseId, helix);
            // next, create handles for the twist curve
            DoHelixTwistHandles(baseId, helix);
            // next, create handles for the width curve
            DoHelixWidthHandles(baseId, helix);
            // draw lines to visualize the helix
            DrawHelix(helix, divisions, fillMode, shapeMode);
            // reset handle color
            Handles.color = oldColor;
            // reset handle matrix
            Handles.matrix = oldMatrix;
            // return result
            return(helix);
        }
Example #8
0
        /// <summary>
        /// Displays the helix twist curve handles.
        /// </summary>
        /// <param name="baseId">Base identifier.</param>
        /// <param name="helix">Helix.</param>
        private static void DoHelixTwistHandles(int baseId, Helix helix)
        {
            float          v;
            AnimationCurve width    = helix.Width;
            AnimationCurve newTwist = new AnimationCurve(helix.Twist.keys);

            Keyframe[] newKeys = newTwist.keys;
            for (int i = 0; i < newKeys.Length; ++i)
            {
                // compute the disc center
                Vector3 discCenter = Vector3.forward * newKeys[i].time * helix.Length;
                v = newKeys[i].value;
                // create an arc handle
                float helixWidth = width.Evaluate(newKeys[i].time);
                v = ArcHandles.SolidAngle(
                    ObjectX.GenerateHashCode(baseId, s_TwistHandleHash, i, 1),
                    v, discCenter,
                    Quaternion.LookRotation(Vector3.right, Vector3.forward),
                    helixWidth,
                    string.Format("{0:0} Degrees", v)
                    );
                Handles.DrawWireDisc(discCenter, Vector3.forward, helixWidth);
                newKeys[i].value = v;
                // create time adjustment handles for intermediary keys
                if (i == 0 || i == newKeys.Length - 1)
                {
                    continue;
                }
                v = newKeys[i].time * helix.Length;
                int id = ObjectX.GenerateHashCode(baseId, s_TwistHandleHash, i, 2);
                v = LinearHandles.Cone(id, v, Vector3.zero, Vector3.forward, capScale: s_TimeAdjustHandleSize);
                v = Mathf.Clamp(
                    v / helix.Length,
                    Mathf.Epsilon * 2f,
                    1f - Mathf.Epsilon * 2f
                    );
                if (Mathf.Abs(v - newKeys[i].time) > s_RequiredMinHandleChange)
                {
                    newKeys[i].time = v;
                }
                // TODO: Fix problems with keys moving over each other
            }
            newTwist.keys = newKeys;
            helix.Twist   = newTwist;
        }
Example #9
0
        /// <summary>
        /// Display a helix length handle.
        /// </summary>
        /// <param name="baseId">Base identifier.</param>
        /// <param name="helix">Helix.</param>
        private static void DoHelixLengthHandle(int baseId, Helix helix)
        {
            float length = helix.Length;
            int   id     = ObjectX.GenerateHashCode(baseId, s_LengthHandleHash);

            length = LinearHandles.Arrow(
                id,
                length,
                Vector3.zero,
                Vector3.forward,
                string.Format("Length: {0:0.###}", length),
                s_LengthHandleSize
                );
            if (Mathf.Abs(length - helix.Length) < s_RequiredMinHandleChange)
            {
                length = helix.Length;
            }
            helix.Length = length;
        }
Example #10
0
        /// <summary>
        /// Displays helix width handles.
        /// </summary>
        /// <param name="baseId">Base identifier.</param>
        /// <param name="helix">Helix.</param>
        private static void DoHelixWidthHandles(int baseId, Helix helix)
        {
            float          v;
            AnimationCurve twist    = helix.Twist;
            AnimationCurve newWidth = helix.Width;

            Keyframe[] newKeys = newWidth.keys;
            for (int i = 0; i < newKeys.Length; ++i)
            {
                // compute the disc center
                Vector3 discCenter = Vector3.forward * newKeys[i].time * helix.Length;
                // compute the angle of the handle around the length
                Quaternion angle = Quaternion.AngleAxis(twist.Evaluate(newKeys[i].time), Vector3.forward);
                // create a cone handle with a slight offset to not overlap the arc handle
                v = newKeys[i].value + s_WidthHandleOffset;
                int id = ObjectX.GenerateHashCode(baseId, s_WidthHandleHash, i, 1);
                v = LinearHandles.Cone(id, v, discCenter, angle * Vector3.right, capScale: s_WidthHandleSize);
                newKeys[i].value = v - s_WidthHandleOffset;
                // create time adjustment handles for intermediary keys
                if (i == 0 || i == newKeys.Length - 1)
                {
                    continue;
                }
                v  = newKeys[i].time * helix.Length;
                id = ObjectX.GenerateHashCode(baseId, s_WidthHandleHash, i, 2);
                v  = LinearHandles.Cone(id, v, Vector3.zero, Vector3.forward, capScale: s_TimeAdjustHandleSize);
                v  = Mathf.Clamp(
                    v / helix.Length,
                    Mathf.Epsilon * 2f,
                    1f - Mathf.Epsilon * 2f
                    );
                if (Mathf.Abs(v - newKeys[i].time) > s_RequiredMinHandleChange)
                {
                    newKeys[i].time = v;
                }
                // TODO: Fix problems with keys moving over each other
            }
            newWidth.keys = newKeys;
            helix.Width   = newWidth;
        }
Example #11
0
 /// <summary>
 /// Initialize
 /// </summary>
 void Start()
 {
     // create the mesh
     if (MeshFilter == null)
     {
         MeshFilter = gameObject.AddComponent <MeshFilter>();
     }
     if (MeshFilter.mesh == null)
     {
         MeshFilter.mesh      = new Mesh();
         MeshFilter.mesh.name = name + " Helix Mesh Ribbon";
     }
     CreateMeshRibbon();
     if (AutoVScale)
     {
         AutoComputeVScale();
     }
     // store the old values
     m_PreviousHelix     = Helix;
     m_PreviousDivisions = Divisions;
     m_PreviousVScale    = VScale;
 }
Example #12
0
 /// <summary>
 /// Tests if two helices are equivalent.
 /// </summary>
 /// <returns>
 /// <see langword="true"/> if all field values are equivalent; otherwise, <see langword="false"/>.
 /// </returns>
 /// <param name="h1">Helix 1.</param>
 /// <param name="h2">Helix 2.</param>
 public static bool AreEquivalent(Helix h1, Helix h2)
 {
     if (h1 == null)
     {
         return(h2 == null);
     }
     else if (h2 == null)
     {
         return(false);
     }
     else if (
         h1.AutoSmoothCurveTangents == h2.AutoSmoothCurveTangents &&
         h1.Length == h2.Length &&
         h1.Twist.IsValueEqualTo(h2.Twist) &&
         h1.Width.IsValueEqualTo(h2.Width)
         )
     {
         return(true);
     }
     else
     {
         return(false);
     }
 }
Example #13
0
 public static Helix WireRibbon(Helix helix, Vector3 origin, Quaternion orientation, Vector3 scale, Color color)
 {
     return(WireRibbon(0, helix, origin, orientation, scale, color));
 }
Example #14
0
 public static void SolidHelix(Helix helix, Vector3 origin, Quaternion orientation, Vector3 scale, Color color)
 {
     SolidHelix(0, helix, origin, orientation, scale, color);
 }
Example #15
0
        /// <summary>
        /// Draws the helix.
        /// </summary>
        /// <param name="helix">Helix.</param>
        /// <param name="divisions">Divisions.</param>
        /// <param name="fillMode">Fill mode.</param>
        /// <param name="shapeMode">Shape mode.</param>
        private static void DrawHelix(Helix helix, int divisions, FillMode fillMode, ShapeMode shapeMode)
        {
            // early out if not repaint phase
            if (Event.current.type != EventType.Repaint)
            {
                return;
            }
            // initialize mesh properties
            Vector3[] vertices  = new Vector3[divisions * 2 + 2];
            Vector2[] uv        = new Vector2[vertices.Length];
            Color[]   colors    = new Color[vertices.Length];
            int[]     triangles = new int[divisions * 12];
            // compute mesh data
            bool  isRibbon = shapeMode == ShapeMode.Ribbon;
            bool  isSolid  = fillMode != FillMode.Wire;
            bool  isMesh   = fillMode == FillMode.Mesh;
            float oneHalfLengthOverDivisions = 0.5f * helix.Length / divisions;

            for (int i = 0; i < vertices.Length; i += 2)
            {
                // current parameter value
                float parameter = (float)(i) * oneHalfLengthOverDivisions;
                // compute vertex
                vertices[i] = helix.Evaluate(parameter);
                // draw line
                if (i > 0)
                {
                    Handles.DrawLine(vertices[i - 2], vertices[i]);
                }
                // compute corresponding vertex
                if (isRibbon)
                {
                    vertices[i + 1] = helix.EvaluateOpposite(parameter);
                    // draw opposite line
                    if (i > 0)
                    {
                        Handles.DrawLine(vertices[i - 1], vertices[i + 1]);
                    }
                }
                else
                {
                    vertices[i + 1].z = vertices[i].z;
                }
                // draw connecting lines if requested
                if (isMesh)
                {
                    Handles.DrawLine(vertices[i], vertices[i + 1]);
                }
                // populate colors
                colors[i]     = Handles.color;
                colors[i].a  *= SceneGUI.FillAlphaScalar;
                colors[i + 1] = colors[i];
            }
            // compute triangles
            int stepCount = 6;

            for (int i = 0; i < triangles.Length; i += stepCount)
            {
                int start = i / stepCount;
                triangles[i]     = start;
                triangles[i + 1] = start + 1;
                triangles[i + 2] = start + 2;
                triangles[i + 3] = start + 2;
                triangles[i + 4] = start + 1;
                triangles[i + 5] = start;
            }
            // update mesh
            if (s_FillMesh == null)
            {
                s_FillMesh           = new Mesh();
                s_FillMesh.hideFlags = HideFlags.DontSave;
            }
            s_FillMesh.Clear();
            s_FillMesh.vertices  = vertices;
            s_FillMesh.colors    = colors;
            s_FillMesh.triangles = triangles;
            s_FillMesh.uv        = uv;
            s_FillMesh.RecalculateNormals();
            // draw the mesh
            if (isSolid)
            {
                Graphics.DrawMeshNow(s_FillMesh, Handles.matrix);
            }
        }