namespace JD.Sequencers.Times { /// /// Helper class to help calculate ideal Minor and Major intervals (number labels and ticks) /// inside a range on the screen. /// public class Intervals { #region Variables private static readonly int[] m_idealMultiples = new [] { 1, 2, 5 }; private int m_pixelWidth; private double m_unitWidth; private int m_minorThreshold = 5; private int m_majorThreshold = 30; private double m_minor; private double m_major; private bool m_dirty = true; #endregion #region Constructor /// /// Default Constructor. /// public Intervals() { } /// /// Constructor. /// /// The width in pixels in which to fit the UnitWidth range. /// The width in units that are to be fitted within PixelWidth. public Intervals(int pixelWidth, double unitWidth) { PixelWidth = pixelWidth; UnitWidth = unitWidth; } /// /// Constructor. /// /// The width in pixels in which to fit the UnitWidth range. /// The width in units that are to be fitted within PixelWidth. /// The minimum number of pixels between minor ticks. /// The minimum number of pixels reserved around a major interval. public Intervals(int pixelWidth, double unitWidth, int minorThreshold, int majorThreshold) : this(pixelWidth, unitWidth) { MinorThreshold = minorThreshold; MajorThreshold = majorThreshold; } #endregion #region Properties /// /// Gets or sets the width in pixels in which to fit the UnitWidth range. /// public int PixelWidth { get { return m_pixelWidth; } set { if (m_pixelWidth != value) { m_pixelWidth = System.Math.Max(0, value); m_dirty = true; } } } /// /// Gets or sets the width in units that are to be fitted within PixelWidth. /// public double UnitWidth { get { return m_unitWidth; } set { if (m_unitWidth != value) { m_unitWidth = System.Math.Max(0, value); m_dirty = true; } } } /// /// Gets or sets the minimum number of pixels between minor ticks. The default is 5 px. /// public int MinorThreshold { get { return m_minorThreshold; } set { if (m_minorThreshold != value) { m_minorThreshold = value; m_dirty = true; } } } /// /// Gets or sets the minimum number of pixels reserved around a major interval. The default is 30 px. /// public int MajorThreshold { get { return m_majorThreshold; } set { if (m_majorThreshold != value) { m_majorThreshold = value; m_dirty = true; } } } /// /// Gets the current Minor interval (in units). /// public double Minor { get { CalculateIntervals(); return m_minor; } } /// /// Gets the current Major interval (in units). /// public double Major { get { CalculateIntervals(); return m_major; } } #endregion #region Methods /// /// Gets the closest ideal interval according to the current scale. /// /// /// /// The minimum amount of pixels between intervals. /// public virtual double GetIdealInterval(double unitWidth, double pixelWidth, double pixelThreshold) { if (unitWidth == 0 || pixelWidth == 0 || pixelThreshold == 0) { return 0; } double unitThreshold = (pixelThreshold * unitWidth) / pixelWidth; int exponent = (int)System.Math.Floor(System.Math.Log10(unitThreshold)); do { double v = System.Math.Pow(10, exponent); foreach (int idealMultiple in m_idealMultiples) { double v2 = v * idealMultiple; double pixelV = (v2 * pixelWidth) / unitWidth; if (pixelV >= pixelThreshold) { return v2; } } exponent++; } while (true); } private void CalculateIntervals() { if (!m_dirty) { return; } m_dirty = false; System.Diagnostics.Debug.Assert(PixelWidth >= 0 && UnitWidth >= 0); if (PixelWidth == 0 || UnitWidth == 0) { m_minor = 0; m_major = 0; } else { m_minor = GetIdealInterval(UnitWidth, PixelWidth, MinorThreshold); m_major = GetIdealInterval(UnitWidth, PixelWidth, MajorThreshold); } } /// /// Gets the next ideal interval. /// /// /// The maximum unit space between each interval. /// public static double GetNextInterval(double unit, double spacing) { int times = (int)System.Math.Floor(unit / spacing); double rest = unit - times * spacing; if (rest == 0) { return unit; // The unit is dead on an interval. } double result = unit + spacing - rest; return result; } #endregion } }