/Main/src/DynamicDataDisplay/Common/BezierBuilder.cs
C# | 120 lines | 87 code | 15 blank | 18 comment | 12 complexity | c7bf63e199cd9415f49b0a541ab44208 MD5 | raw file
Possible License(s): CC-BY-SA-3.0
1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Text; 5using System.Windows; 6 7namespace Microsoft.Research.DynamicDataDisplay.Charts.NewLine 8{ 9 // todo check a license 10 public static class BezierBuilder 11 { 12 public static IEnumerable<Point> GetBezierPoints(Point[] points) 13 { 14 if (points == null) 15 throw new ArgumentNullException("points"); 16 17 Point[] firstControlPoints; 18 Point[] secondControlPoints; 19 20 int n = points.Length - 1; 21 if (n < 1) 22 throw new ArgumentException("At least two knot points required", "points"); 23 if (n == 1) 24 { // Special case: Bezier curve should be a straight line. 25 firstControlPoints = new Point[1]; 26 // 3P1 = 2P0 + P3 27 firstControlPoints[0].X = (2 * points[0].X + points[1].X) / 3; 28 firstControlPoints[0].Y = (2 * points[0].Y + points[1].Y) / 3; 29 30 secondControlPoints = new Point[1]; 31 // P2 = 2P1 – P0 32 secondControlPoints[0].X = 2 * 33 firstControlPoints[0].X - points[0].X; 34 secondControlPoints[0].Y = 2 * 35 firstControlPoints[0].Y - points[0].Y; 36 37 return Join(points, firstControlPoints, secondControlPoints); 38 } 39 40 // Calculate first Bezier control points 41 // Right hand side vector 42 double[] rhs = new double[n]; 43 44 // Set right hand side X values 45 for (int i = 1; i < n - 1; ++i) 46 rhs[i] = 4 * points[i].X + 2 * points[i + 1].X; 47 rhs[0] = points[0].X + 2 * points[1].X; 48 rhs[n - 1] = (8 * points[n - 1].X + points[n].X) / 2.0; 49 // Get first control points X-values 50 double[] x = GetFirstControlPoints(rhs); 51 52 // Set right hand side Y values 53 for (int i = 1; i < n - 1; ++i) 54 rhs[i] = 4 * points[i].Y + 2 * points[i + 1].Y; 55 rhs[0] = points[0].Y + 2 * points[1].Y; 56 rhs[n - 1] = (8 * points[n - 1].Y + points[n].Y) / 2.0; 57 // Get first control points Y-values 58 double[] y = GetFirstControlPoints(rhs); 59 60 // Fill output arrays. 61 firstControlPoints = new Point[n]; 62 secondControlPoints = new Point[n]; 63 for (int i = 0; i < n; ++i) 64 { 65 // First control point 66 firstControlPoints[i] = new Point(x[i], y[i]); 67 // Second control point 68 if (i < n - 1) 69 secondControlPoints[i] = new Point(2 * points 70 [i + 1].X - x[i + 1], 2 * 71 points[i + 1].Y - y[i + 1]); 72 else 73 secondControlPoints[i] = new Point((points 74 [n].X + x[n - 1]) / 2, 75 (points[n].Y + y[n - 1]) / 2); 76 } 77 78 return Join(points, firstControlPoints, secondControlPoints); 79 } 80 81 private static IEnumerable<Point> Join(Point[] points, Point[] firstControlPoints, Point[] secondControlPoints) 82 { 83 var length = firstControlPoints.Length; 84 for (int i = 0; i < length; i++) 85 { 86 yield return points[i]; 87 yield return firstControlPoints[i]; 88 yield return secondControlPoints[i]; 89 } 90 91 yield return points[length]; 92 } 93 94 /// <summary> 95 /// Solves a tridiagonal system for one of coordinates (x or y) 96 /// of first Bezier control points. 97 /// </summary> 98 /// <param name="rhs">Right hand side vector.</param> 99 /// <returns>Solution vector.</returns> 100 private static double[] GetFirstControlPoints(double[] rhs) 101 { 102 int n = rhs.Length; 103 double[] x = new double[n]; // Solution vector. 104 double[] tmp = new double[n]; // Temp workspace. 105 106 double b = 2.0; 107 x[0] = rhs[0] / b; 108 for (int i = 1; i < n; i++) // Decomposition and forward substitution. 109 { 110 tmp[i] = 1 / b; 111 b = (i < n - 1 ? 4.0 : 3.5) - tmp[i]; 112 x[i] = (rhs[i] - x[i - 1]) / b; 113 } 114 for (int i = 1; i < n; i++) 115 x[n - i - 1] -= tmp[n - i] * x[n - i]; // Backsubstitution. 116 117 return x; 118 } 119 } 120}