External/OxyPlot/OxyPlot/Axes/LogarithmicAxis.cs
author moel.mich
Sat, 08 Jun 2013 16:53:22 +0000
changeset 391 5be8f2773237
permissions -rw-r--r--
Added the source code of OxyPlot as of commit d190d7748a73 (6.5.2013).
     1 // --------------------------------------------------------------------------------------------------------------------
     2 // <copyright file="LogarithmicAxis.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 //   Represents an axis with logarithmic scale.
    28 // </summary>
    29 // --------------------------------------------------------------------------------------------------------------------
    30 namespace OxyPlot.Axes
    31 {
    32     using System;
    33     using System.Collections.Generic;
    34     using System.Diagnostics;
    35 
    36     /// <summary>
    37     /// Represents an axis with logarithmic scale.
    38     /// </summary>
    39     /// <remarks>
    40     /// See http://en.wikipedia.org/wiki/Logarithmic_scale.
    41     /// </remarks>
    42     public class LogarithmicAxis : Axis
    43     {
    44         /// <summary>
    45         /// Initializes a new instance of the <see cref = "LogarithmicAxis" /> class.
    46         /// </summary>
    47         public LogarithmicAxis()
    48         {
    49             this.PowerPadding = true;
    50             this.Base = 10;
    51             this.FilterMinValue = 0;
    52         }
    53 
    54         /// <summary>
    55         /// Initializes a new instance of the <see cref="LogarithmicAxis"/> class.
    56         /// </summary>
    57         /// <param name="pos">
    58         /// The position.
    59         /// </param>
    60         /// <param name="title">
    61         /// The title.
    62         /// </param>
    63         public LogarithmicAxis(AxisPosition pos, string title)
    64             : this()
    65         {
    66             this.Position = pos;
    67             this.Title = title;
    68         }
    69 
    70         /// <summary>
    71         /// Initializes a new instance of the <see cref="LogarithmicAxis"/> class.
    72         /// </summary>
    73         /// <param name="position">
    74         /// The position.
    75         /// </param>
    76         /// <param name="minimum">
    77         /// The minimum.
    78         /// </param>
    79         /// <param name="maximum">
    80         /// The maximum.
    81         /// </param>
    82         /// <param name="title">
    83         /// The title.
    84         /// </param>
    85         public LogarithmicAxis(
    86             AxisPosition position, double minimum = double.NaN, double maximum = double.NaN, string title = null)
    87             : this()
    88         {
    89             this.Position = position;
    90             this.Title = title;
    91             this.Minimum = minimum;
    92             this.Maximum = maximum;
    93         }
    94 
    95         /// <summary>
    96         /// Gets or sets the logarithmic base (normally 10).
    97         /// </summary>
    98         /// <remarks>
    99         /// See http://en.wikipedia.org/wiki/Logarithm.
   100         /// </remarks>
   101         /// <value>The logarithmic base.</value>
   102         public double Base { get; set; }
   103 
   104         /// <summary>
   105         /// Gets or sets a value indicating whether the ActualMaximum and ActualMinimum values should be padded to the nearest power of the Base.
   106         /// </summary>
   107         public bool PowerPadding { get; set; }
   108 
   109         /// <summary>
   110         /// Coerces the actual maximum and minimum values.
   111         /// </summary>
   112         public override void CoerceActualMaxMin()
   113         {
   114             if (double.IsNaN(this.ActualMinimum) || double.IsInfinity(this.ActualMinimum))
   115             {
   116                 this.ActualMinimum = 1;
   117             }
   118 
   119             if (this.ActualMinimum <= 0)
   120             {
   121                 this.ActualMinimum = 1;
   122             }
   123 
   124             if (this.ActualMaximum <= this.ActualMinimum)
   125             {
   126                 this.ActualMaximum = this.ActualMinimum * 100;
   127             }
   128 
   129             base.CoerceActualMaxMin();
   130         }
   131 
   132         /// <summary>
   133         /// Gets the coordinates used to draw ticks and tick labels (numbers or category names).
   134         /// </summary>
   135         /// <param name="majorLabelValues">
   136         /// The major label values.
   137         /// </param>
   138         /// <param name="majorTickValues">
   139         /// The major tick values.
   140         /// </param>
   141         /// <param name="minorTickValues">
   142         /// The minor tick values.
   143         /// </param>
   144         public override void GetTickValues(
   145             out IList<double> majorLabelValues, out IList<double> majorTickValues, out IList<double> minorTickValues)
   146         {
   147             if (this.ActualMinimum <= 0)
   148             {
   149                 this.ActualMinimum = 0.1;
   150             }
   151 
   152             double logBase = Math.Log(this.Base);
   153             var e0 = (int)Math.Floor(Math.Log(this.ActualMinimum) / logBase);
   154             var e1 = (int)Math.Ceiling(Math.Log(this.ActualMaximum) / logBase);
   155 
   156             // find the min & max values for the specified base
   157             // round to max 10 digits
   158             double p0 = Math.Pow(this.Base, e0);
   159             double p1 = Math.Pow(this.Base, e1);
   160             double d0 = Math.Round(p0, 10);
   161             double d1 = Math.Round(p1, 10);
   162             if (d0 <= 0)
   163             {
   164                 d0 = p0;
   165             }
   166 
   167             double d = d0;
   168             majorTickValues = new List<double>();
   169             minorTickValues = new List<double>();
   170 
   171             double epsMin = this.ActualMinimum * 1e-6;
   172             double epsMax = this.ActualMaximum * 1e-6;
   173 
   174             while (d <= d1 + epsMax)
   175             {
   176                 // d = RemoveNoiseFromDoubleMath(d);
   177                 if (d >= this.ActualMinimum - epsMin && d <= this.ActualMaximum + epsMax)
   178                 {
   179                     majorTickValues.Add(d);
   180                 }
   181 
   182                 for (int i = 1; i < this.Base; i++)
   183                 {
   184                     double d2 = d * (i + 1);
   185                     if (d2 > d1 + double.Epsilon)
   186                     {
   187                         break;
   188                     }
   189 
   190                     if (d2 > this.ActualMaximum)
   191                     {
   192                         break;
   193                     }
   194 
   195                     if (d2 >= this.ActualMinimum && d2 <= this.ActualMaximum)
   196                     {
   197                         minorTickValues.Add(d2);
   198                     }
   199                 }
   200 
   201                 d *= this.Base;
   202                 if (double.IsInfinity(d))
   203                 {
   204                     break;
   205                 }
   206 
   207                 if (d < double.Epsilon)
   208                 {
   209                     break;
   210                 }
   211 
   212                 if (double.IsNaN(d))
   213                 {
   214                     break;
   215                 }
   216             }
   217 
   218             if (majorTickValues.Count < 2)
   219             {
   220                 base.GetTickValues(out majorLabelValues, out majorTickValues, out minorTickValues);
   221             }
   222             else
   223             {
   224                 majorLabelValues = majorTickValues;
   225             }
   226         }
   227 
   228         /// <summary>
   229         /// Determines whether the specified value is valid.
   230         /// </summary>
   231         /// <param name="value">
   232         /// The value.
   233         /// </param>
   234         /// <returns>
   235         /// <c>true</c> if the specified value is valid; otherwise, <c>false</c>.
   236         /// </returns>
   237         public override bool IsValidValue(double value)
   238         {
   239             return value > 0 && base.IsValidValue(value);
   240         }
   241 
   242         /// <summary>
   243         /// Determines whether the axis is used for X/Y values.
   244         /// </summary>
   245         /// <returns>
   246         /// <c>true</c> if it is an XY axis; otherwise, <c>false</c> .
   247         /// </returns>
   248         public override bool IsXyAxis()
   249         {
   250             return true;
   251         }
   252 
   253         /// <summary>
   254         /// Pans the specified axis.
   255         /// </summary>
   256         /// <param name="ppt">
   257         /// The previous point (screen coordinates).
   258         /// </param>
   259         /// <param name="cpt">
   260         /// The current point (screen coordinates).
   261         /// </param>
   262         public override void Pan(ScreenPoint ppt, ScreenPoint cpt)
   263         {
   264             if (!this.IsPanEnabled)
   265             {
   266                 return;
   267             }
   268 
   269             bool isHorizontal = this.IsHorizontal();
   270 
   271             double x0 = this.InverseTransform(isHorizontal ? ppt.X : ppt.Y);
   272             double x1 = this.InverseTransform(isHorizontal ? cpt.X : cpt.Y);
   273 
   274             if (Math.Abs(x1) < double.Epsilon)
   275             {
   276                 return;
   277             }
   278 
   279             double dx = x0 / x1;
   280 
   281             double newMinimum = this.ActualMinimum * dx;
   282             double newMaximum = this.ActualMaximum * dx;
   283             if (newMinimum < this.AbsoluteMinimum)
   284             {
   285                 newMinimum = this.AbsoluteMinimum;
   286                 newMaximum = newMinimum * this.ActualMaximum / this.ActualMinimum;
   287             }
   288 
   289             if (newMaximum > this.AbsoluteMaximum)
   290             {
   291                 newMaximum = this.AbsoluteMaximum;
   292                 newMinimum = newMaximum * this.ActualMaximum / this.ActualMinimum;
   293             }
   294 
   295             this.ViewMinimum = newMinimum;
   296             this.ViewMaximum = newMaximum;
   297 
   298             this.OnAxisChanged(new AxisChangedEventArgs(AxisChangeTypes.Pan));
   299         }
   300 
   301         /// <summary>
   302         /// Transforms the specified coordinate to screen coordinates.
   303         /// </summary>
   304         /// <param name="x">
   305         /// The value.
   306         /// </param>
   307         /// <returns>
   308         /// The transformed value (screen coordinate).
   309         /// </returns>
   310         public override double Transform(double x)
   311         {
   312             Debug.Assert(x > 0, "Value should be positive.");
   313             if (x <= 0)
   314             {
   315                 return -1;
   316             }
   317 
   318             return (Math.Log(x) - this.offset) * this.scale;
   319         }
   320 
   321         /// <summary>
   322         /// Zooms the axis at the specified coordinate.
   323         /// </summary>
   324         /// <param name="factor">
   325         /// The zoom factor.
   326         /// </param>
   327         /// <param name="x">
   328         /// The coordinate to zoom at.
   329         /// </param>
   330         public override void ZoomAt(double factor, double x)
   331         {
   332             if (!this.IsZoomEnabled)
   333             {
   334                 return;
   335             }
   336 
   337             double px = this.PreTransform(x);
   338             double dx0 = this.PreTransform(this.ActualMinimum) - px;
   339             double dx1 = this.PreTransform(this.ActualMaximum) - px;
   340             double newViewMinimum = this.PostInverseTransform((dx0 / factor) + px);
   341             double newViewMaximum = this.PostInverseTransform((dx1 / factor) + px);
   342 
   343             this.ViewMinimum = Math.Max(newViewMinimum, this.AbsoluteMinimum);
   344             this.ViewMaximum = Math.Min(newViewMaximum, this.AbsoluteMaximum);
   345         }
   346 
   347         /// <summary>
   348         /// Applies a transformation after the inverse transform of the value. This is used in logarithmic axis.
   349         /// </summary>
   350         /// <param name="x">The value to transform.</param>
   351         /// <returns>
   352         /// The transformed value.
   353         /// </returns>
   354         internal override double PostInverseTransform(double x)
   355         {
   356             return Math.Exp(x);
   357         }
   358 
   359         /// <summary>
   360         /// Applies a transformation before the transform the value. This is used in logarithmic axis.
   361         /// </summary>
   362         /// <param name="x">The value to transform.</param>
   363         /// <returns>
   364         /// The transformed value.
   365         /// </returns>
   366         internal override double PreTransform(double x)
   367         {
   368             Debug.Assert(x > 0, "Value should be positive.");
   369 
   370             if (x <= 0)
   371             {
   372                 return 0;
   373             }
   374 
   375             return Math.Log(x);
   376         }
   377 
   378         /// <summary>
   379         /// Updates the actual maximum and minimum values.
   380         /// If the user has zoomed/panned the axis, the internal ViewMaximum/ViewMinimum values will be used.
   381         /// If Maximum or Minimum have been set, these values will be used.
   382         /// Otherwise the maximum and minimum values of the series will be used, including the 'padding'.
   383         /// </summary>
   384         internal override void UpdateActualMaxMin()
   385         {
   386             if (this.PowerPadding)
   387             {
   388                 double logBase = Math.Log(this.Base);
   389                 var e0 = (int)Math.Floor(Math.Log(this.ActualMinimum) / logBase);
   390                 var e1 = (int)Math.Ceiling(Math.Log(this.ActualMaximum) / logBase);
   391                 if (!double.IsNaN(this.ActualMinimum))
   392                 {
   393                     this.ActualMinimum = Math.Exp(e0 * logBase).RemoveNoiseFromDoubleMath();
   394                 }
   395 
   396                 if (!double.IsNaN(this.ActualMaximum))
   397                 {
   398                     this.ActualMaximum = Math.Exp(e1 * logBase).RemoveNoiseFromDoubleMath();
   399                 }
   400             }
   401 
   402             base.UpdateActualMaxMin();
   403         }
   404 
   405     }
   406 }