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
}
}