Пример #1
0
        public static Quaternion RotateAlongHelix(float curvature, float torsion, float arcLength)
        {
            // No torsion = just a circular rotation.
            if (Mathf.Abs(torsion) < 1e-6)
            {
                return(rotateAlongCircle(curvature, arcLength));
            }
            // Torsion but no curvature = rotate about forward axis in a screw motion
            if (curvature < 1e-6)
            {
                float rotationAngle = torsion * arcLength;
                return(Quaternion.AngleAxis(Mathf.Rad2Deg * rotationAngle, Vector3.forward));
            }

            // Correct because the initial tangent of a helix isn't (0, 0, 1),
            // but we want it to be for linking up of helical pieces.
            OrthonormalFrame zeroFrame   = FrameAlongHelix(curvature, torsion, 0);
            Quaternion       correctiveR = Quaternion.Inverse(Quaternion.LookRotation(zeroFrame.T, zeroFrame.N));

            OrthonormalFrame frame = FrameAlongHelix(curvature, torsion, arcLength);

            // Corrective rotation so that initial tangent is forward.
            //Quaternion r = Quaternion.FromToRotation(Vector3.forward, Vector3.down);

            return(correctiveR * Quaternion.LookRotation(frame.T, frame.N));
        }
Пример #2
0
        TorsionImpulseCurve MakeTorsionImpulseCurve(List <float> impulses, List <float> arcPoints,
                                                    List <float> curvatureList, List <float> torsionList)
        {
            Debug.Log("Arc length of DC = " + ArcLength);

            Vector3          startingNormal = Vector3.Cross(startingBinormal, startingTangent);
            OrthonormalFrame startFrame     = new OrthonormalFrame(startingTangent, startingNormal, startingBinormal);

            List <float> arcSteps = new List <float>();

            for (int i = 0; i < arcPoints.Count - 1; i++)
            {
                arcSteps.Add(arcPoints[i + 1] - arcPoints[i]);
            }

            GameObject obj = new GameObject();

            obj.name = "TorsionApproxCurve";
            TorsionImpulseCurve impulseCurve = obj.AddComponent <TorsionImpulseCurve>();

            impulseCurve.InitFromData(impulses, arcSteps,
                                      curvatureList, torsionList,
                                      startFrame, StartingPoint);

            impulseCurve.SetMaterial(lineRender.material);

            return(impulseCurve);
        }
