Пример #1
0
        private void CreatePath()
        {
            // Create a cyclic path. (More information on paths can be found in the DigitalRune
            // Mathematics documentation and related samples.)
            _path = new Path3F
            {
                SmoothEnds = true,
                PreLoop    = CurveLoopType.Cycle,
                PostLoop   = CurveLoopType.Cycle
            };

            // The curvature of the path is defined by a number of path keys.
            _path.Add(new PathKey3F
            {
                Parameter     = 0,                              // The path parameter defines position of the path key on the curve.
                Point         = new Vector3(-4, 0.5f, -3),      // The world space position of the path key.
                Interpolation = SplineInterpolation.CatmullRom, // The type of interpolation that is used between this path key and the next.
            });
            _path.Add(new PathKey3F
            {
                Parameter     = 1,
                Point         = new Vector3(-1, 0.5f, -5),
                Interpolation = SplineInterpolation.CatmullRom,
            });
            _path.Add(new PathKey3F
            {
                Parameter     = 2,
                Point         = new Vector3(3, 0.5f, -4),
                Interpolation = SplineInterpolation.CatmullRom,
            });
            _path.Add(new PathKey3F
            {
                Parameter     = 3,
                Point         = new Vector3(0, 0.5f, 0),
                Interpolation = SplineInterpolation.CatmullRom,
            });
            _path.Add(new PathKey3F
            {
                Parameter     = 4,
                Point         = new Vector3(-3, 0.5f, 3),
                Interpolation = SplineInterpolation.CatmullRom,
            });
            _path.Add(new PathKey3F
            {
                Parameter     = 5,
                Point         = new Vector3(-1, 0.5f, 5),
                Interpolation = SplineInterpolation.CatmullRom,
            });
            _path.Add(new PathKey3F
            {
                Parameter     = 6,
                Point         = new Vector3(0, 0.5f, 0),
                Interpolation = SplineInterpolation.CatmullRom,
            });

            // The last key uses the same position as the first key to create a closed path.
            PathKey3F lastKey = new PathKey3F
            {
                Parameter     = _path.Count,
                Point         = _path[0].Point,
                Interpolation = SplineInterpolation.CatmullRom,
            };

            _path.Add(lastKey);

            // The current path parameter goes from 0 to 7. This path parameter is not linearly
            // proportional to the path length. This is not suitable for animations.
            // To move an object with constant speed along a path, the path parameter should
            // be linearly proportional to the length of the path.
            // ParameterizeByLength() changes the path parameter so that the path parameter
            // at the each key is equal to the length of path (measured from the first key position
            // to the current key position).
            // ParameterizeByLength() uses an iterative process, we end the process after 10
            // iterations or when the error is less than 0.01f.
            _path.ParameterizeByLength(10, 0.01f);

            // Sample the path for rendering.
            int   numberOfSamples = _pointList.Length - 1;
            float pathLength      = _path.Last().Parameter;

            for (int i = 0; i <= numberOfSamples; i++)
            {
                Vector3 pointOnPath = _path.GetPoint(pathLength / numberOfSamples * i);
                _pointList[i] = pointOnPath;
            }
        }
Пример #2
0
        public void ParameterizeByLength()
        {
            Path3F empty = new Path3F();
              empty.Sort();
              empty.ParameterizeByLength(20, 0.001f); // No exception, do nothing.

              Path3F path = CreatePath();
              Path3F lengthPath = CreatePath();
              lengthPath.ParameterizeByLength(20, 0.001f);

              Assert.AreEqual(0, lengthPath[0].Parameter);
              Assert.AreEqual(3, lengthPath[1].Parameter);
              Assert.AreEqual(3, lengthPath[2].Parameter);
              Assert.AreEqual(3, lengthPath[3].Parameter);

              float step = 0.001f;
              float length = 3;
              int i = 4;
              float u = 20;
              Vector3F oldPoint = path.GetPoint(u);
              for (; u < 51 && i<10; u += step)
              {
            if (Numeric.AreEqual(u, path[i].Parameter))
            {
              Assert.IsTrue(Numeric.AreEqual(length, lengthPath[i].Parameter, 0.01f));

              // Set explicit values against numerical problems.
              length = lengthPath[i].Parameter;
              u = path[i].Parameter;
              oldPoint = path.GetPoint(u);

              i++;
            }
            Vector3F newPoint = path.GetPoint(u + step);
            length += (newPoint - oldPoint).Length;
            oldPoint = newPoint;
              }
              Assert.AreEqual(10, i); // Have we checked all keys?

            path.PreLoop = CurveLoopType.Constant;
              path.PostLoop = CurveLoopType.Oscillate;

              path.PreLoop = CurveLoopType.Linear;
              path.PostLoop = CurveLoopType.Cycle;

              path.PreLoop = CurveLoopType.Cycle;
              path.PostLoop = CurveLoopType.CycleOffset;

              path.PreLoop = CurveLoopType.CycleOffset;
              path.PostLoop = CurveLoopType.Linear;

              path.PreLoop = CurveLoopType.Oscillate;
              path.PostLoop = CurveLoopType.Constant;
        }
