External/OxyPlot/OxyPlot/Foundation/Conrec.cs
author StephaneLenclud
Tue, 03 Feb 2015 10:14:18 +0100
branchMiniDisplay
changeset 450 f2d8620e2434
permissions -rw-r--r--
Rebracer update.
     1 // --------------------------------------------------------------------------------------------------------------------
     2 // <copyright file="Conrec.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 //   Creates contours from a triangular mesh.
    28 // </summary>
    29 // --------------------------------------------------------------------------------------------------------------------
    30 namespace OxyPlot
    31 {
    32     using System;
    33 
    34     /// <summary>
    35     /// Provides functionality to create contours from a triangular mesh.
    36     /// </summary>
    37     /// <remarks>
    38     /// <para>
    39     /// Ported from C / Fortran code by Paul Bourke.
    40     /// See <a href="http://paulbourke.net/papers/conrec/">Conrec</a> for
    41     /// full description of code and the original source.
    42     ///  </para>
    43     /// <para>
    44     /// Contouring aids in visualizing three dimensional surfaces on a two dimensional
    45     /// medium (on paper or in this case a computer graphics screen). Two most common
    46     /// applications are displaying topological features of an area on a map or the air
    47     /// pressure on a weather map. In all cases some parameter is plotted as a function
    48     /// of two variables, the longitude and latitude or x and y axis. One problem with
    49     /// computer contouring is the process is usually CPU intensive and the algorithms
    50     /// often use advanced mathematical techniques making them susceptible to error.
    51     ///  </para>
    52     /// </remarks>
    53     public static class Conrec
    54     {
    55         /// <summary>
    56         /// Renderer delegate
    57         /// </summary>
    58         /// <param name="x1">
    59         /// Start point x-coordinate
    60         /// </param>
    61         /// <param name="y1">
    62         /// Start point y-coordinate
    63         /// </param>
    64         /// <param name="x2">
    65         /// End point x-coordinate
    66         /// </param>
    67         /// <param name="y2">
    68         /// End point y-coordinate
    69         /// </param>
    70         /// <param name="z">
    71         /// Contour level
    72         /// </param>
    73         public delegate void RendererDelegate(double x1, double y1, double x2, double y2, double z);
    74 
    75         /// <summary>
    76         /// Contour is a contouring subroutine for rectangularily spaced data
    77         /// It emits calls to a line drawing subroutine supplied by the user
    78         /// which draws a contour map corresponding to data on a randomly
    79         /// spaced rectangular grid. The coordinates emitted are in the same
    80         /// units given in the x() and y() arrays.
    81         /// Any number of contour levels may be specified but they must be
    82         /// in order of increasing value.
    83         /// </summary>
    84         /// <param name="d">
    85         /// Matrix of data to contour.
    86         /// </param>
    87         /// <param name="x">
    88         /// Data matrix column coordinates.
    89         /// </param>
    90         /// <param name="y">
    91         /// Data matrix row coordinates.
    92         /// </param>
    93         /// <param name="z">
    94         /// Contour levels in increasing order.
    95         /// </param>
    96         /// <param name="renderer">
    97         /// The renderer.
    98         /// </param>
    99         public static void Contour(double[,] d, double[] x, double[] y, double[] z, RendererDelegate renderer)
   100         {
   101             double x1 = 0.0;
   102             double x2 = 0.0;
   103             double y1 = 0.0;
   104             double y2 = 0.0;
   105 
   106             var h = new double[5];
   107             var sh = new int[5];
   108             var xh = new double[5];
   109             var yh = new double[5];
   110 
   111             int ilb = d.GetLowerBound(0);
   112             int iub = d.GetUpperBound(0);
   113             int jlb = d.GetLowerBound(1);
   114             int jub = d.GetUpperBound(1);
   115             int nc = z.Length;
   116 
   117             // The indexing of im and jm should be noted as it has to start from zero
   118             // unlike the fortran counter part
   119             int[] im = { 0, 1, 1, 0 };
   120             int[] jm = { 0, 0, 1, 1 };
   121 
   122             // Note that castab is arranged differently from the FORTRAN code because
   123             // Fortran and C/C++ arrays are transposed of each other, in this case
   124             // it is more tricky as castab is in 3 dimension
   125             int[,,] castab =
   126                             {
   127                                  { { 0, 0, 8 }, { 0, 2, 5 }, { 7, 6, 9 } }, { { 0, 3, 4 }, { 1, 3, 1 }, { 4, 3, 0 } },
   128                                  { { 9, 6, 7 }, { 5, 2, 0 }, { 8, 0, 0 } }
   129                             };
   130 
   131             Func<int, int, double> xsect = (p1, p2) => ((h[p2] * xh[p1]) - (h[p1] * xh[p2])) / (h[p2] - h[p1]);
   132             Func<int, int, double> ysect = (p1, p2) => ((h[p2] * yh[p1]) - (h[p1] * yh[p2])) / (h[p2] - h[p1]);
   133 
   134             for (int j = jub - 1; j >= jlb; j--)
   135             {
   136                 int i;
   137                 for (i = ilb; i <= iub - 1; i++)
   138                 {
   139                     double temp1 = Math.Min(d[i, j], d[i, j + 1]);
   140                     double temp2 = Math.Min(d[i + 1, j], d[i + 1, j + 1]);
   141                     double dmin = Math.Min(temp1, temp2);
   142                     temp1 = Math.Max(d[i, j], d[i, j + 1]);
   143                     temp2 = Math.Max(d[i + 1, j], d[i + 1, j + 1]);
   144                     double dmax = Math.Max(temp1, temp2);
   145 
   146                     if (dmax >= z[0] && dmin <= z[nc - 1])
   147                     {
   148                         int k;
   149                         for (k = 0; k < nc; k++)
   150                         {
   151                             if (z[k] >= dmin && z[k] <= dmax)
   152                             {
   153                                 int m;
   154                                 for (m = 4; m >= 0; m--)
   155                                 {
   156                                     if (m > 0)
   157                                     {
   158                                         // The indexing of im and jm should be noted as it has to
   159                                         // start from zero
   160                                         h[m] = d[i + im[m - 1], j + jm[m - 1]] - z[k];
   161                                         xh[m] = x[i + im[m - 1]];
   162                                         yh[m] = y[j + jm[m - 1]];
   163                                     }
   164                                     else
   165                                     {
   166                                         h[0] = 0.25 * (h[1] + h[2] + h[3] + h[4]);
   167                                         xh[0] = 0.5 * (x[i] + x[i + 1]);
   168                                         yh[0] = 0.5 * (y[j] + y[j + 1]);
   169                                     }
   170 
   171                                     if (h[m] > 0.0)
   172                                     {
   173                                         sh[m] = 1;
   174                                     }
   175                                     else if (h[m] < 0.0)
   176                                     {
   177                                         sh[m] = -1;
   178                                     }
   179                                     else
   180                                     {
   181                                         sh[m] = 0;
   182                                     }
   183                                 }
   184 
   185                                 //// Note: at this stage the relative heights of the corners and the
   186                                 //// centre are in the h array, and the corresponding coordinates are
   187                                 //// in the xh and yh arrays. The centre of the box is indexed by 0
   188                                 //// and the 4 corners by 1 to 4 as shown below.
   189                                 //// Each triangle is then indexed by the parameter m, and the 3
   190                                 //// vertices of each triangle are indexed by parameters m1,m2,and
   191                                 //// m3.
   192                                 //// It is assumed that the centre of the box is always vertex 2
   193                                 //// though this isimportant only when all 3 vertices lie exactly on
   194                                 //// the same contour level, in which case only the side of the box
   195                                 //// is drawn.
   196                                 //// vertex 4 +-------------------+ vertex 3
   197                                 //// | \               / |
   198                                 //// |   \    m-3    /   |
   199                                 //// |     \       /     |
   200                                 //// |       \   /       |
   201                                 //// |  m=2    X   m=2   |       the centre is vertex 0
   202                                 //// |       /   \       |
   203                                 //// |     /       \     |
   204                                 //// |   /    m=1    \   |
   205                                 //// | /               \ |
   206                                 //// vertex 1 +-------------------+ vertex 2
   207 
   208                                 // Scan each triangle in the box
   209                                 for (m = 1; m <= 4; m++)
   210                                 {
   211                                     int m1 = m;
   212                                     int m2 = 0;
   213                                     int m3;
   214                                     if (m != 4)
   215                                     {
   216                                         m3 = m + 1;
   217                                     }
   218                                     else
   219                                     {
   220                                         m3 = 1;
   221                                     }
   222 
   223                                     int caseValue = castab[sh[m1] + 1, sh[m2] + 1, sh[m3] + 1];
   224                                     if (caseValue != 0)
   225                                     {
   226                                         switch (caseValue)
   227                                         {
   228                                             case 1: // Line between vertices 1 and 2
   229                                                 x1 = xh[m1];
   230                                                 y1 = yh[m1];
   231                                                 x2 = xh[m2];
   232                                                 y2 = yh[m2];
   233                                                 break;
   234                                             case 2: // Line between vertices 2 and 3
   235                                                 x1 = xh[m2];
   236                                                 y1 = yh[m2];
   237                                                 x2 = xh[m3];
   238                                                 y2 = yh[m3];
   239                                                 break;
   240                                             case 3: // Line between vertices 3 and 1
   241                                                 x1 = xh[m3];
   242                                                 y1 = yh[m3];
   243                                                 x2 = xh[m1];
   244                                                 y2 = yh[m1];
   245                                                 break;
   246                                             case 4: // Line between vertex 1 and side 2-3
   247                                                 x1 = xh[m1];
   248                                                 y1 = yh[m1];
   249                                                 x2 = xsect(m2, m3);
   250                                                 y2 = ysect(m2, m3);
   251                                                 break;
   252                                             case 5: // Line between vertex 2 and side 3-1
   253                                                 x1 = xh[m2];
   254                                                 y1 = yh[m2];
   255                                                 x2 = xsect(m3, m1);
   256                                                 y2 = ysect(m3, m1);
   257                                                 break;
   258                                             case 6: // Line between vertex 3 and side 1-2
   259                                                 x1 = xh[m3];
   260                                                 y1 = yh[m3];
   261                                                 x2 = xsect(m1, m2);
   262                                                 y2 = ysect(m1, m2);
   263                                                 break;
   264                                             case 7: // Line between sides 1-2 and 2-3
   265                                                 x1 = xsect(m1, m2);
   266                                                 y1 = ysect(m1, m2);
   267                                                 x2 = xsect(m2, m3);
   268                                                 y2 = ysect(m2, m3);
   269                                                 break;
   270                                             case 8: // Line between sides 2-3 and 3-1
   271                                                 x1 = xsect(m2, m3);
   272                                                 y1 = ysect(m2, m3);
   273                                                 x2 = xsect(m3, m1);
   274                                                 y2 = ysect(m3, m1);
   275                                                 break;
   276                                             case 9: // Line between sides 3-1 and 1-2
   277                                                 x1 = xsect(m3, m1);
   278                                                 y1 = ysect(m3, m1);
   279                                                 x2 = xsect(m1, m2);
   280                                                 y2 = ysect(m1, m2);
   281                                                 break;
   282                                         }
   283 
   284                                         renderer(x1, y1, x2, y2, z[k]);
   285                                     }
   286                                 }
   287                             }
   288                         }
   289                     }
   290                 }
   291             }
   292         }
   293     }
   294 }