External/OxyPlot/OxyPlot/Foundation/CanonicalSplineHelper.cs
author Stephane Lenclud
Sat, 30 Jan 2016 23:01:51 +0100
branchMiniDisplay
changeset 454 f84878f52cd9
permissions -rw-r--r--
Disabling Nuvoton NCT6791D because of fan full speed bug on Asus Z97 WS.
     1 // --------------------------------------------------------------------------------------------------------------------
     2 // <copyright file="CanonicalSplineHelper.cs" company="OxyPlot">
     3 //   The MIT License (MIT)
     4 //
     5 //   Copyright (c) 2012 Oystein Bjorke
     6 //
     7 //   Permission is hereby granted, free of charge, to any person obtaining a
     8 //   copy of this software and associated documentation files (the
     9 //   "Software"), to deal in the Software without restriction, including
    10 //   without limitation the rights to use, copy, modify, merge, publish,
    11 //   distribute, sublicense, and/or sell copies of the Software, and to
    12 //   permit persons to whom the Software is furnished to do so, subject to
    13 //   the following conditions:
    14 //
    15 //   The above copyright notice and this permission notice shall be included
    16 //   in all copies or substantial portions of the Software.
    17 //
    18 //   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
    19 //   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    20 //   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    21 //   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    22 //   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    23 //   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    24 //   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    25 // </copyright>
    26 // <summary>
    27 //   Interpolates a list of points using a canonical spline.
    28 // </summary>
    29 // --------------------------------------------------------------------------------------------------------------------
    30 namespace OxyPlot
    31 {
    32     using System;
    33     using System.Collections.Generic;
    34     using System.Linq;
    35 
    36     /// <summary>
    37     /// Provides functionality to interpolate a list of points by a canonical spline.
    38     /// </summary>
    39     internal static class CanonicalSplineHelper
    40     {
    41         // CanonicalSplineHelper.cs (c) 2009 by Charles Petzold (WPF and Silverlight)
    42         // www.charlespetzold.com/blog/2009/01/Canonical-Splines-in-WPF-and-Silverlight.html
    43         /// <summary>
    44         /// Creates a spline of data points.
    45         /// </summary>
    46         /// <param name="points">
    47         /// The points.
    48         /// </param>
    49         /// <param name="tension">
    50         /// The tension.
    51         /// </param>
    52         /// <param name="tensions">
    53         /// The tensions.
    54         /// </param>
    55         /// <param name="isClosed">
    56         /// True if the spline is closed.
    57         /// </param>
    58         /// <param name="tolerance">
    59         /// The tolerance.
    60         /// </param>
    61         /// <returns>
    62         /// A list of data points.
    63         /// </returns>
    64         internal static List<IDataPoint> CreateSpline(
    65             IList<IDataPoint> points, double tension, IList<double> tensions, bool isClosed, double tolerance)
    66         {
    67             var screenPoints = points.Select(p => new ScreenPoint(p.X, p.Y)).ToList();
    68             var interpolatedScreenPoints = CreateSpline(screenPoints, tension, tensions, isClosed, tolerance);
    69             var interpolatedDataPoints = new List<IDataPoint>(interpolatedScreenPoints.Count);
    70 
    71             foreach (var s in interpolatedScreenPoints)
    72             {
    73                 interpolatedDataPoints.Add(new DataPoint(s.X, s.Y));
    74             }
    75 
    76             return interpolatedDataPoints;
    77         }
    78 
    79         /// <summary>
    80         /// Creates a spline of screen points.
    81         /// </summary>
    82         /// <param name="points">
    83         /// The points.
    84         /// </param>
    85         /// <param name="tension">
    86         /// The tension.
    87         /// </param>
    88         /// <param name="tensions">
    89         /// The tensions.
    90         /// </param>
    91         /// <param name="isClosed">
    92         /// True if the spline is closed.
    93         /// </param>
    94         /// <param name="tolerance">
    95         /// The tolerance.
    96         /// </param>
    97         /// <returns>
    98         /// A list of screen points.
    99         /// </returns>
   100         internal static List<ScreenPoint> CreateSpline(
   101             IList<ScreenPoint> points, double tension, IList<double> tensions, bool isClosed, double tolerance)
   102         {
   103             var result = new List<ScreenPoint>();
   104             if (points == null)
   105             {
   106                 return result;
   107             }
   108 
   109             int n = points.Count;
   110             if (n < 1)
   111             {
   112                 return result;
   113             }
   114 
   115             if (n < 2)
   116             {
   117                 result.AddRange(points);
   118                 return result;
   119             }
   120 
   121             if (n == 2)
   122             {
   123                 if (!isClosed)
   124                 {
   125                     Segment(result, points[0], points[0], points[1], points[1], tension, tension, tolerance);
   126                 }
   127                 else
   128                 {
   129                     Segment(result, points[1], points[0], points[1], points[0], tension, tension, tolerance);
   130                     Segment(result, points[0], points[1], points[0], points[1], tension, tension, tolerance);
   131                 }
   132             }
   133             else
   134             {
   135                 bool useTensionCollection = tensions != null && tensions.Count > 0;
   136 
   137                 for (int i = 0; i < n; i++)
   138                 {
   139                     double t1 = useTensionCollection ? tensions[i % tensions.Count] : tension;
   140                     double t2 = useTensionCollection ? tensions[(i + 1) % tensions.Count] : tension;
   141 
   142                     if (i == 0)
   143                     {
   144                         Segment(
   145                             result,
   146                             isClosed ? points[n - 1] : points[0],
   147                             points[0],
   148                             points[1],
   149                             points[2],
   150                             t1,
   151                             t2,
   152                             tolerance);
   153                     }
   154                     else if (i == n - 2)
   155                     {
   156                         Segment(
   157                             result,
   158                             points[i - 1],
   159                             points[i],
   160                             points[i + 1],
   161                             isClosed ? points[0] : points[i + 1],
   162                             t1,
   163                             t2,
   164                             tolerance);
   165                     }
   166                     else if (i == n - 1)
   167                     {
   168                         if (isClosed)
   169                         {
   170                             Segment(result, points[i - 1], points[i], points[0], points[1], t1, t2, tolerance);
   171                         }
   172                     }
   173                     else
   174                     {
   175                         Segment(result, points[i - 1], points[i], points[i + 1], points[i + 2], t1, t2, tolerance);
   176                     }
   177                 }
   178             }
   179 
   180             return result;
   181         }
   182 
   183         /// <summary>
   184         /// The segment.
   185         /// </summary>
   186         /// <param name="points">
   187         /// The points.
   188         /// </param>
   189         /// <param name="pt0">
   190         /// The pt 0.
   191         /// </param>
   192         /// <param name="pt1">
   193         /// The pt 1.
   194         /// </param>
   195         /// <param name="pt2">
   196         /// The pt 2.
   197         /// </param>
   198         /// <param name="pt3">
   199         /// The pt 3.
   200         /// </param>
   201         /// <param name="t1">
   202         /// The t 1.
   203         /// </param>
   204         /// <param name="t2">
   205         /// The t 2.
   206         /// </param>
   207         /// <param name="tolerance">
   208         /// The tolerance.
   209         /// </param>
   210         private static void Segment(
   211             IList<ScreenPoint> points,
   212             ScreenPoint pt0,
   213             ScreenPoint pt1,
   214             ScreenPoint pt2,
   215             ScreenPoint pt3,
   216             double t1,
   217             double t2,
   218             double tolerance)
   219         {
   220             // See Petzold, "Programming Microsoft Windows with C#", pages 645-646 or
   221             // Petzold, "Programming Microsoft Windows with Microsoft Visual Basic .NET", pages 638-639
   222             // for derivation of the following formulas:
   223             double sx1 = t1 * (pt2.X - pt0.X);
   224             double sy1 = t1 * (pt2.Y - pt0.Y);
   225             double sx2 = t2 * (pt3.X - pt1.X);
   226             double sy2 = t2 * (pt3.Y - pt1.Y);
   227 
   228             double ax = sx1 + sx2 + 2 * pt1.X - 2 * pt2.X;
   229             double ay = sy1 + sy2 + 2 * pt1.Y - 2 * pt2.Y;
   230             double bx = -2 * sx1 - sx2 - 3 * pt1.X + 3 * pt2.X;
   231             double by = -2 * sy1 - sy2 - 3 * pt1.Y + 3 * pt2.Y;
   232 
   233             double cx = sx1;
   234             double cy = sy1;
   235             double dx = pt1.X;
   236             double dy = pt1.Y;
   237 
   238             var num = (int)((Math.Abs(pt1.X - pt2.X) + Math.Abs(pt1.Y - pt2.Y)) / tolerance);
   239 
   240             // Notice begins at 1 so excludes the first point (which is just pt1)
   241             for (int i = 1; i < num; i++)
   242             {
   243                 double t = (double)i / (num - 1);
   244                 var pt = new ScreenPoint(
   245                     ax * t * t * t + bx * t * t + cx * t + dx,
   246                     ay * t * t * t + by * t * t + cy * t + dy);
   247                 points.Add(pt);
   248             }
   249         }
   250 
   251     }
   252 }