Пример #3
0
        TorsionImpulseCurve MakeTorsionImpulseCurve(List <float> impulses, List <float> arcPoints, float constTorsion)
        {
            float averageCurvature = AverageCurvature();

            Vector3          startingNormal = Vector3.Cross(startingBinormal, startingTangent);
            OrthonormalFrame startFrame     = new OrthonormalFrame(startingTangent, startingNormal, startingBinormal);

            List <float> arcSteps = new List <float>();

            for (int i = 0; i < arcPoints.Count - 1; i++)
            {
                arcSteps.Add(arcPoints[i + 1] - arcPoints[i]);
            }

            GameObject obj = new GameObject();

            obj.name = "TorsionApproxCurve";
            TorsionImpulseCurve impulseCurve = obj.AddComponent <TorsionImpulseCurve>();

            impulseCurve.InitFromData(impulses, arcSteps,
                                      averageCurvature, constTorsion,
                                      startFrame, StartingPoint);

            impulseCurve.SetMaterial(lineRender.material);
            impulseCurve.BulbShrinkRatio = ActualShrinkRatio;

            return(impulseCurve);
        }
        public void InitFromData(List <float> impulses, List <float> arcSteps,
                                 List <float> curvature, List <float> torsion,
                                 OrthonormalFrame initialFrame, Vector3 initialPos)
        {
            if (initialFrame.T.magnitude < 0.01f ||
                initialFrame.B.magnitude < 0.01f ||
                initialFrame.N.magnitude < 0.01f)
            {
                throw new System.Exception("Initial frame has zeroes");
            }

            displayPoints = new List <Vector3>();
            lRenderer     = GetComponent <LineRenderer>();
            segments      = new List <CurveSegment>();

            // Create the initial segment
            CurveSegment prevHelix = new CurveSegment(initialPos, curvature[0], -torsion[0], 0, arcSteps[0], initialFrame);

            segments.Add(prevHelix);
            AddPointsOfSegment(prevHelix);

            float len = arcSteps[0];

            // For each impulse, make a new segment rotated by that angle.
            for (int i = 1; i < impulses.Count; i++)
            {
                float impulse = impulses[i];
                float arcStep = arcSteps[i];
                // New start point is the end of the previous curve segment.
                Vector3 newBase = TransformedHelixPoint(prevHelix, prevHelix.arcLength);
                // New frame is the frame rotated to the end of the segment.
                OrthonormalFrame newFrame = TransformedHelixFrame(prevHelix, prevHelix.arcLength);

                // Apply the torsion impulse as well.
                Quaternion impulseRot = Quaternion.AngleAxis(Mathf.Rad2Deg * impulse, newFrame.T);
                newFrame = newFrame.RotatedBy(impulseRot);

                int clampedC = Mathf.Min(i, curvature.Count - 1);
                int clampedT = Mathf.Min(i, torsion.Count - 1);

                prevHelix = new CurveSegment(newBase, curvature[clampedC], -torsion[clampedT], impulse, arcStep, newFrame);

                len += arcStep;

                AddPointsOfSegment(prevHelix);
                segments.Add(prevHelix);
            }

            // Add the last point of the last curve
            displayPoints.Add(TransformedHelixPoint(prevHelix, arcSteps[arcSteps.Count - 1]));

            // Set up line renderer
            lRenderer.SetVertexCount(displayPoints.Count);
            lRenderer.SetPositions(displayPoints.ToArray());
            lRenderer.SetWidth(0.1f, 0.1f);

            //Debug.Log("Arc length of TIC = " + ArcLength);
        }
 public CurveSegment(Vector3 basePos, float curv, float tors, float imp, float len, OrthonormalFrame f)
 {
     startPosition = basePos;
     curvature     = curv;
     torsion       = tors;
     impulse       = imp;
     arcLength     = len;
     frame         = f;
 }
Пример #6
0
        public void ComputeFrenet(Vector3 prevPos, Vector3 nextPos)
        {
            Vector3 tangent     = (nextPos - position).normalized;
            Vector3 prevTangent = (position - prevPos).normalized;
            Vector3 binormal    = Vector3.Cross(prevTangent, tangent).normalized;

            Vector3 normal = Vector3.Cross(binormal, tangent);

            frenetFrame = new OrthonormalFrame(tangent, normal, binormal);
        }
        OrthonormalFrame TransformedHelixFrame(CurveSegment cs, float arcLen)
        {
            // Apply the local helix rotation.
            Quaternion oldRot = Quaternion.LookRotation(cs.frame.T, cs.frame.N);
            Quaternion newRot = oldRot *
                                TelescopeUtils.RotateAlongHelix(cs.curvature, cs.torsion, arcLen)
                                * Quaternion.Inverse(oldRot);
            OrthonormalFrame newFrame = cs.frame.RotatedBy(newRot);

            return(newFrame);
        }
        public void InitFromData(List <float> impulses, List <float> arcSteps,
                                 float curvature, float torsion,
                                 OrthonormalFrame initialFrame, Vector3 initialPos)
        {
            List <float> c = new List <float>();

            c.Add(curvature);
            List <float> t = new List <float>();

            t.Add(torsion);
            InitFromData(impulses, arcSteps, c, t, initialFrame, initialPos);
        }
Пример #9
0
        void SetPositionOnCurve()
        {
            if (!dCurve)
            {
                return;
            }
            transform.position = dCurve.PositionAtPoint(arcLength);
            OrthonormalFrame frame = bishop ? dCurve.BishopAtPoint(arcLength) : dCurve.FrenetAtPoint(arcLength);

            Quaternion r = Quaternion.LookRotation(frame.T, frame.N);

            transform.rotation = r;
        }