Пример #3
0
        public void GetParameterByLength()
        {
            Path3F empty = new Path3F();
              empty.ParameterizeByLength(20, 0.001f); // No exception, do nothing.
              Assert.IsTrue(float.IsNaN(empty.GetParameterFromLength(10, 20, 0.1f)));

              Path3F path = CreatePath();
              path.ParameterizeByLength(20, 0.001f);

              Assert.AreEqual(0, path.GetParameterFromLength(0, 20, 0.001f));
              Assert.AreEqual(3, path.GetParameterFromLength(3, 20, 0.001f));
              Assert.AreEqual(path[4].Parameter, path.GetParameterFromLength(path[4].Parameter, 20, 0.01f));
              Assert.AreEqual(path[5].Parameter, path.GetParameterFromLength(path[5].Parameter, 20, 0.01f));
              Assert.AreEqual(path[6].Parameter, path.GetParameterFromLength(path[6].Parameter, 20, 0.01f));
              Assert.AreEqual(path[7].Parameter, path.GetParameterFromLength(path[7].Parameter, 20, 0.01f));
              Assert.AreEqual(path[8].Parameter, path.GetParameterFromLength(path[8].Parameter, 20, 0.01f));
              Assert.AreEqual(path[9].Parameter, path.GetParameterFromLength(path[9].Parameter, 20, 0.01f));

              float pathLength = path[9].Parameter;

              float desiredLength = 11;
              float actualLength = path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.01f), 20, 0.01f);
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.01f));

              desiredLength = 26;
              actualLength = path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.01f), 20, 0.01f);
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.01f));

              desiredLength = 33.5f;
              actualLength = path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.001f), 20, 0.001f);
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.01f));

              path.PreLoop = CurveLoopType.Linear;
              path.PostLoop = CurveLoopType.Linear;
              desiredLength = 60;
              actualLength = path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.001f), 20, 0.001f);
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.1f));

              desiredLength = -10f;
              actualLength = -path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.001f), 20, 0.001f);
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.01f));

              path.PreLoop = CurveLoopType.CycleOffset;
              path.PostLoop = CurveLoopType.CycleOffset;
              path.ParameterizeByLength(20, 0.001f);
              desiredLength = -90;
              actualLength = -path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.01f), 20, 0.01f);
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.1f));
              desiredLength = -50;
              actualLength = -path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.01f), 20, 0.01f);
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.1f));
              desiredLength = -30;
              actualLength = -path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.01f), 20, 0.01f);
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.1f));
              desiredLength = 50;
              actualLength = path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.01f), 20, 0.01f);
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.1f));
              desiredLength = 100;
              actualLength = path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.01f), 20, 0.01f);
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.1f));
              desiredLength = 130;
              actualLength = path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.01f), 20, 0.01f);
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.1f));
              desiredLength = 200;
              actualLength = path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.01f), 20, 0.01f);
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.1f));

              path.PreLoop = CurveLoopType.Oscillate;
              path.PostLoop = CurveLoopType.Cycle;
              path.ParameterizeByLength(20, 0.001f);
              desiredLength = -110;
              actualLength = -path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.01f), 20, 0.01f);
              Assert.IsTrue(-3 * pathLength < path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(-2 * pathLength > path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.1f));
              desiredLength = -50;
              actualLength = -path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.01f), 20, 0.01f);
              Assert.IsTrue(-2 * pathLength < path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(-1 * pathLength > path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.1f));
              desiredLength = -30;
              actualLength = -path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.01f), 20, 0.01f);
              Assert.IsTrue(-1 * pathLength < path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(0 > path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.1f));
              desiredLength = 50;
              actualLength = path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.01f), 20, 0.01f);
              Assert.IsTrue(1 * pathLength < path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(2 * pathLength > path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.1f));
              desiredLength = 110;
              actualLength = path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.01f), 20, 0.01f);
              Assert.IsTrue(2 * pathLength < path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(3 * pathLength > path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.1f));
              desiredLength = 130;
              actualLength = path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.01f), 20, 0.01f);
              Assert.IsTrue(3 * pathLength < path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(4 * pathLength > path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.1f));
              desiredLength = 190;
              actualLength = path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.01f), 20, 0.01f);
              Assert.IsTrue(4 * pathLength < path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(5 * pathLength > path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.2f));

              path.PreLoop = CurveLoopType.Cycle;
              path.PostLoop = CurveLoopType.Oscillate;
              path.ParameterizeByLength(20, 0.001f);
              desiredLength = -90;
              actualLength = -path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.01f), 20, 0.01f);
              Assert.IsTrue(-3 * pathLength < path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(-2 * pathLength > path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.1f));
              desiredLength = -50;
              actualLength = -path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.01f), 20, 0.01f);
              Assert.IsTrue(-2 * pathLength < path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(-1 * pathLength > path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.1f));
              desiredLength = -30;
              actualLength = -path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.01f), 20, 0.01f);
              Assert.IsTrue(-1 * pathLength < path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(0 > path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.1f));
              desiredLength = 50;
              actualLength = path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.01f), 20, 0.01f);
              Assert.IsTrue(pathLength < path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(2 * pathLength > path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.1f));
              desiredLength = 110;
              actualLength = path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.01f), 20, 0.01f);
              Assert.IsTrue(2 * pathLength < path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(3 * pathLength > path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.1f));
              desiredLength = 130;
              actualLength = path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.01f), 20, 0.01f);
              Assert.IsTrue(3 * pathLength < path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(4 * pathLength > path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.1f));
              desiredLength = 210;
              actualLength = path.GetLength(0, path.GetParameterFromLength(desiredLength, 20, 0.01f), 20, 0.01f);
              Assert.IsTrue(5 * pathLength < path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(6 * pathLength > path.GetParameterFromLength(desiredLength, 20, 0.01f));
              Assert.IsTrue(Numeric.AreEqual(desiredLength, actualLength, 0.1f));

              // Test path with zero length.
              path = new Path3F();
              path.Add(new PathKey3F()
              {
            Parameter = 10,
            Point = new Vector3F(0, 0, 1),
            Interpolation = SplineInterpolation.Linear,
            TangentIn = new Vector3F(1, 0, 0),
            TangentOut = new Vector3F(1, 0, 0),
              });
              path.ParameterizeByLength(20, 0.001f);
              Assert.AreEqual(0, path.GetParameterFromLength(0, 20, 0.1f));
              path.Add(new PathKey3F()
              {
            Parameter = 20,
            Point = new Vector3F(0, 0, 1),
            Interpolation = SplineInterpolation.Linear,
            TangentIn = new Vector3F(1, 0, 0),
            TangentOut = new Vector3F(1, 0, 0),
              });
              path.ParameterizeByLength(20, 0.001f);
              Assert.AreEqual(0, path.GetParameterFromLength(0, 20, 0.1f));
        }
