[12503] | 1 | using System;
|
---|
| 2 | using System.Collections.Generic;
|
---|
| 3 | using System.Linq;
|
---|
| 4 | using System.Text;
|
---|
| 5 | using System.Windows;
|
---|
| 6 |
|
---|
| 7 | namespace 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 | }
|
---|