Пример #10
0
        public OrthonormalFrame PropagateBishop(Vector3 prevPos, Vector3 nextPos, OrthonormalFrame prevFrame)
        {
            Vector3 tangent     = (nextPos - position).normalized;
            Vector3 prevTangent = (position - prevPos).normalized;
            Vector3 binormal    = Vector3.Cross(prevTangent, tangent).normalized;

            float angle = TelescopeUtils.AngleBetween(prevTangent, tangent, binormal);

            Quaternion rotation = Quaternion.AngleAxis(angle, binormal);

            OrthonormalFrame rotated = prevFrame.RotatedBy(rotation);

            bishopFrame = rotated;
            return(rotated);
        }
Пример #11
0
        public OrthonormalFrame BishopAtPoint(float t)
        {
            int   segNum;
            float scaledT;

            ScaleArcParameter(out segNum, out scaledT, t);

            if (segNum == discretizedPoints.Count - 1)
            {
                return(discretizedPoints[segNum].bishopFrame);
            }

            return(OrthonormalFrame.Slerp(discretizedPoints[segNum].bishopFrame,
                                          discretizedPoints[segNum + 1].bishopFrame,
                                          scaledT));
        }
Пример #12
0
        public static OrthonormalFrame Slerp(OrthonormalFrame frame1, OrthonormalFrame frame2, float t)
        {
            Vector3 tangent1 = frame1.T;
            Vector3 tangent2 = frame2.T;
            Vector3 tangent  = Vector3.Slerp(tangent1, tangent2, t);

            Vector3 normal1 = frame1.N;
            Vector3 normal2 = frame2.N;
            Vector3 normal  = Vector3.Slerp(normal1, normal2, t);

            Vector3 binormal1 = frame1.B;
            Vector3 binormal2 = frame2.B;
            Vector3 binormal  = Vector3.Slerp(binormal1, binormal2, t);

            return(new OrthonormalFrame(tangent, normal, binormal));
        }
Пример #13
0
        void SetPositionOnCurve()
        {
            if (!dCurve)
            {
                return;
            }

            Vector3    p      = dCurve.transform.position;
            Quaternion frameR = dCurve.transform.rotation;

            transform.position = frameR * dCurve.PositionAtPoint(arcLength) + p;
            OrthonormalFrame frame = dCurve.FrameAtPoint(arcLength).RotatedBy(frameR);

            Quaternion r = Quaternion.LookRotation(frame.T, frame.N);

            transform.rotation = r;
        }
Пример #14
0
        public static OrthonormalFrame FrameAlongHelix(float curvature, float torsion, float arcLength)
        {
            // No torsion = just a circular rotation.
            if (Mathf.Abs(torsion) < 1e-6)
            {
                OrthonormalFrame defaultFrame = new OrthonormalFrame(Vector3.forward, Vector3.up,
                                                                     Vector3.Cross(Vector3.forward, Vector3.up));
                Quaternion r = rotateAlongCircle(curvature, arcLength);
                return(defaultFrame.RotatedBy(r));
            }
            // Torsion but no curvature = rotate about forward axis in a screw motion
            if (curvature < 1e-6)
            {
                OrthonormalFrame defaultFrame = new OrthonormalFrame(Vector3.forward, Vector3.up,
                                                                     Vector3.Cross(Vector3.forward, Vector3.up));
                float      rotationAngle = torsion * arcLength;
                Quaternion r             = Quaternion.AngleAxis(Mathf.Rad2Deg * rotationAngle, Vector3.forward);
                return(defaultFrame.RotatedBy(r));
            }

            float sumSq  = curvature * curvature + torsion * torsion;
            float a      = curvature / sumSq;
            float b      = torsion / sumSq;
            float abSqrt = Mathf.Sqrt(a * a + b * b);

            float t = arcLength;

            Vector3 tangent = new Vector3(b,
                                          -a * Mathf.Sin(t / abSqrt),
                                          a * Mathf.Cos(t / abSqrt)) / abSqrt;

            tangent.y *= -1;
            tangent.Normalize();

            Vector3 normal = new Vector3(0,
                                         Mathf.Cos(t / abSqrt),
                                         Mathf.Sin(t / abSqrt)) * -1;

            normal.y *= -1;
            normal.Normalize();

            Vector3 binormal = Vector3.Cross(tangent, normal);

            return(new OrthonormalFrame(tangent, normal, binormal));
        }