Пример #4
0
        // Creates a random 3D path.
        private void CreatePath()
        {
            // Create a cyclic path.
            _path = new Path3F
            {
                PreLoop    = CurveLoopType.Cycle,
                PostLoop   = CurveLoopType.Cycle,
                SmoothEnds = true
            };

            // Add random path key points.
            for (int i = 0; i < 5; i++)
            {
                float x   = RandomHelper.Random.NextFloat(-3, 3);
                float y   = RandomHelper.Random.NextFloat(1, 3);
                float z   = RandomHelper.Random.NextFloat(-3, 0);
                var   key = new PathKey3F
                {
                    Parameter     = i,
                    Point         = new Vector3F(x, y, z),
                    Interpolation = SplineInterpolation.CatmullRom
                };
                _path.Add(key);
            }

            // The last key uses the same position as the first key to create a closed path.
            var lastKey = new PathKey3F
            {
                Parameter     = _path.Count,
                Point         = _path[0].Point,
                Interpolation = SplineInterpolation.CatmullRom,
            };

            _path.Add(lastKey);

            // The current path parameter goes from 0 to 5. This path parameter is not linearly
            // proportional to the path length. This is not suitable for animations.
            // To move an object with constant speed along the path, the path parameter should
            // be linearly proportional to the length of the path.
            // ParameterizeByLength() changes the path parameter so that the path parameter
            // at the each key is equal to the length of path (measured from the first key position
            // to the current key position).
            // ParameterizeByLength() uses and iterative process, we end the process after 10
            // iterations or when the error is less than 0.001f.
            _path.ParameterizeByLength(10, 0.001f);

            // Now, the parameter of the first key (_path[0]) is unchanged.
            // The parameter of the second key (_path[1]) is equal to the length of the path
            // from the first key to the second key.
            // The parameter of the third key (_path[2]) is equal to the length of the path
            // from the first key to the third key.
            // And so on.

            // The parameter of the last key is equal to the length of the whole path:
            //   float pathLength = _path[_path.Count - 1].Parameter;

            // Important: The path parameter is now equal to the path length at the path keys.
            // But in general between path keys the path parameter is not linearly proportional
            // to the path length. This is due to the nature of splines.
            //
            // Example:
            // Lets assume the second path key is at path length 100 and the third key is
            // at path length 200.
            // If we call _path.GetPoint(100), we get the position of the second key.
            // If we call _path.GetPoint(200), we get the position ot the third key.
            // We can call _path.GetPoint(130) to get a position on the path between the second and
            // third key. But it is not guaranteed that the path is exactly 130 long at this position.
            // We only know that the point is somewhere between 100 and 200 path length.
            //
            // To get the path point at exactly the distance 130 from the path start, we have to call
            //   float parameter = _path.GetParameterFromLength(130, 10, 0.01f);
            // This uses an iterative root finding process to find the path parameter where the
            // path length is 130.
            // Then we can get the path position with
            //   Vector3F pathPointAt130Length = _path.GetPoint(parameter).
        }
