Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.Problems.GrammaticalOptimization/DynamicDataDisplay/Charts/Isolines/IsolineBuilder.cs @ 13777

Last change on this file since 13777 was 12503, checked in by aballeit, 10 years ago

#2283 added GUI and charts; fixed MCTS

File size: 20.3 KB
Line 
1using System;
2using System.Linq;
3using System.Diagnostics;
4using System.Runtime.Serialization;
5using System.Windows;
6using System.Windows.Media;
7using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
8using Microsoft.Research.DynamicDataDisplay.DataSources;
9using System.Collections.Generic;
10
11namespace Microsoft.Research.DynamicDataDisplay.Charts.Isolines
12{
13  /// <summary>
14  /// Generates geometric object for isolines of the input 2d scalar field.
15  /// </summary>
16  public sealed class IsolineBuilder
17  {
18    /// <summary>
19    /// The density of isolines means the number of levels to draw.
20    /// </summary>
21    private int density = 12;
22
23    private bool[,] processed;
24
25    /// <summary>Number to be treated as missing value. NaN if no missing value is specified</summary>
26    private double missingValue = Double.NaN;
27
28    static IsolineBuilder()
29    {
30      SetCellDictionaries();
31    }
32
33    /// <summary>
34    /// Initializes a new instance of the <see cref="IsolineBuilder"/> class.
35    /// </summary>
36    public IsolineBuilder() { }
37
38    /// <summary>
39    /// Initializes a new instance of the <see cref="IsolineBuilder"/> class for specified 2d scalar data source.
40    /// </summary>
41    /// <param name="dataSource">The data source with 2d scalar data.</param>
42    public IsolineBuilder(IDataSource2D<double> dataSource)
43    {
44      DataSource = dataSource;
45    }
46
47    public double MissingValue
48    {
49      get
50      {
51        return missingValue;
52      }
53      set
54      {
55        missingValue = value;
56      }
57    }
58
59    #region Private methods
60
61    private static Dictionary<int, Dictionary<int, Edge>> dictChooser = new Dictionary<int, Dictionary<int, Edge>>();
62    private static void SetCellDictionaries()
63    {
64      var bottomDict = new Dictionary<int, Edge>();
65      bottomDict.Add((int)CellBitmask.RightBottom, Edge.Right);
66      bottomDict.Add(Edge.Left,
67        CellBitmask.LeftTop,
68        CellBitmask.LeftBottom | CellBitmask.RightBottom | CellBitmask.RightTop,
69        CellBitmask.LeftTop | CellBitmask.RightBottom | CellBitmask.RightTop,
70        CellBitmask.LeftBottom);
71      bottomDict.Add(Edge.Right,
72        CellBitmask.RightTop,
73        CellBitmask.LeftBottom | CellBitmask.RightBottom | CellBitmask.LeftTop,
74        CellBitmask.LeftBottom | CellBitmask.LeftTop | CellBitmask.RightTop);
75      bottomDict.Add(Edge.Top,
76        CellBitmask.RightBottom | CellBitmask.RightTop,
77        CellBitmask.LeftBottom | CellBitmask.LeftTop);
78
79      var leftDict = new Dictionary<int, Edge>();
80      leftDict.Add(Edge.Top,
81        CellBitmask.LeftTop,
82        CellBitmask.LeftBottom | CellBitmask.RightBottom | CellBitmask.RightTop);
83      leftDict.Add(Edge.Right,
84        CellBitmask.LeftTop | CellBitmask.RightTop,
85        CellBitmask.LeftBottom | CellBitmask.RightBottom);
86      leftDict.Add(Edge.Bottom,
87        CellBitmask.RightBottom | CellBitmask.RightTop | CellBitmask.LeftTop,
88        CellBitmask.LeftBottom);
89
90      var topDict = new Dictionary<int, Edge>();
91      topDict.Add(Edge.Right,
92        CellBitmask.RightTop,
93        CellBitmask.LeftTop | CellBitmask.LeftBottom | CellBitmask.RightBottom);
94      topDict.Add(Edge.Right,
95        CellBitmask.RightBottom,
96        CellBitmask.LeftTop | CellBitmask.LeftBottom | CellBitmask.RightTop);
97      topDict.Add(Edge.Left,
98        CellBitmask.RightBottom | CellBitmask.RightTop | CellBitmask.LeftTop,
99        CellBitmask.LeftBottom,
100        CellBitmask.LeftTop,
101        CellBitmask.LeftBottom | CellBitmask.RightBottom | CellBitmask.RightTop);
102      topDict.Add(Edge.Bottom,
103        CellBitmask.RightBottom | CellBitmask.RightTop,
104        CellBitmask.LeftTop | CellBitmask.LeftBottom);
105
106      var rightDict = new Dictionary<int, Edge>();
107      rightDict.Add(Edge.Top,
108        CellBitmask.RightTop,
109        CellBitmask.LeftBottom | CellBitmask.RightBottom | CellBitmask.LeftTop);
110      rightDict.Add(Edge.Left,
111        CellBitmask.LeftTop | CellBitmask.RightTop,
112        CellBitmask.LeftBottom | CellBitmask.RightBottom);
113      rightDict.Add(Edge.Bottom,
114        CellBitmask.RightBottom,
115        CellBitmask.LeftTop | CellBitmask.LeftBottom | CellBitmask.RightTop);
116
117      dictChooser.Add((int)Edge.Left, leftDict);
118      dictChooser.Add((int)Edge.Right, rightDict);
119      dictChooser.Add((int)Edge.Bottom, bottomDict);
120      dictChooser.Add((int)Edge.Top, topDict);
121    }
122
123    private Edge GetOutEdge(Edge inEdge, ValuesInCell cv, IrregularCell rect, double value)
124    {
125      // value smaller than all values in corners or
126      // value greater than all values in corners
127      if (!cv.ValueBelongTo(value))
128      {
129        throw new IsolineGenerationException(Strings.Exceptions.IsolinesValueIsOutOfCell);
130      }
131
132      CellBitmask cellVal = cv.GetCellValue(value);
133      var dict = dictChooser[(int)inEdge];
134      if (dict.ContainsKey((int)cellVal))
135      {
136        Edge result = dict[(int)cellVal];
137        switch (result)
138        {
139          case Edge.Left:
140            if (cv.LeftTop.IsNaN() || cv.LeftBottom.IsNaN())
141              result = Edge.None;
142            break;
143          case Edge.Right:
144            if (cv.RightTop.IsNaN() || cv.RightBottom.IsNaN())
145              result = Edge.None;
146            break;
147          case Edge.Top:
148            if (cv.RightTop.IsNaN() || cv.LeftTop.IsNaN())
149              result = Edge.None;
150            break;
151          case Edge.Bottom:
152            if (cv.LeftBottom.IsNaN() || cv.RightBottom.IsNaN())
153              result = Edge.None;
154            break;
155        }
156        return result;
157      }
158      else if (cellVal.IsDiagonal())
159      {
160        return GetOutForOpposite(inEdge, cellVal, value, cv, rect);
161      }
162
163      const double near_zero = 0.0001;
164      const double near_one = 1 - near_zero;
165
166      double lt = cv.LeftTop;
167      double rt = cv.RightTop;
168      double rb = cv.RightBottom;
169      double lb = cv.LeftBottom;
170
171      switch (inEdge)
172      {
173        case Edge.Left:
174          if (value == lt)
175            value = near_one * lt + near_zero * lb;
176          else if (value == lb)
177            value = near_one * lb + near_zero * lt;
178          else
179            return Edge.None;
180          // Now this is possible because of missing value
181          //throw new IsolineGenerationException(Strings.Exceptions.IsolinesUnsupportedCase);
182          break;
183        case Edge.Top:
184          if (value == rt)
185            value = near_one * rt + near_zero * lt;
186          else if (value == lt)
187            value = near_one * lt + near_zero * rt;
188          else
189            return Edge.None;
190          // Now this is possibe because of missing value
191          //throw new IsolineGenerationException(Strings.Exceptions.IsolinesUnsupportedCase);
192          break;
193        case Edge.Right:
194          if (value == rb)
195            value = near_one * rb + near_zero * rt;
196          else if (value == rt)
197            value = near_one * rt + near_zero * rb;
198          else
199            return Edge.None;
200          // Now this is possibe because of missing value
201          //throw new IsolineGenerationException(Strings.Exceptions.IsolinesUnsupportedCase);
202          break;
203        case Edge.Bottom:
204          if (value == rb)
205            value = near_one * rb + near_zero * lb;
206          else if (value == lb)
207            value = near_one * lb + near_zero * rb;
208          else
209            return Edge.None;
210          // Now this is possibe because of missing value
211          //throw new IsolineGenerationException(Strings.Exceptions.IsolinesUnsupportedCase);
212          break;
213      }
214
215      // Recursion?
216      //return GetOutEdge(inEdge, cv, rect, value);
217
218      return Edge.None;
219    }
220
221    private Edge GetOutForOpposite(Edge inEdge, CellBitmask cellVal, double value, ValuesInCell cellValues, IrregularCell rect)
222    {
223      Edge outEdge;
224
225      SubCell subCell = GetSubCell(inEdge, value, cellValues);
226
227      int iters = 1000; // max number of iterations
228      do
229      {
230        ValuesInCell subValues = cellValues.GetSubCell(subCell);
231        IrregularCell subRect = rect.GetSubRect(subCell);
232        outEdge = GetOutEdge(inEdge, subValues, subRect, value);
233        if (outEdge == Edge.None)
234          return Edge.None;
235        bool isAppropriate = subCell.IsAppropriate(outEdge);
236        if (isAppropriate)
237        {
238          ValuesInCell sValues = subValues.GetSubCell(subCell);
239
240          Point point = GetPointXY(outEdge, value, subValues, subRect);
241          segments.AddPoint(point);
242          return outEdge;
243        }
244        else
245        {
246          subCell = GetAdjacentEdge(subCell, outEdge);
247        }
248
249        byte e = (byte)outEdge;
250        inEdge = (Edge)((e > 2) ? (e >> 2) : (e << 2));
251        iters--;
252      } while (iters >= 0);
253
254      throw new IsolineGenerationException(Strings.Exceptions.IsolinesDataIsUndetailized);
255    }
256
257    private static SubCell GetAdjacentEdge(SubCell sub, Edge edge)
258    {
259      SubCell res = SubCell.LeftBottom;
260
261      switch (sub)
262      {
263        case SubCell.LeftBottom:
264          res = edge == Edge.Top ? SubCell.LeftTop : SubCell.RightBottom;
265          break;
266        case SubCell.LeftTop:
267          res = edge == Edge.Bottom ? SubCell.LeftBottom : SubCell.RightTop;
268          break;
269        case SubCell.RightBottom:
270          res = edge == Edge.Top ? SubCell.RightTop : SubCell.LeftBottom;
271          break;
272        case SubCell.RightTop:
273        default:
274          res = edge == Edge.Bottom ? SubCell.RightBottom : SubCell.LeftTop;
275          break;
276      }
277
278      return res;
279    }
280
281    private static SubCell GetSubCell(Edge inEdge, double value, ValuesInCell vc)
282    {
283      double lb = vc.LeftBottom;
284      double rb = vc.RightBottom;
285      double rt = vc.RightTop;
286      double lt = vc.LeftTop;
287
288      SubCell res = SubCell.LeftBottom;
289      switch (inEdge)
290      {
291        case Edge.Left:
292          res = (Math.Abs(value - lb) < Math.Abs(value - lt)) ? SubCell.LeftBottom : SubCell.LeftTop;
293          break;
294        case Edge.Top:
295          res = (Math.Abs(value - lt) < Math.Abs(value - rt)) ? SubCell.LeftTop : SubCell.RightTop;
296          break;
297        case Edge.Right:
298          res = (Math.Abs(value - rb) < Math.Abs(value - rt)) ? SubCell.RightBottom : SubCell.RightTop;
299          break;
300        case Edge.Bottom:
301        default:
302          res = (Math.Abs(value - lb) < Math.Abs(value - rb)) ? SubCell.LeftBottom : SubCell.RightBottom;
303          break;
304      }
305
306      ValuesInCell subValues = vc.GetSubCell(res);
307      bool valueInside = subValues.ValueBelongTo(value);
308      if (!valueInside)
309      {
310        throw new IsolineGenerationException(Strings.Exceptions.IsolinesDataIsUndetailized);
311      }
312
313      return res;
314    }
315
316    private static Point GetPoint(double value, double a1, double a2, Vector v1, Vector v2)
317    {
318      double ratio = (value - a1) / (a2 - a1);
319
320      Verify.IsTrue(0 <= ratio && ratio <= 1);
321
322      Vector r = (1 - ratio) * v1 + ratio * v2;
323      return new Point(r.X, r.Y);
324    }
325
326    private Point GetPointXY(Edge edge, double value, ValuesInCell vc, IrregularCell rect)
327    {
328      double lt = vc.LeftTop;
329      double lb = vc.LeftBottom;
330      double rb = vc.RightBottom;
331      double rt = vc.RightTop;
332
333      switch (edge)
334      {
335        case Edge.Left:
336          return GetPoint(value, lb, lt, rect.LeftBottom, rect.LeftTop);
337        case Edge.Top:
338          return GetPoint(value, lt, rt, rect.LeftTop, rect.RightTop);
339        case Edge.Right:
340          return GetPoint(value, rb, rt, rect.RightBottom, rect.RightTop);
341        case Edge.Bottom:
342          return GetPoint(value, lb, rb, rect.LeftBottom, rect.RightBottom);
343        default:
344          throw new InvalidOperationException();
345      }
346    }
347
348    private bool BelongsToEdge(double value, double edgeValue1, double edgeValue2, bool onBoundary)
349    {
350      if (!Double.IsNaN(missingValue) && (edgeValue1 == missingValue || edgeValue2 == missingValue))
351        return false;
352
353      if (onBoundary)
354      {
355        return (edgeValue1 <= value && value < edgeValue2) ||
356        (edgeValue2 <= value && value < edgeValue1);
357      }
358      else
359      {
360        return (edgeValue1 < value && value < edgeValue2) ||
361          (edgeValue2 < value && value < edgeValue1);
362      }
363    }
364
365    private bool IsPassed(Edge edge, int i, int j, byte[,] edges)
366    {
367      switch (edge)
368      {
369        case Edge.Left:
370          return (i == 0) || (edges[i, j] & (byte)edge) != 0;
371        case Edge.Bottom:
372          return (j == 0) || (edges[i, j] & (byte)edge) != 0;
373        case Edge.Top:
374          return (j == edges.GetLength(1) - 2) || (edges[i, j + 1] & (byte)Edge.Bottom) != 0;
375        case Edge.Right:
376          return (i == edges.GetLength(0) - 2) || (edges[i + 1, j] & (byte)Edge.Left) != 0;
377        default:
378          throw new InvalidOperationException();
379      }
380    }
381
382    private void MakeEdgePassed(Edge edge, int i, int j)
383    {
384      switch (edge)
385      {
386        case Edge.Left:
387        case Edge.Bottom:
388          edges[i, j] |= (byte)edge;
389          break;
390        case Edge.Top:
391          edges[i, j + 1] |= (byte)Edge.Bottom;
392          break;
393        case Edge.Right:
394          edges[i + 1, j] |= (byte)Edge.Left;
395          break;
396        default:
397          throw new InvalidOperationException();
398      }
399    }
400
401    private Edge TrackLine(Edge inEdge, double value, ref int x, ref int y, out double newX, out double newY)
402    {
403      // Getting output edge
404      ValuesInCell vc = (missingValue.IsNaN()) ?
405        (new ValuesInCell(values[x, y],
406          values[x + 1, y],
407          values[x + 1, y + 1],
408          values[x, y + 1])) :
409        (new ValuesInCell(values[x, y],
410          values[x + 1, y],
411          values[x + 1, y + 1],
412          values[x, y + 1],
413          missingValue));
414
415      IrregularCell rect = new IrregularCell(
416        grid[x, y],
417        grid[x + 1, y],
418        grid[x + 1, y + 1],
419        grid[x, y + 1]);
420
421      Edge outEdge = GetOutEdge(inEdge, vc, rect, value);
422      if (outEdge == Edge.None)
423      {
424        newX = newY = -1; // Impossible cell indices
425        return Edge.None;
426      }
427
428      // Drawing new segment
429      Point point = GetPointXY(outEdge, value, vc, rect);
430      newX = point.X;
431      newY = point.Y;
432      segments.AddPoint(point);
433      processed[x, y] = true;
434
435      // Whether out-edge already was passed?
436      if (IsPassed(outEdge, x, y, edges)) // line is closed
437      {
438        //MakeEdgePassed(outEdge, x, y); // boundaries should be marked as passed too
439        return Edge.None;
440      }
441
442      // Make this edge passed
443      MakeEdgePassed(outEdge, x, y);
444
445      // Getting next cell's indices
446      switch (outEdge)
447      {
448        case Edge.Left:
449          x--;
450          return Edge.Right;
451        case Edge.Top:
452          y++;
453          return Edge.Bottom;
454        case Edge.Right:
455          x++;
456          return Edge.Left;
457        case Edge.Bottom:
458          y--;
459          return Edge.Top;
460        default:
461          throw new InvalidOperationException();
462      }
463    }
464
465    private void TrackLineNonRecursive(Edge inEdge, double value, int x, int y)
466    {
467      int s = x, t = y;
468
469      ValuesInCell vc = (missingValue.IsNaN()) ?
470        (new ValuesInCell(values[x, y],
471          values[x + 1, y],
472          values[x + 1, y + 1],
473          values[x, y + 1])) :
474        (new ValuesInCell(values[x, y],
475          values[x + 1, y],
476          values[x + 1, y + 1],
477          values[x, y + 1],
478          missingValue));
479
480      IrregularCell rect = new IrregularCell(
481        grid[x, y],
482        grid[x + 1, y],
483        grid[x + 1, y + 1],
484        grid[x, y + 1]);
485
486      Point point = GetPointXY(inEdge, value, vc, rect);
487
488      segments.StartLine(point, (value - minMax.Min) / (minMax.Max - minMax.Min), value);
489
490      MakeEdgePassed(inEdge, x, y);
491
492      //processed[x, y] = true;
493
494      double x2, y2;
495      do
496      {
497        inEdge = TrackLine(inEdge, value, ref s, ref t, out x2, out y2);
498      } while (inEdge != Edge.None);
499    }
500
501    #endregion
502
503    private bool HasIsoline(int x, int y)
504    {
505      return (edges[x, y] != 0 &&
506        ((x < edges.GetLength(0) - 1 && edges[x + 1, y] != 0) ||
507         (y < edges.GetLength(1) - 1 && edges[x, y + 1] != 0)));
508    }
509
510    /// <summary>Finds isoline for specified reference value</summary>
511    /// <param name="value">Reference value</param>
512    private void PrepareCells(double value)
513    {
514      double currentRatio = (value - minMax.Min) / (minMax.Max - minMax.Min);
515
516      if (currentRatio < 0 || currentRatio > 1)
517        return; // No contour lines for such value
518
519      int xSize = dataSource.Width;
520      int ySize = dataSource.Height;
521      int x, y;
522      for (x = 0; x < xSize; x++)
523        for (y = 0; y < ySize; y++)
524          edges[x, y] = 0;
525
526      processed = new bool[xSize, ySize];
527
528      // Looking in boundaries.
529      // left
530      for (y = 1; y < ySize; y++)
531      {
532        if (BelongsToEdge(value, values[0, y - 1], values[0, y], true) &&
533          (edges[0, y - 1] & (byte)Edge.Left) == 0)
534        {
535          TrackLineNonRecursive(Edge.Left, value, 0, y - 1);
536        }
537      }
538
539      // bottom
540      for (x = 0; x < xSize - 1; x++)
541      {
542        if (BelongsToEdge(value, values[x, 0], values[x + 1, 0], true)
543          && (edges[x, 0] & (byte)Edge.Bottom) == 0)
544        {
545          TrackLineNonRecursive(Edge.Bottom, value, x, 0);
546        };
547      }
548
549      // right
550      x = xSize - 1;
551      for (y = 1; y < ySize; y++)
552      {
553        // Is this correct?
554        //if (BelongsToEdge(value, values[0, y - 1], values[0, y], true) &&
555        //    (edges[0, y - 1] & (byte)Edge.Left) == 0)
556        //{
557        //    TrackLineNonRecursive(Edge.Left, value, 0, y - 1);
558        //};
559
560        if (BelongsToEdge(value, values[x, y - 1], values[x, y], true) &&
561          (edges[x, y - 1] & (byte)Edge.Left) == 0)
562        {
563          TrackLineNonRecursive(Edge.Right, value, x - 1, y - 1);
564        };
565      }
566
567      // horizontals
568      for (x = 1; x < xSize - 1; x++)
569        for (y = 1; y < ySize - 1; y++)
570        {
571          if ((edges[x, y] & (byte)Edge.Bottom) == 0 &&
572            BelongsToEdge(value, values[x, y], values[x + 1, y], false) &&
573            !processed[x, y - 1])
574          {
575            TrackLineNonRecursive(Edge.Top, value, x, y - 1);
576          }
577          if ((edges[x, y] & (byte)Edge.Bottom) == 0 &&
578            BelongsToEdge(value, values[x, y], values[x + 1, y], false) &&
579            !processed[x, y])
580          {
581            TrackLineNonRecursive(Edge.Bottom, value, x, y);
582          }
583          if ((edges[x, y] & (byte)Edge.Left) == 0 &&
584            BelongsToEdge(value, values[x, y], values[x, y - 1], false) &&
585            !processed[x - 1, y - 1])
586          {
587            TrackLineNonRecursive(Edge.Right, value, x - 1, y - 1);
588          }
589          if ((edges[x, y] & (byte)Edge.Left) == 0 &&
590            BelongsToEdge(value, values[x, y], values[x, y - 1], false) &&
591            !processed[x, y - 1])
592          {
593            TrackLineNonRecursive(Edge.Left, value, x, y - 1);
594          }
595        }
596    }
597
598    /// <summary>
599    /// Builds isoline data for 2d scalar field contained in data source.
600    /// </summary>
601    /// <returns>Collection of data describing built isolines.</returns>
602    public IsolineCollection BuildIsoline()
603    {
604      VerifyDataSource();
605
606      segments = new IsolineCollection();
607
608      // Cannot draw isolines for fields with one dimension lesser than 2
609      if (dataSource.Width < 2 || dataSource.Height < 2)
610        return segments;
611
612      Init();
613
614      if (!minMax.IsEmpty)
615      {
616        values = dataSource.Data;
617        double[] levels = GetLevelsForIsolines();
618
619        foreach (double level in levels)
620        {
621          PrepareCells(level);
622        }
623
624        if (segments.Lines.Count > 0 && segments.Lines[segments.Lines.Count - 1].OtherPoints.Count == 0)
625          segments.Lines.RemoveAt(segments.Lines.Count - 1);
626      }
627      return segments;
628    }
629
630    private void Init()
631    {
632      if (dataSource.Range.HasValue)
633        minMax = dataSource.Range.Value;
634      else
635        minMax = (Double.IsNaN(missingValue) ? dataSource.GetMinMax() : dataSource.GetMinMax(missingValue));
636
637      if (dataSource.MissingValue.HasValue)
638        missingValue = dataSource.MissingValue.Value;
639
640      segments.Min = minMax.Min;
641      segments.Max = minMax.Max;
642    }
643
644    /// <summary>
645    /// Builds isoline data for the specified level in 2d scalar field.
646    /// </summary>
647    /// <param name="level">The level.</param>
648    /// <returns></returns>
649    public IsolineCollection BuildIsoline(double level)
650    {
651      VerifyDataSource();
652
653      segments = new IsolineCollection();
654
655      Init();
656     
657      if (!minMax.IsEmpty)
658      {
659        values = dataSource.Data;
660
661
662        PrepareCells(level);
663
664        if (segments.Lines.Count > 0 && segments.Lines[segments.Lines.Count - 1].OtherPoints.Count == 0)
665          segments.Lines.RemoveAt(segments.Lines.Count - 1);
666      }
667      return segments;
668    }
669
670    private void VerifyDataSource()
671    {
672      if (dataSource == null)
673        throw new InvalidOperationException(Strings.Exceptions.IsolinesDataSourceShouldBeSet);
674    }
675
676    IsolineCollection segments;
677
678    private double[,] values;
679    private byte[,] edges;
680    private Point[,] grid;
681
682    private Range<double> minMax;
683    private IDataSource2D<double> dataSource;
684    /// <summary>
685    /// Gets or sets the data source - 2d scalar field.
686    /// </summary>
687    /// <value>The data source.</value>
688    public IDataSource2D<double> DataSource
689    {
690      get { return dataSource; }
691      set
692      {
693        if (dataSource != value)
694        {
695          value.VerifyNotNull("value");
696
697          dataSource = value;
698          grid = dataSource.Grid;
699          edges = new byte[dataSource.Width, dataSource.Height];
700        }
701      }
702    }
703
704    private const double shiftPercent = 0.05;
705    private double[] GetLevelsForIsolines()
706    {
707      double[] levels;
708      double min = minMax.Min;
709      double max = minMax.Max;
710
711      double step = (max - min) / (density - 1);
712      double delta = (max - min);
713
714      levels = new double[density];
715      levels[0] = min + delta * shiftPercent;
716      levels[levels.Length - 1] = max - delta * shiftPercent;
717
718      for (int i = 1; i < levels.Length - 1; i++)
719        levels[i] = min + i * step;
720
721      return levels;
722    }
723  }
724}
Note: See TracBrowser for help on using the repository browser.