270 lines
6.2 KiB
C#
270 lines
6.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
|
|
|
|
namespace Microsoft.Research.DynamicDataDisplay.Charts
|
|
{
|
|
internal abstract class TimePeriodTicksProvider<T> : ITicksProvider<T>
|
|
{
|
|
public event EventHandler Changed;
|
|
protected void RaiseChanged()
|
|
{
|
|
if (Changed != null)
|
|
{
|
|
Changed(this, EventArgs.Empty);
|
|
}
|
|
}
|
|
|
|
protected abstract T RoundUp(T time, DifferenceIn diff);
|
|
protected abstract T RoundDown(T time, DifferenceIn diff);
|
|
|
|
private bool differenceInited = false;
|
|
private DifferenceIn difference;
|
|
protected DifferenceIn Difference
|
|
{
|
|
get
|
|
{
|
|
if (!differenceInited)
|
|
{
|
|
difference = GetDifferenceCore();
|
|
differenceInited = true;
|
|
}
|
|
return difference;
|
|
}
|
|
}
|
|
protected abstract DifferenceIn GetDifferenceCore();
|
|
|
|
private int[] tickCounts = null;
|
|
protected int[] TickCounts
|
|
{
|
|
get
|
|
{
|
|
if (tickCounts == null)
|
|
tickCounts = GetTickCountsCore();
|
|
return tickCounts;
|
|
}
|
|
}
|
|
protected abstract int[] GetTickCountsCore();
|
|
|
|
public int DecreaseTickCount(int ticksCount)
|
|
{
|
|
if (ticksCount > TickCounts[0]) return TickCounts[0];
|
|
|
|
for (int i = 0; i < TickCounts.Length; i++)
|
|
if (ticksCount > TickCounts[i])
|
|
return TickCounts[i];
|
|
|
|
return TickCounts.Last();
|
|
}
|
|
|
|
public int IncreaseTickCount(int ticksCount)
|
|
{
|
|
if (ticksCount >= TickCounts[0]) return TickCounts[0];
|
|
|
|
for (int i = TickCounts.Length - 1; i >= 0; i--)
|
|
if (ticksCount < TickCounts[i])
|
|
return TickCounts[i];
|
|
|
|
return TickCounts.Last();
|
|
}
|
|
|
|
protected abstract int GetSpecificValue(T start, T dt);
|
|
protected abstract T GetStart(T start, int value, int step);
|
|
protected abstract bool IsMinDate(T dt);
|
|
protected abstract T AddStep(T dt, int step);
|
|
|
|
public ITicksInfo<T> GetTicks(Range<T> range, int ticksCount)
|
|
{
|
|
T start = range.Min;
|
|
T end = range.Max;
|
|
DifferenceIn diff = Difference;
|
|
start = RoundDown(start, end);
|
|
end = RoundUp(start, end);
|
|
|
|
RoundingInfo bounds = RoundingHelper.CreateRoundedRange(
|
|
GetSpecificValue(start, start),
|
|
GetSpecificValue(start, end));
|
|
|
|
int delta = (int)(bounds.Max - bounds.Min);
|
|
if (delta == 0)
|
|
return new TicksInfo<T> { Ticks = new T[] { start } };
|
|
|
|
int step = delta / ticksCount;
|
|
|
|
if (step == 0) step = 1;
|
|
|
|
T tick = GetStart(start, (int)bounds.Min, step);
|
|
bool isMinDateTime = IsMinDate(tick) && step != 1;
|
|
if (isMinDateTime)
|
|
step--;
|
|
|
|
List<T> ticks = new List<T>();
|
|
T finishTick = AddStep(range.Max, step);
|
|
while (Continue(tick, finishTick))
|
|
{
|
|
ticks.Add(tick);
|
|
tick = AddStep(tick, step);
|
|
if (isMinDateTime)
|
|
{
|
|
isMinDateTime = false;
|
|
step++;
|
|
}
|
|
}
|
|
|
|
ticks = Trim(ticks, range);
|
|
|
|
TicksInfo<T> res = new TicksInfo<T> { Ticks = ticks.ToArray(), Info = diff };
|
|
return res;
|
|
}
|
|
|
|
protected abstract bool Continue(T current, T end);
|
|
|
|
protected abstract T RoundUp(T start, T end);
|
|
|
|
protected abstract T RoundDown(T start, T end);
|
|
|
|
protected abstract List<T> Trim(List<T> ticks, Range<T> range);
|
|
|
|
public ITicksProvider<T> MinorProvider
|
|
{
|
|
get { throw new NotSupportedException(); }
|
|
}
|
|
|
|
public ITicksProvider<T> MajorProvider
|
|
{
|
|
get { throw new NotSupportedException(); }
|
|
}
|
|
}
|
|
|
|
internal abstract class DatePeriodTicksProvider : TimePeriodTicksProvider<DateTime>
|
|
{
|
|
protected sealed override bool Continue(DateTime current, DateTime end)
|
|
{
|
|
return current < end;
|
|
}
|
|
|
|
protected sealed override List<DateTime> Trim(List<DateTime> ticks, Range<DateTime> range)
|
|
{
|
|
int startIndex = 0;
|
|
for (int i = 0; i < ticks.Count - 1; i++)
|
|
{
|
|
if (ticks[i] <= range.Min && range.Min <= ticks[i + 1])
|
|
{
|
|
startIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
int endIndex = ticks.Count - 1;
|
|
for (int i = ticks.Count - 1; i >= 1; i--)
|
|
{
|
|
if (ticks[i] >= range.Max && range.Max > ticks[i - 1])
|
|
{
|
|
endIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
List<DateTime> res = new List<DateTime>(endIndex - startIndex + 1);
|
|
for (int i = startIndex; i <= endIndex; i++)
|
|
{
|
|
res.Add(ticks[i]);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
protected sealed override DateTime RoundUp(DateTime start, DateTime end)
|
|
{
|
|
bool isPositive = (end - start).Ticks > 0;
|
|
return isPositive ? SafelyRoundUp(end) : RoundDown(end, Difference);
|
|
}
|
|
|
|
private DateTime SafelyRoundUp(DateTime dt)
|
|
{
|
|
if (AddStep(dt, 1) == DateTime.MaxValue)
|
|
return DateTime.MaxValue;
|
|
|
|
return RoundUp(dt, Difference);
|
|
}
|
|
|
|
protected sealed override DateTime RoundDown(DateTime start, DateTime end)
|
|
{
|
|
bool isPositive = (end - start).Ticks > 0;
|
|
return isPositive ? RoundDown(start, Difference) : SafelyRoundUp(start);
|
|
}
|
|
|
|
protected sealed override DateTime RoundDown(DateTime time, DifferenceIn diff)
|
|
{
|
|
DateTime res = time;
|
|
|
|
switch (diff)
|
|
{
|
|
case DifferenceIn.Year:
|
|
res = new DateTime(time.Year, 1, 1);
|
|
break;
|
|
case DifferenceIn.Month:
|
|
res = new DateTime(time.Year, time.Month, 1);
|
|
break;
|
|
case DifferenceIn.Day:
|
|
res = time.Date;
|
|
break;
|
|
case DifferenceIn.Hour:
|
|
res = time.Date.AddHours(time.Hour);
|
|
break;
|
|
case DifferenceIn.Minute:
|
|
res = time.Date.AddHours(time.Hour).AddMinutes(time.Minute);
|
|
break;
|
|
case DifferenceIn.Second:
|
|
res = time.Date.AddHours(time.Hour).AddMinutes(time.Minute).AddSeconds(time.Second);
|
|
break;
|
|
case DifferenceIn.Millisecond:
|
|
res = time.Date.AddHours(time.Hour).AddMinutes(time.Minute).AddSeconds(time.Second).AddMilliseconds(time.Millisecond);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
DebugVerify.Is(res <= time);
|
|
|
|
return res;
|
|
}
|
|
|
|
protected override DateTime RoundUp(DateTime dateTime, DifferenceIn diff)
|
|
{
|
|
DateTime res = RoundDown(dateTime, diff);
|
|
|
|
switch (diff)
|
|
{
|
|
case DifferenceIn.Year:
|
|
res = res.AddYears(1);
|
|
break;
|
|
case DifferenceIn.Month:
|
|
res = res.AddMonths(1);
|
|
break;
|
|
case DifferenceIn.Day:
|
|
res = res.AddDays(1);
|
|
break;
|
|
case DifferenceIn.Hour:
|
|
res = res.AddHours(1);
|
|
break;
|
|
case DifferenceIn.Minute:
|
|
res = res.AddMinutes(1);
|
|
break;
|
|
case DifferenceIn.Second:
|
|
res = res.AddSeconds(1);
|
|
break;
|
|
case DifferenceIn.Millisecond:
|
|
res = res.AddMilliseconds(1);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
}
|
|
}
|