#define Graph_And_Chart_PRO using System; using System.Collections.Generic; using System.Linq; using System.Text; using UnityEngine; using UnityEngine.Events; namespace ChartAndGraph { /// /// the graph chart class. /// [ExecuteInEditMode] public abstract class GraphChartBase : ScrollableAxisChart { [SerializeField] [Tooltip("The height ratio of the chart")] protected float heightRatio = 300; [SerializeField] [Tooltip("The width ratio of the chart")] protected float widthRatio = 600; /// /// event arguments for a bar chart event /// public class GraphEventArgs { public GraphEventArgs(int index,Vector3 position, DoubleVector2 value,float magnitude, string category,string xString,string yString) { Position = position; Value = value; Category = category; XString = xString; YString = yString; Index = index; Magnitude = magnitude; } public float Magnitude { get; private set; } public int Index { get; private set; } public string XString { get; private set; } public string YString { get; private set; } public Vector3 Position { get; private set; } public DoubleVector2 Value { get; private set; } public string Category { get; private set; } public string Group { get; private set; } } /// /// a graph chart event /// [Serializable] public class GraphEvent : UnityEvent { } /// /// occures when a point is clicked /// public GraphEvent PointClicked = new GraphEvent(); /// /// occurs when a point is hovered /// public GraphEvent PointHovered = new GraphEvent(); /// /// occurs when no point is hovered any longer /// public UnityEvent NonHovered = new UnityEvent(); /// /// The height ratio of the chart /// [SerializeField] public float HeightRatio { get { return heightRatio; } set { heightRatio = value; Invalidate(); } } /// /// The width ratio of the chart /// public float WidthRatio { get { return widthRatio; } set { widthRatio = value; Invalidate(); } } [SerializeField] private string itemFormat = ":"; public string ItemFormat { get { return itemFormat; } set { itemFormat = value; Invalidate(); } } protected bool mRealtimeUpdateIndex = false; protected Dictionary mMinimumUpdateIndex = new Dictionary(); /// /// the graph data /// [HideInInspector] [SerializeField] protected GraphData Data = new GraphData(); /// /// Holds the graph chart data. including values and categories /// public GraphData DataSource { get { return Data; } } protected override LegenedData LegendInfo { get { LegenedData data = new LegenedData(); if (Data == null) return data; foreach(var cat in ((IInternalGraphData)Data).Categories) { LegenedData.LegenedItem item = new LegenedData.LegenedItem(); item.Name = cat.Name; if (cat.FillMaterial != null) item.Material = cat.FillMaterial; else if (cat.LineMaterial != null) item.Material = cat.LineMaterial; else item.Material = cat.PointMaterial; data.AddLegenedItem(item); } return data; } } protected override IChartData DataLink { get { return Data; } } public abstract void ClearCache(); private void HookEvents() { ((IInternalGraphData)Data).InternalDataChanged -= GraphChart_InternalDataChanged; ((IInternalGraphData)Data).InternalRealTimeDataChanged -= GraphChartBase_InternalRealTimeDataChanged; ; ((IInternalGraphData)Data).InternalViewPortionChanged -= GraphChartBase_InternalViewPortionChanged; ((IInternalGraphData)Data).InternalViewPortionChanged += GraphChartBase_InternalViewPortionChanged; ((IInternalGraphData)Data).InternalDataChanged += GraphChart_InternalDataChanged; ((IInternalGraphData)Data).InternalRealTimeDataChanged += GraphChartBase_InternalRealTimeDataChanged; ; } private void GraphChartBase_InternalViewPortionChanged(object sender, EventArgs e) { ViewPortionChanged(); } protected abstract void ViewPortionChanged(); private void GraphChartBase_InternalRealTimeDataChanged(int index,string category) { if (category == null) // full invalidtion { mRealtimeUpdateIndex = false; } if (mRealtimeUpdateIndex) { mRealtimeUpdateIndex = true; int minIndex; if (mMinimumUpdateIndex.TryGetValue(category, out minIndex) == false) minIndex = int.MaxValue; if (minIndex > index) minIndex = index; mMinimumUpdateIndex[category] = minIndex; } InvalidateRealtime(); } protected void ClearRealtimeIndexdata() { mMinimumUpdateIndex.Clear(); mRealtimeUpdateIndex = true; } public override void Invalidate() { base.Invalidate(); mRealtimeUpdateIndex = false; // trigger a full invalidation mMinimumUpdateIndex.Clear(); } private void GraphChart_InternalDataChanged(object sender, EventArgs e) { Invalidate(); } protected override void Start() { base.Start(); if (ChartCommon.IsInEditMode == false) { HookEvents(); } Invalidate(); } protected override void OnValidate() { base.OnValidate(); if (ChartCommon.IsInEditMode == false) { HookEvents(); } Data.RestoreDataValues(); Invalidate(); } protected override void OnLabelSettingChanged() { base.OnLabelSettingChanged(); Invalidate(); } protected override void OnAxisValuesChanged() { base.OnAxisValuesChanged(); Invalidate(); } protected override void OnLabelSettingsSet() { base.OnLabelSettingsSet(); Invalidate(); } protected DoubleVector4 TransformPoint(Rect viewRect,Vector3 point ,DoubleVector2 min, DoubleVector2 range) { return ChartCommon.interpolateInRect(viewRect, new DoubleVector3((point.x - min.x) / range.x, (point.y - min.y) / range.y)); } protected override void Update() { base.Update(); } private void UpdateMinMax(DoubleVector3 point,ref double minX,ref double minY,ref double maxX,ref double maxY) { minX = Math.Min(minX, point.x); maxX = Math.Max(maxX, point.x); minY = Math.Min(minY, point.y); maxY = Math.Max(maxY, point.y); } protected override float GetScrollingRange(int axis) { float min = (float)((IInternalGraphData)Data).GetMinValue(axis, false); float max = (float)((IInternalGraphData)Data).GetMaxValue(axis, false); return max - min; } private Rect CreateUvRect(Rect completeRect,Rect lineRect) { if (completeRect.width < 0.0001f || completeRect.height < 0.0001f) return new Rect(); if(float.IsInfinity(lineRect.xMax) || float.IsInfinity(lineRect.xMin) || float.IsInfinity(lineRect.yMin) || float.IsInfinity(lineRect.yMax)) return new Rect(); float x = (lineRect.xMin - completeRect.xMin) / completeRect.width; float y = (lineRect.yMin - completeRect.yMin) / completeRect.height; float w = lineRect.width / completeRect.width; float h = lineRect.height / completeRect.height; return new Rect(x, y, w, h); } protected int ClipPoints(IList points,List res,out Rect uv) { double minX, minY, maxX, maxY, xScroll, yScroll, xSize, ySize, xOut; GetScrollParams(out minX, out minY, out maxX, out maxY, out xScroll, out yScroll, out xSize, out ySize, out xOut); double direction = 1.0; if (minX > maxX) direction = -1.0; bool prevOut = false; bool prevIn = false; double maxXLocal = double.MinValue, minXLocal = double.MaxValue, maxYLocal = double.MinValue, minYLocal = double.MaxValue; minX = double.MaxValue; minY = double.MaxValue; maxX = double.MinValue; maxY = double.MinValue; int refrenceIndex = 0; for(int i=0; i xOut* direction) // || point.y < yScroll || point.y > yOut) { prevOut = true; if (pIn) { res.Add(point.ToDoubleVector4()); // uv = CreateUvRect(new Rect(minX, minY, maxX - minX, maxY - minY), new Rect(minXLocal, minYLocal, maxXLocal - minXLocal, maxYLocal - minYLocal)); // return refrenceIndex; } if(pOut) { if(point.x* direction > xOut* direction && points[i-1].x* direction < xScroll* direction) { UpdateMinMax(points[i - 1], ref minXLocal, ref minYLocal, ref maxXLocal, ref maxYLocal); UpdateMinMax(point, ref minXLocal, ref minYLocal, ref maxXLocal, ref maxYLocal); res.Add(points[i - 1].ToDoubleVector4()); res.Add(point.ToDoubleVector4()); } } } else { prevIn = true; if (pOut) { refrenceIndex = i - 1; UpdateMinMax(points[i - 1], ref minXLocal, ref minYLocal, ref maxXLocal, ref maxYLocal); res.Add(points[i - 1].ToDoubleVector4()); } UpdateMinMax(point, ref minXLocal, ref minYLocal, ref maxXLocal, ref maxYLocal); res.Add(point.ToDoubleVector4()); } } for(int i=0; i points, Rect viewRect, DoubleVector3 min, DoubleVector3 max) { DoubleVector3 range = max - min; if (Math.Abs(range.x) <= 0.0001f || Math.Abs(range.y) < 0.0001f) return; double radiusMultiplier = Math.Min(viewRect.width / Math.Abs(range.x), viewRect.height / Math.Abs(range.y)); for (int i = 0; i < points.Count; i++) { DoubleVector3 point = points[i]; DoubleVector4 res = ChartCommon.interpolateInRect(viewRect, new DoubleVector3((point.x - min.x) / range.x, (point.y - min.y) / range.y)); res.z = point.z * radiusMultiplier; points[i] = res.ToDoubleVector3(); } } protected bool TransformPoints(IList points,List output,Rect viewRect, DoubleVector3 min, DoubleVector3 max) { output.Clear(); DoubleVector3 range = max - min; if (Math.Abs(range.x) <= 0.0001f || Math.Abs(range.y) < 0.0001f) return false; double radiusMultiplier = Math.Min(viewRect.width / Math.Abs(range.x), viewRect.height / Math.Abs(range.y)); bool HasSize = false; for(int i=0; i 0f) HasSize = true; output.Add(res.ToVector4()); } return HasSize; } protected override void ValidateProperties() { base.ValidateProperties(); if (heightRatio < 0f) heightRatio = 0f; if (widthRatio < 0f) widthRatio = 0f; } protected override bool SupportsCategoryLabels { get { return false; } } protected override bool SupportsGroupLables { get { return false; } } protected override bool SupportsItemLabels { get { return true; } } protected override float TotalHeightLink { get { return heightRatio; } } protected override float TotalWidthLink { get { return widthRatio; } } protected override float TotalDepthLink { get { return 0.0f; } } protected override bool HasValues(AxisBase axis) { return true; //all axis have values in the graph chart } protected override double MaxValue(AxisBase axis) { if (axis == null) return 0.0; if (axis == mHorizontalAxis) return ((IInternalGraphData)Data).GetMaxValue(0, false); if (axis == mVerticalAxis) return ((IInternalGraphData)Data).GetMaxValue(1, false); return 0.0; } protected override double MinValue(AxisBase axis) { if (axis == null) return 0.0; if (axis == mHorizontalAxis) return ((IInternalGraphData)Data).GetMinValue(0, false); if (axis == mVerticalAxis) return ((IInternalGraphData)Data).GetMinValue(1, false); return 0.0; } protected override void OnItemHoverted(object userData) { base.OnItemHoverted(userData); GraphEventArgs args = userData as GraphEventArgs; if (PointHovered != null) PointHovered.Invoke(args); } StringBuilder mTmpBuilder = new StringBuilder(); public string FormatItem(double x, double y) { DoubleVector3 p = new DoubleVector3(x, y); string xFormat = StringFromAxisFormat(p, mHorizontalAxis, true); string yFormat = StringFromAxisFormat(p, mVerticalAxis, false); FormatItem(mTmpBuilder, xFormat, yFormat); return mTmpBuilder.ToString(); } public string FormatItem(string x, string y) { FormatItem(mTmpBuilder, x, y); return mTmpBuilder.ToString(); } protected void FormatItem(StringBuilder builder, string x, string y) { builder.Length = 0; builder.Append(itemFormat); builder.Replace("", x).Replace("", y).Replace("\\n", Environment.NewLine); } protected override void OnItemSelected(object userData) { base.OnItemSelected(userData); GraphEventArgs args = userData as GraphEventArgs; if (PointClicked != null) PointClicked.Invoke(args); } } }