Пример #15
0
        void FixFrenetBackward(int start)
        {
            // Find the most recent valid frame
            int validIndex = start - 1;

            while (discretizedPoints[validIndex].frenetFrame.B.magnitude == 0)
            {
                validIndex--;
            }

            OrthonormalFrame validFrame = discretizedPoints[validIndex].frenetFrame;

            // No need to un-rotate because the tangents are already aligned.
            for (int i = start; i > validIndex; i--)
            {
                discretizedPoints[i].frenetFrame = validFrame;
            }
        }
        /// <summary>
        /// Rotate this entire curve by the given rotation.
        /// </summary>
        /// <param name="rotation"></param>
        public void Rotate(Quaternion rotation)
        {
            OrthonormalFrame initFrame   = segments[0].frame.RotatedBy(rotation);
            CurveSegment     initSegment = segments[0];

            initSegment.frame = initFrame;
            segments[0]       = initSegment;

            CurveSegment prevHelix = segments[0];
            float        len       = prevHelix.arcLength;

            displayPoints.Clear();
            AddPointsOfSegment(prevHelix);

            for (int i = 1; i < segments.Count; i++)
            {
                // New start point is the end of the previous curve segment.
                Vector3 newBase = TransformedHelixPoint(prevHelix, prevHelix.arcLength);
                // New frame is the frame rotated to the end of the segment.
                OrthonormalFrame newFrame = TransformedHelixFrame(prevHelix, prevHelix.arcLength);

                // Apply the torsion impulse as well.
                Quaternion impulseRot = Quaternion.AngleAxis(Mathf.Rad2Deg * segments[i].impulse, newFrame.T);
                newFrame = newFrame.RotatedBy(impulseRot);

                prevHelix = new CurveSegment(newBase, segments[i].curvature, segments[i].torsion,
                                             segments[i].impulse, segments[i].arcLength, newFrame);

                len += segments[i].arcLength;

                AddPointsOfSegment(prevHelix);
                segments[i] = prevHelix;
            }

            // Add the last point of the last curve
            displayPoints.Add(TransformedHelixPoint(prevHelix, prevHelix.arcLength));

            // Set up line renderer
            lRenderer.SetVertexCount(displayPoints.Count);
            lRenderer.SetPositions(displayPoints.ToArray());
            lRenderer.SetWidth(0.1f, 0.1f);
        }
Пример #17
0
        public static Vector3 TranslateAlongHelix(float curvature, float torsion, float arcLength)
        {
            if (Mathf.Abs(torsion) < 1e-6)
            {
                return(translateAlongCircle(curvature, arcLength));
            }
            if (curvature < 1e-6)
            {
                return(Vector3.forward * arcLength);
            }

            // Correct because the initial tangent of a helix isn't (0, 0, 1),
            // but we want it to be for linking up of helical pieces.
            OrthonormalFrame zeroFrame   = FrameAlongHelix(curvature, torsion, 0);
            Quaternion       correctiveR = Quaternion.Inverse(Quaternion.LookRotation(zeroFrame.T, zeroFrame.N));

            float sumSq = curvature * curvature + torsion * torsion;
            float a     = curvature / sumSq;
            float b     = torsion / sumSq;

            float abSqrt = Mathf.Sqrt(a * a + b * b);

            float t = arcLength;

            Vector3 pos = new Vector3(b * t / abSqrt,
                                      a * Mathf.Cos(t / abSqrt),
                                      a * Mathf.Sin(t / abSqrt));

            // This treats (0, 0, 0) as the center and (0, 0, a) as the first point.
            // Want to treat (0, -a, 0) as the first point, so rotate.
            //Quaternion r = Quaternion.FromToRotation(Vector3.forward, Vector3.down);

            pos.y *= -1;

            // Shift so that (0, a, 0) is the center and (0, 0, 0) is the first point.
            pos += (a * Vector3.up);
            return(correctiveR * pos);
        }