Пример #5
0
    // Creates a random 3D path.
    private void CreatePath()
    {
      // Create a cyclic path.
      _path = new Path3F
      {
        PreLoop = CurveLoopType.Cycle,
        PostLoop = CurveLoopType.Cycle,
        SmoothEnds = true
      };

      // Add random path key points.
      for (int i = 0; i < 5; i++)
      {
        float x = RandomHelper.Random.NextFloat(-3, 3);
        float y = RandomHelper.Random.NextFloat(1, 3);
        float z = RandomHelper.Random.NextFloat(-3, 0);
        var key = new PathKey3F
        {
          Parameter = i,
          Point = new Vector3F(x, y, z),
          Interpolation = SplineInterpolation.CatmullRom
        };
        _path.Add(key);
      }

      // The last key uses the same position as the first key to create a closed path.
      var lastKey = new PathKey3F
      {
        Parameter = _path.Count,
        Point = _path[0].Point,
        Interpolation = SplineInterpolation.CatmullRom,
      };
      _path.Add(lastKey);

      // The current path parameter goes from 0 to 5. This path parameter is not linearly
      // proportional to the path length. This is not suitable for animations.
      // To move an object with constant speed along the path, the path parameter should
      // be linearly proportional to the length of the path.
      // ParameterizeByLength() changes the path parameter so that the path parameter
      // at the each key is equal to the length of path (measured from the first key position
      // to the current key position).
      // ParameterizeByLength() uses and iterative process, we end the process after 10 
      // iterations or when the error is less than 0.001f.
      _path.ParameterizeByLength(10, 0.001f);

      // Now, the parameter of the first key (_path[0]) is unchanged.
      // The parameter of the second key (_path[1]) is equal to the length of the path
      // from the first key to the second key.
      // The parameter of the third key (_path[2]) is equal to the length of the path
      // from the first key to the third key.
      // And so on. 

      // The parameter of the last key is equal to the length of the whole path:
      //   float pathLength = _path[_path.Count - 1].Parameter;

      // Important: The path parameter is now equal to the path length at the path keys.
      // But in general between path keys the path parameter is not linearly proportional
      // to the path length. This is due to the nature of splines.
      //
      // Example: 
      // Lets assume the second path key is at path length 100 and the third key is 
      // at path length 200.
      // If we call _path.GetPoint(100), we get the position of the second key.
      // If we call _path.GetPoint(200), we get the position ot the third key.
      // We can call _path.GetPoint(130) to get a position on the path between the second and
      // third key. But it is not guaranteed that the path is exactly 130 long at this position.
      // We only know that the point is somewhere between 100 and 200 path length.
      //
      // To get the path point at exactly the distance 130 from the path start, we have to call
      //   float parameter = _path.GetParameterFromLength(130, 10, 0.01f);
      // This uses an iterative root finding process to find the path parameter where the
      // path length is 130.
      // Then we can get the path position with 
      //   Vector3F pathPointAt130Length = _path.GetPoint(parameter).
    }
