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; } }
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; }
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)); }
// 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). }
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; } }