Пример #18
0
        void FixFrenetForward(int start)
        {
            int validIndex = start + 1;

            // Search forward until we find a valid Frenet frame
            while (discretizedPoints[validIndex].frenetFrame.B.magnitude == 0)
            {
                validIndex++;
            }

            // Un-rotate the valid frame so that it's aligned with the current tangent
            OrthonormalFrame nextFrame       = discretizedPoints[validIndex].frenetFrame;
            Vector3          nextTangent     = nextFrame.T;
            Vector3          currentTangent  = discretizedPoints[validIndex - 1].frenetFrame.T;
            Quaternion       reverseRotation = Quaternion.FromToRotation(nextTangent, currentTangent);
            OrthonormalFrame backFrame       = nextFrame.RotatedBy(reverseRotation);

            // Set the aligned frame to all of the points missing frames
            for (int i = start; i < validIndex; i++)
            {
                discretizedPoints[i].frenetFrame = backFrame;
            }
        }
        public void RecomputeAndRedraw()
        {
            displayPoints.Clear();
            AddPointsOfSegment(segments[0]);

            // For each impulse, recompute the curve along that segment.
            for (int i = 1; i < segments.Count; i++)
            {
                CurveSegment prevHelix = segments[i - 1];
                float        impulse   = segments[i].impulse;
                // New start point is the end of the previous curve segment.
                Vector3 newBase = TransformedHelixPoint(prevHelix, prevHelix.arcLength);
                // New frame is the frame rotated to the end of the segment.
                OrthonormalFrame newFrame = TransformedHelixFrame(prevHelix, prevHelix.arcLength);

                CurveSegment s = segments[i];
                s.frame         = newFrame;
                s.startPosition = newBase;
                segments[i]     = s;

                // Apply the torsion impulse as well.
                Quaternion impulseRot = Quaternion.AngleAxis(Mathf.Rad2Deg * impulse, newFrame.T);
                newFrame = newFrame.RotatedBy(impulseRot);

                AddPointsOfSegment(segments[i]);
            }

            // Add the last point of the last curve
            int last = segments.Count - 1;

            displayPoints.Add(TransformedHelixPoint(segments[last], segments[last].arcLength));

            // Set up line renderer
            lRenderer.SetVertexCount(displayPoints.Count);
            lRenderer.SetPositions(displayPoints.ToArray());
            lRenderer.SetWidth(0.1f, 0.1f);
        }
Пример #20
0
        void AddAxes()
        {
            foreach (TelescopeShell shell in segment.shells)
            {
                GameObject          axisObj = new GameObject();
                TorsionImpulseCurve axis    = axisObj.AddComponent <TorsionImpulseCurve>();

                List <float> impulses = new List <float>();
                impulses.Add(0);
                List <float> arcs = new List <float>();
                arcs.Add(shell.length);

                OrthonormalFrame frame = new OrthonormalFrame(Vector3.forward, Vector3.up, Vector3.left);

                axis.InitFromData(impulses, arcs, shell.curvature, -shell.torsion, frame, Vector3.zero);
                LineRenderer axisLR = axis.GetComponent <LineRenderer>();
                axisLR.useWorldSpace = false;
                axisLR.material      = lineMaterial;

                axis.transform.parent        = shell.transform;
                axis.transform.localPosition = Vector3.zero;
                axis.transform.localRotation = Quaternion.identity;
            }
        }
 public void SetOrientation(OrthonormalFrame frame)
 {
     transform.rotation = Quaternion.LookRotation(frame.T, frame.B);
 }
        public TelescopeSegment MakeTelescope(float startRadius, bool reverse = false)
        {
            List <TelescopeParameters> tParams = new List <TelescopeParameters>();

            CurveSegment initialSeg = (reverse) ? segments[segments.Count - 1] : segments[0];

            float wallThickness = Constants.WALL_THICKNESS;
            float currRadius    = startRadius * BulbShrinkRatio;

            TelescopeParameters initial = new TelescopeParameters(initialSeg.arcLength, currRadius,
                                                                  Constants.WALL_THICKNESS, initialSeg.curvature, initialSeg.torsion, 0);

            tParams.Add(initial);

            for (int i = 1; i < segments.Count; i++)
            {
                int index = (reverse) ? segments.Count - 1 - i : i;

                /*
                 * if (i == 2 && StartJuncture && StartJuncture.childCurves.Count == 5)
                 * {
                 *  Debug.Log("Hack to create lizard toenails, please delete once done");
                 *  currRadius -= 1.5f * wallThickness;
                 * }*/

                // Subtract the wall thickness of the previous piece
                currRadius -= wallThickness;

                float curvature = (reverse) ? segments[index].curvature : segments[index].curvature;
                float torsion   = (reverse) ? segments[index].torsion : segments[index].torsion;
                float impulse   = (reverse) ? -segments[index + 1].impulse : -segments[index].impulse;
                impulse *= Mathf.Rad2Deg;

                TelescopeParameters p = new TelescopeParameters(segments[index].arcLength, currRadius,
                                                                wallThickness, curvature, torsion, impulse);
                tParams.Add(p);
            }

            GameObject obj = new GameObject();

            obj.name = "telescope" + (numScopes++);

            TelescopeSegment segment = obj.AddComponent <TelescopeSegment>();

            segment.paramMode = SegmentParametersMode.Concrete;
            segment.material  = DesignerController.instance.defaultTelescopeMaterial;

            if (reverse)
            {
                CurveSegment lastSeg = segments[segments.Count - 1];
                obj.transform.position = TransformedHelixPoint(lastSeg, lastSeg.arcLength);

                OrthonormalFrame initFrame = TransformedHelixFrame(lastSeg, lastSeg.arcLength);
                segment.initialDirection = -initFrame.T;
                segment.initialUp        = initFrame.N;
            }
            else
            {
                obj.transform.position   = segments[0].startPosition;
                segment.initialDirection = segments[0].frame.T;
                segment.initialUp        = segments[0].frame.N;
            }

            segment.MakeShellsFromConcrete(tParams);

            if (reverse)
            {
                segment.ReverseTelescope();
            }

            return(segment);
        }