Пример #6
0
    private void CreatePath()
    {
      // Create a cyclic path. (More information on paths can be found in the DigitalRune
      // Mathematics documentation and related samples.)
      _path = new Path3F
      {
        SmoothEnds = true,
        PreLoop = CurveLoopType.Cycle,
        PostLoop = CurveLoopType.Cycle
      };

      // The curvature of the path is defined by a number of path keys.
      _path.Add(new PathKey3F
      {
        Parameter = 0,                                  // The path parameter defines position of the path key on the curve.
        Point = new Vector3F(-4, 0.5f, -3),             // The world space position of the path key.
        Interpolation = SplineInterpolation.CatmullRom, // The type of interpolation that is used between this path key and the next.
      });
      _path.Add(new PathKey3F
      {
        Parameter = 1,
        Point = new Vector3F(-1, 0.5f, -5),
        Interpolation = SplineInterpolation.CatmullRom,
      });
      _path.Add(new PathKey3F
      {
        Parameter = 2,
        Point = new Vector3F(3, 0.5f, -4),
        Interpolation = SplineInterpolation.CatmullRom,
      });
      _path.Add(new PathKey3F
      {
        Parameter = 3,
        Point = new Vector3F(0, 0.5f, 0),
        Interpolation = SplineInterpolation.CatmullRom,
      });
      _path.Add(new PathKey3F
      {
        Parameter = 4,
        Point = new Vector3F(-3, 0.5f, 3),
        Interpolation = SplineInterpolation.CatmullRom,
      });
      _path.Add(new PathKey3F
      {
        Parameter = 5,
        Point = new Vector3F(-1, 0.5f, 5),
        Interpolation = SplineInterpolation.CatmullRom,
      });
      _path.Add(new PathKey3F
      {
        Parameter = 6,
        Point = new Vector3F(0, 0.5f, 0),
        Interpolation = SplineInterpolation.CatmullRom,
      });

      // The last key uses the same position as the first key to create a closed path.
      PathKey3F lastKey = new PathKey3F
      {
        Parameter = _path.Count,
        Point = _path[0].Point,
        Interpolation = SplineInterpolation.CatmullRom,
      };
      _path.Add(lastKey);

      // The current path parameter goes from 0 to 7. This path parameter is not linearly
      // proportional to the path length. This is not suitable for animations.
      // To move an object with constant speed along a path, the path parameter should
      // be linearly proportional to the length of the path.
      // ParameterizeByLength() changes the path parameter so that the path parameter
      // at the each key is equal to the length of path (measured from the first key position
      // to the current key position).
      // ParameterizeByLength() uses an iterative process, we end the process after 10 
      // iterations or when the error is less than 0.01f.
      _path.ParameterizeByLength(10, 0.01f);

      // Sample the path for rendering.
      int numberOfSamples = _pointList.Length - 1;
      float pathLength = _path.Last().Parameter;
      for (int i = 0; i <= numberOfSamples; i++)
      {
        Vector3F pointOnPath = _path.GetPoint(pathLength / numberOfSamples * i);
        _pointList[i] = pointOnPath;
      }
    }