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)
5 // Copyright (c) 2012 Oystein Bjorke
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:
15 // The above copyright notice and this permission notice shall be included
16 // in all copies or substantial portions of the Software.
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.
27 // Represents an axis with logarithmic scale.
29 // --------------------------------------------------------------------------------------------------------------------
30 namespace OxyPlot.Axes
33 using System.Collections.Generic;
34 using System.Diagnostics;
37 /// Represents an axis with logarithmic scale.
40 /// See http://en.wikipedia.org/wiki/Logarithmic_scale.
42 public class LogarithmicAxis : Axis
45 /// Initializes a new instance of the <see cref = "LogarithmicAxis" /> class.
47 public LogarithmicAxis()
49 this.PowerPadding = true;
51 this.FilterMinValue = 0;
55 /// Initializes a new instance of the <see cref="LogarithmicAxis"/> class.
57 /// <param name="pos">
60 /// <param name="title">
63 public LogarithmicAxis(AxisPosition pos, string title)
71 /// Initializes a new instance of the <see cref="LogarithmicAxis"/> class.
73 /// <param name="position">
76 /// <param name="minimum">
79 /// <param name="maximum">
82 /// <param name="title">
85 public LogarithmicAxis(
86 AxisPosition position, double minimum = double.NaN, double maximum = double.NaN, string title = null)
89 this.Position = position;
91 this.Minimum = minimum;
92 this.Maximum = maximum;
96 /// Gets or sets the logarithmic base (normally 10).
99 /// See http://en.wikipedia.org/wiki/Logarithm.
101 /// <value>The logarithmic base.</value>
102 public double Base { get; set; }
105 /// Gets or sets a value indicating whether the ActualMaximum and ActualMinimum values should be padded to the nearest power of the Base.
107 public bool PowerPadding { get; set; }
110 /// Coerces the actual maximum and minimum values.
112 public override void CoerceActualMaxMin()
114 if (double.IsNaN(this.ActualMinimum) || double.IsInfinity(this.ActualMinimum))
116 this.ActualMinimum = 1;
119 if (this.ActualMinimum <= 0)
121 this.ActualMinimum = 1;
124 if (this.ActualMaximum <= this.ActualMinimum)
126 this.ActualMaximum = this.ActualMinimum * 100;
129 base.CoerceActualMaxMin();
133 /// Gets the coordinates used to draw ticks and tick labels (numbers or category names).
135 /// <param name="majorLabelValues">
136 /// The major label values.
138 /// <param name="majorTickValues">
139 /// The major tick values.
141 /// <param name="minorTickValues">
142 /// The minor tick values.
144 public override void GetTickValues(
145 out IList<double> majorLabelValues, out IList<double> majorTickValues, out IList<double> minorTickValues)
147 if (this.ActualMinimum <= 0)
149 this.ActualMinimum = 0.1;
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);
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);
168 majorTickValues = new List<double>();
169 minorTickValues = new List<double>();
171 double epsMin = this.ActualMinimum * 1e-6;
172 double epsMax = this.ActualMaximum * 1e-6;
174 while (d <= d1 + epsMax)
176 // d = RemoveNoiseFromDoubleMath(d);
177 if (d >= this.ActualMinimum - epsMin && d <= this.ActualMaximum + epsMax)
179 majorTickValues.Add(d);
182 for (int i = 1; i < this.Base; i++)
184 double d2 = d * (i + 1);
185 if (d2 > d1 + double.Epsilon)
190 if (d2 > this.ActualMaximum)
195 if (d2 >= this.ActualMinimum && d2 <= this.ActualMaximum)
197 minorTickValues.Add(d2);
202 if (double.IsInfinity(d))
207 if (d < double.Epsilon)
218 if (majorTickValues.Count < 2)
220 base.GetTickValues(out majorLabelValues, out majorTickValues, out minorTickValues);
224 majorLabelValues = majorTickValues;
229 /// Determines whether the specified value is valid.
231 /// <param name="value">
235 /// <c>true</c> if the specified value is valid; otherwise, <c>false</c>.
237 public override bool IsValidValue(double value)
239 return value > 0 && base.IsValidValue(value);
243 /// Determines whether the axis is used for X/Y values.
246 /// <c>true</c> if it is an XY axis; otherwise, <c>false</c> .
248 public override bool IsXyAxis()
254 /// Pans the specified axis.
256 /// <param name="ppt">
257 /// The previous point (screen coordinates).
259 /// <param name="cpt">
260 /// The current point (screen coordinates).
262 public override void Pan(ScreenPoint ppt, ScreenPoint cpt)
264 if (!this.IsPanEnabled)
269 bool isHorizontal = this.IsHorizontal();
271 double x0 = this.InverseTransform(isHorizontal ? ppt.X : ppt.Y);
272 double x1 = this.InverseTransform(isHorizontal ? cpt.X : cpt.Y);
274 if (Math.Abs(x1) < double.Epsilon)
281 double newMinimum = this.ActualMinimum * dx;
282 double newMaximum = this.ActualMaximum * dx;
283 if (newMinimum < this.AbsoluteMinimum)
285 newMinimum = this.AbsoluteMinimum;
286 newMaximum = newMinimum * this.ActualMaximum / this.ActualMinimum;
289 if (newMaximum > this.AbsoluteMaximum)
291 newMaximum = this.AbsoluteMaximum;
292 newMinimum = newMaximum * this.ActualMaximum / this.ActualMinimum;
295 this.ViewMinimum = newMinimum;
296 this.ViewMaximum = newMaximum;
298 this.OnAxisChanged(new AxisChangedEventArgs(AxisChangeTypes.Pan));
302 /// Transforms the specified coordinate to screen coordinates.
308 /// The transformed value (screen coordinate).
310 public override double Transform(double x)
312 Debug.Assert(x > 0, "Value should be positive.");
318 return (Math.Log(x) - this.offset) * this.scale;
322 /// Zooms the axis at the specified coordinate.
324 /// <param name="factor">
328 /// The coordinate to zoom at.
330 public override void ZoomAt(double factor, double x)
332 if (!this.IsZoomEnabled)
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);
343 this.ViewMinimum = Math.Max(newViewMinimum, this.AbsoluteMinimum);
344 this.ViewMaximum = Math.Min(newViewMaximum, this.AbsoluteMaximum);
348 /// Applies a transformation after the inverse transform of the value. This is used in logarithmic axis.
350 /// <param name="x">The value to transform.</param>
352 /// The transformed value.
354 internal override double PostInverseTransform(double x)
360 /// Applies a transformation before the transform the value. This is used in logarithmic axis.
362 /// <param name="x">The value to transform.</param>
364 /// The transformed value.
366 internal override double PreTransform(double x)
368 Debug.Assert(x > 0, "Value should be positive.");
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'.
384 internal override void UpdateActualMaxMin()
386 if (this.PowerPadding)
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))
393 this.ActualMinimum = Math.Exp(e0 * logBase).RemoveNoiseFromDoubleMath();
396 if (!double.IsNaN(this.ActualMaximum))
398 this.ActualMaximum = Math.Exp(e1 * logBase).RemoveNoiseFromDoubleMath();
402 base.UpdateActualMaxMin();