Пример #23
0
 public OrthonormalFrame PropagateBishop(DCurvePoint prev, DCurvePoint next, OrthonormalFrame prevFrame)
 {
     return(PropagateBishop(prev.position, next.position, prevFrame));
 }
Пример #24
0
        /// <summary>
        /// Resolve points where the Frenet frame is undefined due to
        /// the tangent being constant (i.e. zero derivative).
        /// </summary>
        void FixFrenetFrames()
        {
            // Find the index of the first valid frame.
            int firstFrameIndex = -1;

            for (int i = 0; i < discretizedPoints.Count; i++)
            {
                OrthonormalFrame frame = discretizedPoints[i].frenetFrame;
                if (frame.B.magnitude > 0)
                {
                    firstFrameIndex = i;
                    break;
                }
            }

            // If there were no valid Frenet frames, then the entire curve is just
            // a striaght line. We just assign an arbitrary
            // frame aligned with the tangent for each one.
            if (firstFrameIndex == -1)
            {
                Vector3 tangent = discretizedPoints[0].frenetFrame.T;
                Vector3 normal, binormal;
                // If the tangent is pointing up, use an orthogonal direction
                if (tangent == Vector3.up)
                {
                    binormal = Vector3.right;
                    normal   = Vector3.Cross(binormal, tangent);
                }
                // Otherwise take some vector orthogonal to the tangent,
                // by projecting the up direction onto it
                else
                {
                    binormal = Vector3.up;
                    binormal = binormal - Vector3.Dot(binormal, tangent) * tangent;
                    binormal.Normalize();
                    normal = Vector3.Cross(binormal, tangent);
                }

                startingBinormal = binormal;
                OrthonormalFrame defaultFrame = new OrthonormalFrame(tangent, normal, binormal);

                foreach (DCurvePoint dcp in discretizedPoints)
                {
                    dcp.frenetFrame = defaultFrame;
                }
            }

            // Otherwise there is at least one valid frame, so propagate to all missing frames.
            else
            {
                for (int i = 0; i < discretizedPoints.Count; i++)
                {
                    // If the current frame is invalid (has zero binormal), fix it
                    if (discretizedPoints[i].frenetFrame.B.magnitude == 0)
                    {
                        if (i < firstFrameIndex)
                        {
                            FixFrenetForward(i);
                        }
                        else if (i > firstFrameIndex)
                        {
                            FixFrenetBackward(i);
                        }
                    }
                }
            }
        }