mirror of
https://gitee.com/akwkevin/aistudio.-wpf.-diagram
synced 2026-03-03 00:00:57 +08:00
354 lines
12 KiB
C#
354 lines
12 KiB
C#
//The MIT License(MIT)
|
|
|
|
//Copyright(c) 2016 Alberto Rodriguez & LiveCharts Contributors
|
|
|
|
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
//of this software and associated documentation files (the "Software"), to deal
|
|
//in the Software without restriction, including without limitation the rights
|
|
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
//copies of the Software, and to permit persons to whom the Software is
|
|
//furnished to do so, subject to the following conditions:
|
|
|
|
//The above copyright notice and this permission notice shall be included in all
|
|
//copies or substantial portions of the Software.
|
|
|
|
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
//SOFTWARE.
|
|
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using LiveCharts.Charts;
|
|
using LiveCharts.Configurations;
|
|
using LiveCharts.Definitions.Series;
|
|
using LiveCharts.Dtos;
|
|
using LiveCharts.Helpers;
|
|
#if NET45
|
|
using System.Reflection;
|
|
#endif
|
|
|
|
namespace LiveCharts
|
|
{
|
|
/// <summary>
|
|
/// Creates a collection of chart values
|
|
/// </summary>
|
|
/// <typeparam name="T">Type to plot, notice you could need to configure the type.</typeparam>
|
|
public class ChartValues<T> : NoisyCollection<T>, IChartValues
|
|
{
|
|
#region Constructors
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of chart values
|
|
/// </summary>
|
|
public ChartValues()
|
|
{
|
|
Trackers = new Dictionary<ISeriesView, PointTracker>();
|
|
NoisyCollectionChanged += OnChanged;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of chart values, with a given collection
|
|
/// </summary>
|
|
public ChartValues(IEnumerable<T> collection) : base(collection)
|
|
{
|
|
Trackers = new Dictionary<ISeriesView, PointTracker>();
|
|
NoisyCollectionChanged += OnChanged;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
private IPointEvaluator<T> DefaultConfiguration { get; set; }
|
|
private Dictionary<ISeriesView, PointTracker> Trackers { get; set; }
|
|
#endregion
|
|
|
|
#region Public Methods
|
|
|
|
/// <summary>
|
|
/// Evaluates the limits in the chart values
|
|
/// </summary>
|
|
public void Initialize(ISeriesView seriesView)
|
|
{
|
|
var config = GetConfig(seriesView);
|
|
if (config == null) return;
|
|
|
|
var xMin = double.PositiveInfinity;
|
|
var xMax = double.NegativeInfinity;
|
|
var yMin = double.PositiveInfinity;
|
|
var yMax = double.NegativeInfinity;
|
|
var wMin = double.PositiveInfinity;
|
|
var wMax = double.NegativeInfinity;
|
|
|
|
var tracker = GetTracker(seriesView);
|
|
|
|
var cp = new ChartPoint();
|
|
|
|
var ax = seriesView.Model.Chart.AxisX[seriesView.ScalesXAt];
|
|
var ay = seriesView.Model.Chart.AxisY[seriesView.ScalesYAt];
|
|
double fx = double.IsNaN(ax.MinValue) ? double.NegativeInfinity : ax.MinValue,
|
|
tx = double.IsNaN(ax.MaxValue) ? double.PositiveInfinity : ax.MaxValue,
|
|
fy = double.IsNaN(ay.MinValue) ? double.NegativeInfinity : ay.MinValue,
|
|
ty = double.IsNaN(ay.MaxValue) ? double.PositiveInfinity : ay.MaxValue;
|
|
|
|
var isHorizontal = seriesView.Model.SeriesOrientation == SeriesOrientation.Horizontal;
|
|
|
|
var index = 0;
|
|
foreach(var item in this)
|
|
{
|
|
config.Evaluate(index, item, cp);
|
|
index++;
|
|
|
|
if (isHorizontal)
|
|
{
|
|
if (cp.X < fx || cp.X > tx) continue;
|
|
}
|
|
else
|
|
{
|
|
if (cp.Y < fy || cp.Y > ty) continue;
|
|
}
|
|
|
|
if (seriesView is IFinancialSeriesView)
|
|
{
|
|
if (cp.X < xMin) xMin = cp.X;
|
|
if (cp.X > xMax) xMax = cp.X;
|
|
|
|
if (cp.Low < yMin) yMin = cp.Low;
|
|
if (cp.High > yMax) yMax = cp.High;
|
|
|
|
if (cp.Weight < wMin) wMin = cp.Weight;
|
|
if (cp.Weight > wMax) wMax = cp.Weight;
|
|
|
|
}
|
|
else if (seriesView is IScatterSeriesView || seriesView is IHeatSeriesView)
|
|
{
|
|
if (cp.X < xMin) xMin = cp.X;
|
|
if (cp.X > xMax) xMax = cp.X;
|
|
|
|
if (cp.Y < yMin) yMin = cp.Y;
|
|
if (cp.Y > yMax) yMax = cp.Y;
|
|
|
|
if (cp.Weight < wMin) wMin = cp.Weight;
|
|
if (cp.Weight > wMax) wMax = cp.Weight;
|
|
}
|
|
else
|
|
{
|
|
if (cp.X < xMin) xMin = cp.X;
|
|
if (cp.X > xMax) xMax = cp.X;
|
|
|
|
if (cp.Y < yMin) yMin = cp.Y;
|
|
if (cp.Y > yMax) yMax = cp.Y;
|
|
}
|
|
}
|
|
|
|
tracker.XLimit = new CoreLimit(double.IsInfinity(xMin)
|
|
? 0
|
|
: xMin, double.IsInfinity(yMin) ? 1 : xMax);
|
|
tracker.YLimit = new CoreLimit(double.IsInfinity(yMin)
|
|
? 0
|
|
: yMin, double.IsInfinity(yMax) ? 1 : yMax);
|
|
tracker.WLimit = new CoreLimit(double.IsInfinity(wMin)
|
|
? 0
|
|
: wMin, double.IsInfinity(wMax) ? 1 : wMax);
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the current chart points in the view, the view is required as an argument, because an instance of IChartValues could hold many ISeriesView instances.
|
|
/// </summary>
|
|
/// <param name="seriesView">The series view</param>
|
|
/// <returns></returns>
|
|
public IEnumerable<ChartPoint> GetPoints(ISeriesView seriesView)
|
|
{
|
|
if (seriesView == null) yield break;
|
|
|
|
var config = GetConfig(seriesView);
|
|
|
|
#if NET40
|
|
var isClass = typeof(T).IsClass;
|
|
var isObservable = isClass && typeof(IObservableChartPoint).IsAssignableFrom(typeof(T));
|
|
#endif
|
|
#if NET45
|
|
var isClass = typeof(T).GetTypeInfo().IsClass;
|
|
var isObservable = isClass &&
|
|
typeof(IObservableChartPoint).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo());
|
|
#endif
|
|
|
|
var tracker = GetTracker(seriesView);
|
|
var gci = tracker.Gci;
|
|
|
|
var index = 0;
|
|
foreach (var value in this)
|
|
{
|
|
if (isObservable)
|
|
{
|
|
var observable = (IObservableChartPoint)value;
|
|
if (observable != null)
|
|
{
|
|
observable.PointChanged -= ObservableOnPointChanged;
|
|
observable.PointChanged += ObservableOnPointChanged;
|
|
}
|
|
}
|
|
|
|
var cp = GetChartPoint(isClass, tracker, index, value);
|
|
|
|
cp.Gci = gci;
|
|
cp.Instance = value;
|
|
cp.Key = index;
|
|
cp.SeriesView = seriesView;
|
|
|
|
config.Evaluate(index, value, cp);
|
|
index++;
|
|
|
|
yield return cp;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the garbage collector
|
|
/// </summary>
|
|
public void InitializeStep(ISeriesView series)
|
|
{
|
|
ValidateGarbageCollector(series);
|
|
GetTracker(series).Gci++;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Collects the unnecessary values
|
|
/// </summary>
|
|
public void CollectGarbage(ISeriesView seriesView)
|
|
{
|
|
#if NET40
|
|
var isclass = typeof(T).IsClass;
|
|
#endif
|
|
#if NET45
|
|
var isclass = typeof(T).GetTypeInfo().IsClass;
|
|
#endif
|
|
|
|
var tracker = GetTracker(seriesView);
|
|
|
|
foreach (var garbage in GetGarbagePoints(seriesView).ToList())
|
|
{
|
|
if (garbage.View != null) //yes null, double.Nan Values, will generate null views.
|
|
garbage.View.RemoveFromView(seriesView.Model.Chart);
|
|
|
|
if (!isclass)
|
|
{
|
|
tracker.Indexed.Remove(garbage.Key);
|
|
}
|
|
else
|
|
{
|
|
tracker.Referenced.Remove(garbage.Instance);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets series that owns the values
|
|
/// </summary>
|
|
/// <param name="view"></param>
|
|
/// <returns></returns>
|
|
public PointTracker GetTracker(ISeriesView view)
|
|
{
|
|
PointTracker tracker;
|
|
|
|
if (Trackers.TryGetValue(view, out tracker)) return tracker;
|
|
|
|
tracker = new PointTracker();
|
|
Trackers[view] = tracker;
|
|
|
|
return tracker;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Privates
|
|
|
|
private IPointEvaluator<T> GetConfig(ISeriesView view)
|
|
{
|
|
//Trying to get the user defined configuration...
|
|
|
|
//series == null means that chart values are null, and LiveCharts
|
|
//could not set the Series Instance tho the current chart values...
|
|
if (view == null || view.Model.SeriesCollection == null) return null;
|
|
|
|
var config =
|
|
(view.Configuration ?? view.Model.SeriesCollection.Configuration) as IPointEvaluator<T>;
|
|
|
|
if (config != null) return config;
|
|
|
|
return DefaultConfiguration ??
|
|
(DefaultConfiguration =
|
|
ChartCore.Configurations.GetConfig<T>(view.Model.SeriesOrientation) as IPointEvaluator<T>);
|
|
}
|
|
|
|
private static ChartPoint GetChartPoint(bool isClass, PointTracker tracker, int index, T value)
|
|
{
|
|
ChartPoint cp;
|
|
|
|
if (!isClass)
|
|
{
|
|
if (tracker.Indexed.TryGetValue(index, out cp)) return cp;
|
|
cp = new ChartPoint
|
|
{
|
|
Instance = value,
|
|
Key = index
|
|
};
|
|
tracker.Indexed[index] = cp;
|
|
}
|
|
else
|
|
{
|
|
if (tracker.Referenced.TryGetValue(value, out cp)) return cp;
|
|
cp = new ChartPoint
|
|
{
|
|
Instance = value,
|
|
Key = index
|
|
};
|
|
tracker.Referenced[value] = cp;
|
|
}
|
|
return cp;
|
|
}
|
|
|
|
private void ObservableOnPointChanged()
|
|
{
|
|
Trackers.Keys.ForEach(x => x.Model.Chart.Updater.Run());
|
|
}
|
|
|
|
private IEnumerable<ChartPoint> GetGarbagePoints(ISeriesView view)
|
|
{
|
|
var tracker = GetTracker(view);
|
|
|
|
return tracker.Indexed.Values.Where(x => IsGarbage(x, tracker)).Concat(
|
|
tracker.Referenced.Values.Where(x => IsGarbage(x, tracker)));
|
|
}
|
|
|
|
private void ValidateGarbageCollector(ISeriesView view)
|
|
{
|
|
var tracker = GetTracker(view);
|
|
|
|
if (tracker.Gci != int.MaxValue) return;
|
|
|
|
tracker.Gci = 0;
|
|
|
|
foreach (var point in tracker.Indexed.Values.Concat(tracker.Referenced.Values))
|
|
point.Gci = 0;
|
|
}
|
|
|
|
private static bool IsGarbage(ChartPoint point, PointTracker tracker)
|
|
{
|
|
return point.Gci < tracker.Gci
|
|
|| double.IsNaN(point.X) || double.IsNaN(point.Y);
|
|
}
|
|
|
|
private void OnChanged(IEnumerable<T> oldItems, IEnumerable<T> newItems)
|
|
{
|
|
if (Trackers.Keys.All(x => x != null && x.Model.Chart != null))
|
|
Trackers.Keys.ForEach(x => x.Model.Chart.Updater.Run());
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |