Initial Commit
This commit is contained in:
92
Charts/Axes/Numeric/CustomBaseNumericLabelProvider.cs
Normal file
92
Charts/Axes/Numeric/CustomBaseNumericLabelProvider.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using Microsoft.Research.DynamicDataDisplay;
|
||||
|
||||
namespace Microsoft.Research.DynamicDataDisplay.Charts.Axes.Numeric
|
||||
{
|
||||
public class CustomBaseNumericLabelProvider : LabelProvider<double>
|
||||
{
|
||||
private double customBase = 2;
|
||||
/// <summary>
|
||||
/// Gets or sets the custom base.
|
||||
/// </summary>
|
||||
/// <value>The custom base.</value>
|
||||
public double CustomBase
|
||||
{
|
||||
get { return customBase; }
|
||||
set
|
||||
{
|
||||
if (Double.IsNaN(value))
|
||||
throw new ArgumentException(Strings.Exceptions.CustomBaseTicksProviderBaseIsNaN);
|
||||
if (value <= 0)
|
||||
throw new ArgumentOutOfRangeException(Strings.Exceptions.CustomBaseTicksProviderBaseIsTooSmall);
|
||||
|
||||
customBase = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CustomBaseNumericLabelProvider"/> class.
|
||||
/// </summary>
|
||||
public CustomBaseNumericLabelProvider() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CustomBaseNumericLabelProvider"/> class.
|
||||
/// </summary>
|
||||
public CustomBaseNumericLabelProvider(double customBase)
|
||||
: this()
|
||||
{
|
||||
CustomBase = customBase;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CustomBaseNumericLabelProvider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="customBase">The custom base.</param>
|
||||
/// <param name="customBaseString">The custom base string.</param>
|
||||
public CustomBaseNumericLabelProvider(double customBase, string customBaseString)
|
||||
: this(customBase)
|
||||
{
|
||||
CustomBaseString = customBaseString;
|
||||
}
|
||||
|
||||
private string customBaseString = null;
|
||||
/// <summary>
|
||||
/// Gets or sets the custom base string.
|
||||
/// </summary>
|
||||
/// <value>The custom base string.</value>
|
||||
public string CustomBaseString
|
||||
{
|
||||
get { return customBaseString; }
|
||||
set
|
||||
{
|
||||
if (customBaseString != value)
|
||||
{
|
||||
customBaseString = value;
|
||||
RaiseChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override string GetStringCore(LabelTickInfo<double> tickInfo)
|
||||
{
|
||||
double value = tickInfo.Tick / customBase;
|
||||
|
||||
string customBaseStr = customBaseString ?? customBase.ToString();
|
||||
string result;
|
||||
if (value == 1)
|
||||
result = customBaseStr;
|
||||
else if (value == -1)
|
||||
{
|
||||
result = "-" + customBaseStr;
|
||||
}
|
||||
else
|
||||
result = value.ToString() + customBaseStr;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
202
Charts/Axes/Numeric/CustomBaseNumericTicksProvider.cs
Normal file
202
Charts/Axes/Numeric/CustomBaseNumericTicksProvider.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
|
||||
using System.Windows.Markup;
|
||||
|
||||
namespace Microsoft.Research.DynamicDataDisplay.Charts.Axes.Numeric
|
||||
{
|
||||
[ContentProperty("TicksProvider")]
|
||||
public class CustomBaseNumericTicksProvider : ITicksProvider<double>
|
||||
{
|
||||
private double customBase = 2;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the custom base.
|
||||
/// </summary>
|
||||
/// <value>The custom base.</value>
|
||||
public double CustomBase
|
||||
{
|
||||
get { return customBase; }
|
||||
set
|
||||
{
|
||||
if (Double.IsNaN(value))
|
||||
throw new ArgumentException(Strings.Exceptions.CustomBaseTicksProviderBaseIsNaN);
|
||||
if (value <= 0)
|
||||
throw new ArgumentOutOfRangeException(Strings.Exceptions.CustomBaseTicksProviderBaseIsTooSmall);
|
||||
|
||||
customBase = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CustomBaseNumericTicksProvider"/> class.
|
||||
/// </summary>
|
||||
public CustomBaseNumericTicksProvider() : this(2.0) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CustomBaseNumericTicksProvider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="customBase">The custom base, e.g. Math.PI</param>
|
||||
public CustomBaseNumericTicksProvider(double customBase) : this(customBase, new NumericTicksProvider()) { }
|
||||
|
||||
private CustomBaseNumericTicksProvider(double customBase, ITicksProvider<double> ticksProvider)
|
||||
{
|
||||
if (ticksProvider == null)
|
||||
throw new ArgumentNullException("ticksProvider");
|
||||
|
||||
CustomBase = customBase;
|
||||
|
||||
TicksProvider = ticksProvider;
|
||||
}
|
||||
|
||||
private void ticksProvider_Changed(object sender, EventArgs e)
|
||||
{
|
||||
Changed.Raise(this);
|
||||
}
|
||||
|
||||
private ITicksProvider<double> ticksProvider = null;
|
||||
public ITicksProvider<double> TicksProvider
|
||||
{
|
||||
get { return ticksProvider; }
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (ticksProvider != null)
|
||||
ticksProvider.Changed -= ticksProvider_Changed;
|
||||
ticksProvider = value;
|
||||
ticksProvider.Changed += ticksProvider_Changed;
|
||||
|
||||
if (minorTicksProvider != null)
|
||||
minorTicksProvider.Changed -= minorTicksProvider_Changed;
|
||||
minorTicksProvider = new MinorProviderWrapper(this);
|
||||
minorTicksProvider.Changed += minorTicksProvider_Changed;
|
||||
|
||||
Changed.Raise(this);
|
||||
}
|
||||
}
|
||||
|
||||
void minorTicksProvider_Changed(object sender, EventArgs e)
|
||||
{
|
||||
Changed.Raise(this);
|
||||
}
|
||||
|
||||
private Range<double> TransformRange(Range<double> range)
|
||||
{
|
||||
double min = range.Min / customBase;
|
||||
double max = range.Max / customBase;
|
||||
|
||||
return new Range<double>(min, max);
|
||||
}
|
||||
|
||||
#region ITicksProvider<double> Members
|
||||
|
||||
private double[] tickMarks;
|
||||
public ITicksInfo<double> GetTicks(Range<double> range, int ticksCount)
|
||||
{
|
||||
var ticks = ticksProvider.GetTicks(TransformRange(range), ticksCount);
|
||||
|
||||
TransformTicks(ticks);
|
||||
|
||||
tickMarks = ticks.Ticks;
|
||||
|
||||
return ticks;
|
||||
}
|
||||
|
||||
private void TransformTicks(ITicksInfo<double> ticks)
|
||||
{
|
||||
for (int i = 0; i < ticks.Ticks.Length; i++)
|
||||
{
|
||||
ticks.Ticks[i] *= customBase;
|
||||
}
|
||||
}
|
||||
|
||||
public int DecreaseTickCount(int ticksCount)
|
||||
{
|
||||
return ticksProvider.DecreaseTickCount(ticksCount);
|
||||
}
|
||||
|
||||
public int IncreaseTickCount(int ticksCount)
|
||||
{
|
||||
return ticksProvider.IncreaseTickCount(ticksCount);
|
||||
}
|
||||
|
||||
private ITicksProvider<double> minorTicksProvider;
|
||||
public ITicksProvider<double> MinorProvider
|
||||
{
|
||||
get { return minorTicksProvider; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the major provider, used to generate major ticks - for example, years for common ticks as months.
|
||||
/// </summary>
|
||||
/// <value>The major provider.</value>
|
||||
public ITicksProvider<double> MajorProvider
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public event EventHandler Changed;
|
||||
|
||||
#endregion
|
||||
|
||||
private sealed class MinorProviderWrapper : ITicksProvider<double>
|
||||
{
|
||||
private readonly CustomBaseNumericTicksProvider owner;
|
||||
|
||||
public MinorProviderWrapper(CustomBaseNumericTicksProvider owner)
|
||||
{
|
||||
this.owner = owner;
|
||||
|
||||
MinorTicksProvider.Changed += MinorTicksProvider_Changed;
|
||||
}
|
||||
|
||||
private void MinorTicksProvider_Changed(object sender, EventArgs e)
|
||||
{
|
||||
Changed.Raise(this);
|
||||
}
|
||||
|
||||
private ITicksProvider<double> MinorTicksProvider
|
||||
{
|
||||
get { return owner.ticksProvider.MinorProvider; }
|
||||
}
|
||||
|
||||
#region ITicksProvider<double> Members
|
||||
|
||||
public ITicksInfo<double> GetTicks(Range<double> range, int ticksCount)
|
||||
{
|
||||
var minorProvider = MinorTicksProvider;
|
||||
var ticks = minorProvider.GetTicks(range, ticksCount);
|
||||
|
||||
return ticks;
|
||||
}
|
||||
|
||||
public int DecreaseTickCount(int ticksCount)
|
||||
{
|
||||
return MinorTicksProvider.DecreaseTickCount(ticksCount);
|
||||
}
|
||||
|
||||
public int IncreaseTickCount(int ticksCount)
|
||||
{
|
||||
return MinorTicksProvider.IncreaseTickCount(ticksCount);
|
||||
}
|
||||
|
||||
public ITicksProvider<double> MinorProvider
|
||||
{
|
||||
get { return MinorTicksProvider.MinorProvider; }
|
||||
}
|
||||
|
||||
public ITicksProvider<double> MajorProvider
|
||||
{
|
||||
get { return owner; }
|
||||
}
|
||||
|
||||
public event EventHandler Changed;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
92
Charts/Axes/Numeric/ExponentialLabelProvider.cs
Normal file
92
Charts/Axes/Numeric/ExponentialLabelProvider.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Documents;
|
||||
using System.Globalization;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Research.DynamicDataDisplay.Charts.Axes;
|
||||
|
||||
namespace Microsoft.Research.DynamicDataDisplay.Charts
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an axis label provider for double ticks, generating labels with numbers in exponential form when it is appropriate.
|
||||
/// </summary>
|
||||
public sealed class ExponentialLabelProvider : NumericLabelProviderBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExponentialLabelProvider"/> class.
|
||||
/// </summary>
|
||||
public ExponentialLabelProvider() { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates labels by given ticks info.
|
||||
/// Is not intended to be called from your code.
|
||||
/// </summary>
|
||||
/// <param name="ticksInfo">The ticks info.</param>
|
||||
/// <returns>
|
||||
/// Array of <see cref="UIElement"/>s, which are axis labels for specified axis ticks.
|
||||
/// </returns>
|
||||
public override UIElement[] CreateLabels(ITicksInfo<double> ticksInfo)
|
||||
{
|
||||
var ticks = ticksInfo.Ticks;
|
||||
|
||||
Init(ticks);
|
||||
|
||||
UIElement[] res = new UIElement[ticks.Length];
|
||||
|
||||
LabelTickInfo<double> tickInfo = new LabelTickInfo<double> { Info = ticksInfo.Info };
|
||||
|
||||
for (int i = 0; i < res.Length; i++)
|
||||
{
|
||||
var tick = ticks[i];
|
||||
tickInfo.Tick = tick;
|
||||
tickInfo.Index = i;
|
||||
|
||||
string labelText = GetString(tickInfo);
|
||||
|
||||
TextBlock label;
|
||||
if (labelText.Contains('E'))
|
||||
{
|
||||
string[] substrs = labelText.Split('E');
|
||||
string mantissa = substrs[0];
|
||||
string exponenta = substrs[1];
|
||||
exponenta = exponenta.TrimStart('+');
|
||||
Span span = new Span();
|
||||
span.Inlines.Add(String.Format(CultureInfo.CurrentCulture, "{0}·10", mantissa));
|
||||
Span exponentaSpan = new Span(new Run(exponenta));
|
||||
exponentaSpan.BaselineAlignment = BaselineAlignment.Superscript;
|
||||
exponentaSpan.FontSize = 8;
|
||||
span.Inlines.Add(exponentaSpan);
|
||||
|
||||
label = new TextBlock(span);
|
||||
LabelProviderProperties.SetExponentialIsCommonLabel(label, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
label = (TextBlock)GetResourceFromPool();
|
||||
if (label == null)
|
||||
{
|
||||
label = new TextBlock();
|
||||
}
|
||||
|
||||
label.Text = labelText;
|
||||
}
|
||||
res[i] = label;
|
||||
label.ToolTip = tick.ToString(CultureInfo.CurrentCulture);
|
||||
|
||||
ApplyCustomView(tickInfo, label);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
protected override bool ReleaseCore(UIElement label)
|
||||
{
|
||||
bool isNotExponential = LabelProviderProperties.GetExponentialIsCommonLabel(label);
|
||||
return isNotExponential && CustomView == null;
|
||||
}
|
||||
}
|
||||
}
|
||||
34
Charts/Axes/Numeric/HorizontalAxis.cs
Normal file
34
Charts/Axes/Numeric/HorizontalAxis.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Research.DynamicDataDisplay.Charts
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a horizontal axis with values of <see cref="Double"/> type.
|
||||
/// Can be placed only from bottom or top side of plotter.
|
||||
/// By default is placed from the bottom side.
|
||||
/// </summary>
|
||||
public class HorizontalAxis : NumericAxis
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HorizontalAxis"/> class, placed on bottom of <see cref="ChartPlotter"/>.
|
||||
/// </summary>
|
||||
public HorizontalAxis()
|
||||
{
|
||||
Placement = AxisPlacement.Bottom;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates the placement - e.g., vertical axis should not be placed from top or bottom, etc.
|
||||
/// If proposed placement is wrong, throws an ArgumentException.
|
||||
/// </summary>
|
||||
/// <param name="newPlacement">The new placement.</param>
|
||||
protected override void ValidatePlacement(AxisPlacement newPlacement)
|
||||
{
|
||||
if (newPlacement == AxisPlacement.Left || newPlacement == AxisPlacement.Right)
|
||||
throw new ArgumentException(Strings.Exceptions.HorizontalAxisCannotBeVertical);
|
||||
}
|
||||
}
|
||||
}
|
||||
131
Charts/Axes/Numeric/LogarithmNumericTicksProvider.cs
Normal file
131
Charts/Axes/Numeric/LogarithmNumericTicksProvider.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows.Markup;
|
||||
using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
|
||||
|
||||
namespace Microsoft.Research.DynamicDataDisplay.Charts.Axes.Numeric
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a ticks provider for logarithmically transfomed axis - returns ticks which are a power of specified logarithm base.
|
||||
/// </summary>
|
||||
public class LogarithmNumericTicksProvider : ITicksProvider<double>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LogarithmNumericTicksProvider"/> class.
|
||||
/// </summary>
|
||||
public LogarithmNumericTicksProvider()
|
||||
{
|
||||
minorProvider = new MinorNumericTicksProvider(this);
|
||||
minorProvider.Changed += ticksProvider_Changed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LogarithmNumericTicksProvider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="logarithmBase">The logarithm base.</param>
|
||||
public LogarithmNumericTicksProvider(double logarithmBase)
|
||||
: this()
|
||||
{
|
||||
LogarithmBase = logarithmBase;
|
||||
}
|
||||
|
||||
private void ticksProvider_Changed(object sender, EventArgs e)
|
||||
{
|
||||
Changed.Raise(this);
|
||||
}
|
||||
|
||||
private double logarithmBase = 10;
|
||||
public double LogarithmBase
|
||||
{
|
||||
get { return logarithmBase; }
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
throw new ArgumentOutOfRangeException(Strings.Exceptions.LogarithmBaseShouldBePositive);
|
||||
|
||||
logarithmBase = value;
|
||||
}
|
||||
}
|
||||
|
||||
private double LogByBase(double d)
|
||||
{
|
||||
return Math.Log10(d) / Math.Log10(logarithmBase);
|
||||
}
|
||||
|
||||
#region ITicksProvider<double> Members
|
||||
|
||||
private double[] ticks;
|
||||
public ITicksInfo<double> GetTicks(Range<double> range, int ticksCount)
|
||||
{
|
||||
double min = LogByBase(range.Min);
|
||||
double max = LogByBase(range.Max);
|
||||
|
||||
double minDown = Math.Floor(min);
|
||||
double maxUp = Math.Ceiling(max);
|
||||
|
||||
double logLength = LogByBase(range.GetLength());
|
||||
|
||||
ticks = CreateTicks(range);
|
||||
|
||||
int log = RoundingHelper.GetDifferenceLog(range.Min, range.Max);
|
||||
TicksInfo<double> result = new TicksInfo<double> { Ticks = ticks, TickSizes = ArrayExtensions.CreateArray(ticks.Length, 1.0), Info = log };
|
||||
return result;
|
||||
}
|
||||
|
||||
private double[] CreateTicks(Range<double> range)
|
||||
{
|
||||
double min = LogByBase(range.Min);
|
||||
double max = LogByBase(range.Max);
|
||||
|
||||
double minDown = Math.Floor(min);
|
||||
double maxUp = Math.Ceiling(max);
|
||||
|
||||
int intStart = (int)Math.Floor(minDown);
|
||||
int count = (int)(maxUp - minDown + 1);
|
||||
|
||||
var ticks = new double[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ticks[i] = intStart + i;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ticks.Length; i++)
|
||||
{
|
||||
ticks[i] = Math.Pow(logarithmBase, ticks[i]);
|
||||
}
|
||||
|
||||
return ticks;
|
||||
}
|
||||
|
||||
public int DecreaseTickCount(int ticksCount)
|
||||
{
|
||||
return ticksCount;
|
||||
}
|
||||
|
||||
public int IncreaseTickCount(int ticksCount)
|
||||
{
|
||||
return ticksCount;
|
||||
}
|
||||
|
||||
private MinorNumericTicksProvider minorProvider;
|
||||
public ITicksProvider<double> MinorProvider
|
||||
{
|
||||
get
|
||||
{
|
||||
minorProvider.SetRanges(ArrayExtensions.GetPairs(ticks));
|
||||
return minorProvider;
|
||||
}
|
||||
}
|
||||
|
||||
public ITicksProvider<double> MajorProvider
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public event EventHandler Changed;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
89
Charts/Axes/Numeric/MinorNumericTicksProvider.cs
Normal file
89
Charts/Axes/Numeric/MinorNumericTicksProvider.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Microsoft.Research.DynamicDataDisplay.Charts
|
||||
{
|
||||
public sealed class MinorNumericTicksProvider : ITicksProvider<double>
|
||||
{
|
||||
private readonly ITicksProvider<double> parent;
|
||||
private Range<double>[] ranges;
|
||||
internal void SetRanges(IEnumerable<Range<double>> ranges)
|
||||
{
|
||||
this.ranges = ranges.ToArray();
|
||||
}
|
||||
|
||||
private double[] coeffs;
|
||||
public double[] Coeffs
|
||||
{
|
||||
get { return coeffs; }
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
coeffs = value;
|
||||
Changed.Raise(this);
|
||||
}
|
||||
}
|
||||
|
||||
internal MinorNumericTicksProvider(ITicksProvider<double> parent)
|
||||
{
|
||||
this.parent = parent;
|
||||
Coeffs = new double[] { 0.3, 0.3, 0.3, 0.3, 0.6, 0.3, 0.3, 0.3, 0.3 };
|
||||
}
|
||||
|
||||
#region ITicksProvider<double> Members
|
||||
|
||||
public event EventHandler Changed;
|
||||
|
||||
public ITicksInfo<double> GetTicks(Range<double> range, int ticksCount)
|
||||
{
|
||||
if (Coeffs.Length == 0)
|
||||
return new TicksInfo<double>();
|
||||
|
||||
var minorTicks = ranges.Select(r => CreateTicks(r)).SelectMany(m => m);
|
||||
var res = new TicksInfo<double>();
|
||||
res.TickSizes = minorTicks.Select(m => m.Value).ToArray();
|
||||
res.Ticks = minorTicks.Select(m => m.Tick).ToArray();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public MinorTickInfo<double>[] CreateTicks(Range<double> range)
|
||||
{
|
||||
double step = (range.Max - range.Min) / (Coeffs.Length + 1);
|
||||
|
||||
MinorTickInfo<double>[] res = new MinorTickInfo<double>[Coeffs.Length];
|
||||
for (int i = 0; i < Coeffs.Length; i++)
|
||||
{
|
||||
res[i] = new MinorTickInfo<double>(Coeffs[i], range.Min + step * (i + 1));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public int DecreaseTickCount(int ticksCount)
|
||||
{
|
||||
return ticksCount;
|
||||
}
|
||||
|
||||
public int IncreaseTickCount(int ticksCount)
|
||||
{
|
||||
return ticksCount;
|
||||
}
|
||||
|
||||
public ITicksProvider<double> MinorProvider
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public ITicksProvider<double> MajorProvider
|
||||
{
|
||||
get { return parent; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
41
Charts/Axes/Numeric/NumericAxis.cs
Normal file
41
Charts/Axes/Numeric/NumericAxis.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows.Media;
|
||||
using Microsoft.Research.DynamicDataDisplay.Charts.Axes.Numeric;
|
||||
|
||||
namespace Microsoft.Research.DynamicDataDisplay.Charts
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a numeric axis with values of <see cref="System.Double"/> type.
|
||||
/// </summary>
|
||||
public class NumericAxis : AxisBase<double>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NumericAxis"/> class.
|
||||
/// </summary>
|
||||
public NumericAxis()
|
||||
: base(new NumericAxisControl(),
|
||||
d => d,
|
||||
d => d)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets conversions of axis - functions used to convert values of axis type to and from double values of viewport.
|
||||
/// Sets both ConvertToDouble and ConvertFromDouble properties.
|
||||
/// </summary>
|
||||
/// <param name="min">The minimal viewport value.</param>
|
||||
/// <param name="minValue">The value of axis type, corresponding to minimal viewport value.</param>
|
||||
/// <param name="max">The maximal viewport value.</param>
|
||||
/// <param name="maxValue">The value of axis type, corresponding to maximal viewport value.</param>
|
||||
public override void SetConversion(double min, double minValue, double max, double maxValue)
|
||||
{
|
||||
var conversion = new NumericConversion(min, minValue, max, maxValue);
|
||||
|
||||
this.ConvertFromDouble = conversion.FromDouble;
|
||||
this.ConvertToDouble = conversion.ToDouble;
|
||||
}
|
||||
}
|
||||
}
|
||||
18
Charts/Axes/Numeric/NumericAxisControl.cs
Normal file
18
Charts/Axes/Numeric/NumericAxisControl.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Research.DynamicDataDisplay.Charts
|
||||
{
|
||||
public class NumericAxisControl : AxisControl<double>
|
||||
{
|
||||
public NumericAxisControl()
|
||||
{
|
||||
LabelProvider = new ExponentialLabelProvider();
|
||||
TicksProvider = new NumericTicksProvider();
|
||||
ConvertToDouble = d => d;
|
||||
Range = new Range<double>(0, 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
38
Charts/Axes/Numeric/NumericConversion.cs
Normal file
38
Charts/Axes/Numeric/NumericConversion.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Research.DynamicDataDisplay.Charts.Axes.Numeric
|
||||
{
|
||||
internal sealed class NumericConversion
|
||||
{
|
||||
private readonly double min;
|
||||
private readonly double length;
|
||||
private readonly double minValue;
|
||||
private readonly double valueLength;
|
||||
|
||||
public NumericConversion(double min, double minValue, double max, double maxValue)
|
||||
{
|
||||
this.min = min;
|
||||
this.length = max - min;
|
||||
|
||||
this.minValue = minValue;
|
||||
this.valueLength = maxValue - minValue;
|
||||
}
|
||||
|
||||
public double FromDouble(double value)
|
||||
{
|
||||
double ratio = (value - min) / length;
|
||||
|
||||
return minValue + ratio * valueLength;
|
||||
}
|
||||
|
||||
public double ToDouble(double value)
|
||||
{
|
||||
double ratio = (value - minValue) / valueLength;
|
||||
|
||||
return min + length * ratio;
|
||||
}
|
||||
}
|
||||
}
|
||||
54
Charts/Axes/Numeric/NumericLabelProviderBase.cs
Normal file
54
Charts/Axes/Numeric/NumericLabelProviderBase.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using Microsoft.Research.DynamicDataDisplay.Charts.Axes;
|
||||
|
||||
namespace Microsoft.Research.DynamicDataDisplay.Charts
|
||||
{
|
||||
public abstract class NumericLabelProviderBase : LabelProviderBase<double>
|
||||
{
|
||||
bool shouldRound = true;
|
||||
private int rounding;
|
||||
protected void Init(double[] ticks)
|
||||
{
|
||||
if (ticks.Length == 0)
|
||||
return;
|
||||
|
||||
double start = ticks[0];
|
||||
double finish = ticks[ticks.Length - 1];
|
||||
|
||||
if (start == finish)
|
||||
{
|
||||
shouldRound = false;
|
||||
return;
|
||||
}
|
||||
|
||||
double delta = finish - start;
|
||||
|
||||
rounding = (int)Math.Round(Math.Log10(delta));
|
||||
|
||||
double newStart = RoundingHelper.Round(start, rounding);
|
||||
double newFinish = RoundingHelper.Round(finish, rounding);
|
||||
if (newStart == newFinish)
|
||||
rounding--;
|
||||
}
|
||||
|
||||
protected override string GetStringCore(LabelTickInfo<double> tickInfo)
|
||||
{
|
||||
string res;
|
||||
if (!shouldRound)
|
||||
{
|
||||
res = tickInfo.Tick.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
int round = Math.Min(15, Math.Max(-15, rounding - 3)); // was rounding - 2
|
||||
res = RoundingHelper.Round(tickInfo.Tick, round).ToString();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
167
Charts/Axes/Numeric/NumericTicksProvider.cs
Normal file
167
Charts/Axes/Numeric/NumericTicksProvider.cs
Normal file
@@ -0,0 +1,167 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Microsoft.Research.DynamicDataDisplay.Charts
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a ticks provider for <see cref="System.Double"/> values.
|
||||
/// </summary>
|
||||
public sealed class NumericTicksProvider : ITicksProvider<double>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NumericTicksProvider"/> class.
|
||||
/// </summary>
|
||||
public NumericTicksProvider()
|
||||
{
|
||||
minorProvider = new MinorNumericTicksProvider(this);
|
||||
minorProvider.Changed += minorProvider_Changed;
|
||||
minorProvider.Coeffs = new double[] { 0.3, 0.3, 0.3, 0.3, 0.6, 0.3, 0.3, 0.3, 0.3 };
|
||||
}
|
||||
|
||||
private void minorProvider_Changed(object sender, EventArgs e)
|
||||
{
|
||||
Changed.Raise(this);
|
||||
}
|
||||
|
||||
public event EventHandler Changed;
|
||||
private void RaiseChangedEvent()
|
||||
{
|
||||
Changed.Raise(this);
|
||||
}
|
||||
|
||||
private double minStep = 0.0;
|
||||
/// <summary>
|
||||
/// Gets or sets the minimal step between ticks.
|
||||
/// </summary>
|
||||
/// <value>The min step.</value>
|
||||
public double MinStep
|
||||
{
|
||||
get { return minStep; }
|
||||
set
|
||||
{
|
||||
Verify.IsTrue(value >= 0.0, "value");
|
||||
if (minStep != value)
|
||||
{
|
||||
minStep = value;
|
||||
RaiseChangedEvent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private double[] ticks;
|
||||
public ITicksInfo<double> GetTicks(Range<double> range, int ticksCount)
|
||||
{
|
||||
double start = range.Min;
|
||||
double finish = range.Max;
|
||||
|
||||
double delta = finish - start;
|
||||
|
||||
int log = (int)Math.Round(Math.Log10(delta));
|
||||
|
||||
double newStart = RoundingHelper.Round(start, log);
|
||||
double newFinish = RoundingHelper.Round(finish, log);
|
||||
if (newStart == newFinish)
|
||||
{
|
||||
log--;
|
||||
newStart = RoundingHelper.Round(start, log);
|
||||
newFinish = RoundingHelper.Round(finish, log);
|
||||
}
|
||||
|
||||
// calculating step between ticks
|
||||
double unroundedStep = (newFinish - newStart) / ticksCount;
|
||||
int stepLog = log;
|
||||
// trying to round step
|
||||
double step = RoundingHelper.Round(unroundedStep, stepLog);
|
||||
if (step == 0)
|
||||
{
|
||||
stepLog--;
|
||||
step = RoundingHelper.Round(unroundedStep, stepLog);
|
||||
if (step == 0)
|
||||
{
|
||||
// step will not be rounded if attempts to be rounded to zero.
|
||||
step = unroundedStep;
|
||||
}
|
||||
}
|
||||
|
||||
if (step < minStep)
|
||||
step = minStep;
|
||||
|
||||
if (step != 0.0)
|
||||
{
|
||||
ticks = CreateTicks(start, finish, step);
|
||||
}
|
||||
else
|
||||
{
|
||||
ticks = new double[] { };
|
||||
}
|
||||
|
||||
TicksInfo<double> res = new TicksInfo<double> { Info = log, Ticks = ticks };
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static double[] CreateTicks(double start, double finish, double step)
|
||||
{
|
||||
DebugVerify.Is(step != 0.0);
|
||||
|
||||
double x = step * Math.Floor(start / step);
|
||||
|
||||
if (x == x + step)
|
||||
{
|
||||
return new double[0];
|
||||
}
|
||||
|
||||
List<double> res = new List<double>();
|
||||
|
||||
double increasedFinish = finish + step * 1.05;
|
||||
while (x <= increasedFinish)
|
||||
{
|
||||
res.Add(x);
|
||||
DebugVerify.Is(res.Count < 2000);
|
||||
x += step;
|
||||
}
|
||||
return res.ToArray();
|
||||
}
|
||||
|
||||
private static int[] tickCounts = new int[] { 20, 10, 5, 4, 2, 1 };
|
||||
|
||||
public const int DefaultPreferredTicksCount = 10;
|
||||
|
||||
public int DecreaseTickCount(int ticksCount)
|
||||
{
|
||||
return tickCounts.FirstOrDefault(tick => tick < ticksCount);
|
||||
}
|
||||
|
||||
public int IncreaseTickCount(int ticksCount)
|
||||
{
|
||||
int newTickCount = tickCounts.Reverse().FirstOrDefault(tick => tick > ticksCount);
|
||||
if (newTickCount == 0)
|
||||
newTickCount = tickCounts[0];
|
||||
|
||||
return newTickCount;
|
||||
}
|
||||
|
||||
private readonly MinorNumericTicksProvider minorProvider;
|
||||
public ITicksProvider<double> MinorProvider
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ticks != null)
|
||||
{
|
||||
minorProvider.SetRanges(ticks.GetPairs());
|
||||
}
|
||||
|
||||
return minorProvider;
|
||||
}
|
||||
}
|
||||
|
||||
public ITicksProvider<double> MajorProvider
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
}
|
||||
}
|
||||
53
Charts/Axes/Numeric/ToStringLabelProvider.cs
Normal file
53
Charts/Axes/Numeric/ToStringLabelProvider.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using Microsoft.Research.DynamicDataDisplay.Charts.Axes;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Microsoft.Research.DynamicDataDisplay.Charts
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a simple label provider for double ticks, which simply returns result of .ToString() method, called for rounded ticks.
|
||||
/// </summary>
|
||||
public class ToStringLabelProvider : NumericLabelProviderBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ToStringLabelProvider"/> class.
|
||||
/// </summary>
|
||||
public ToStringLabelProvider() { }
|
||||
|
||||
public override UIElement[] CreateLabels(ITicksInfo<double> ticksInfo)
|
||||
{
|
||||
var ticks = ticksInfo.Ticks;
|
||||
|
||||
Init(ticks);
|
||||
|
||||
UIElement[] res = new UIElement[ticks.Length];
|
||||
LabelTickInfo<double> tickInfo = new LabelTickInfo<double> { Info = ticksInfo.Info };
|
||||
for (int i = 0; i < res.Length; i++)
|
||||
{
|
||||
tickInfo.Tick = ticks[i];
|
||||
tickInfo.Index = i;
|
||||
|
||||
string labelText = GetString(tickInfo);
|
||||
|
||||
TextBlock label = (TextBlock)GetResourceFromPool();
|
||||
if (label == null)
|
||||
{
|
||||
label = new TextBlock();
|
||||
}
|
||||
|
||||
label.Text = labelText;
|
||||
label.ToolTip = ticks[i].ToString();
|
||||
|
||||
res[i] = label;
|
||||
|
||||
ApplyCustomView(tickInfo, label);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Charts/Axes/Numeric/UnroundingLabelProvider.cs
Normal file
11
Charts/Axes/Numeric/UnroundingLabelProvider.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Research.DynamicDataDisplay.Charts.Axes.Numeric
|
||||
{
|
||||
public class UnroundingLabelProvider : LabelProvider<double>
|
||||
{
|
||||
}
|
||||
}
|
||||
35
Charts/Axes/Numeric/VerticalAxis.cs
Normal file
35
Charts/Axes/Numeric/VerticalAxis.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Microsoft.Research.DynamicDataDisplay.Charts
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a vertical axis with values of System.Double type.
|
||||
/// Can be placed only from left or right side of plotter.
|
||||
/// By default is placed from the left side.
|
||||
/// </summary>
|
||||
public class VerticalAxis : NumericAxis
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="VerticalAxis"/> class.
|
||||
/// </summary>
|
||||
public VerticalAxis()
|
||||
{
|
||||
Placement = AxisPlacement.Left;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates the placement - e.g., vertical axis should not be placed from top or bottom, etc.
|
||||
/// If proposed placement if wrong, throws an ArgumentException.
|
||||
/// </summary>
|
||||
/// <param name="newPlacement">The new placement.</param>
|
||||
protected override void ValidatePlacement(AxisPlacement newPlacement)
|
||||
{
|
||||
if (newPlacement == AxisPlacement.Bottom || newPlacement == AxisPlacement.Top)
|
||||
throw new ArgumentException(Strings.Exceptions.VerticalAxisCannotBeHorizontal);
|
||||
}
|
||||
}
|
||||
}
|
||||
21
Charts/Axes/Numeric/VerticalNumericAxis.cs
Normal file
21
Charts/Axes/Numeric/VerticalNumericAxis.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Research.DynamicDataDisplay.Charts.Axes
|
||||
{
|
||||
public class VerticalNumericAxis : NumericAxis
|
||||
{
|
||||
public VerticalNumericAxis()
|
||||
{
|
||||
Placement = AxisPlacement.Left;
|
||||
}
|
||||
|
||||
protected override void ValidatePlacement(AxisPlacement newPlacement)
|
||||
{
|
||||
if (newPlacement == AxisPlacement.Bottom || newPlacement == AxisPlacement.Top)
|
||||
throw new ArgumentException(Strings.Exceptions.VerticalAxisCannotBeHorizontal);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user