using System; using System.Collections.Generic; using System.Reflection; using UnityEngine; namespace XCharts.Runtime { public partial class BaseChart { public T AddSerie(string serieName = null, bool show = true, bool addToHead = false) where T : Serie { if (!CanAddSerie()) return null; var index = -1; var serie = InsertSerie(index, typeof(T), serieName, show, addToHead) as T; CreateSerieHandler(serie); return serie; } public T InsertSerie(int index, string serieName = null, bool show = true) where T : Serie { if (!CanAddSerie()) return null; return InsertSerie(index, typeof(T), serieName, show) as T; } public void InsertSerie(Serie serie, int index = -1, bool addToHead = false) { serie.AnimationRestart(); AnimationStyleHelper.UpdateSerieAnimation(serie); if (addToHead) m_Series.Insert(0, serie); else if (index >= 0) m_Series.Insert(index, serie); else m_Series.Add(serie); ResetSeriesIndex(); SeriesHelper.UpdateSerieNameList(this, ref m_LegendRealShowName); } public bool MoveUpSerie(int serieIndex) { if (serieIndex < 0 || serieIndex > m_Series.Count - 1) return false; if (serieIndex == 0) return false; var up = GetSerie(serieIndex - 1); var temp = GetSerie(serieIndex); m_Series[serieIndex - 1] = temp; m_Series[serieIndex] = up; ResetSeriesIndex(); InitSerieHandlers(); RefreshChart(); return true; } public bool MoveDownSerie(int serieIndex) { if (serieIndex < 0 || serieIndex > m_Series.Count - 1) return false; if (serieIndex == m_Series.Count - 1) return false; var down = GetSerie(serieIndex + 1); var temp = GetSerie(serieIndex); m_Series[serieIndex + 1] = temp; m_Series[serieIndex] = down; ResetSeriesIndex(); InitSerieHandlers(); RefreshChart(); return true; } public bool CanAddSerie() where T : Serie { return CanAddSerie(typeof(T)); } public bool CanAddSerie(Type type) { return m_TypeListForSerie.ContainsKey(type); } public bool HasSerie() where T : Serie { return HasSerie(typeof(T)); } public bool HasSerie(Type type) { if (!type.IsSubclassOf(typeof(Serie))) return false; foreach (var serie in m_Series) { if (serie.GetType() == type) return true; } return false; } public T GetSerie() where T : Serie { foreach (var serie in m_Series) { if (serie is T) return serie as T; } return null; } public Serie GetSerie(string serieName) { foreach (var serie in m_Series) { if (serie.serieName.Equals(serieName)) return serie; } return null; } public Serie GetSerie(int serieIndex) { if (serieIndex < 0 || serieIndex > m_Series.Count - 1) return null; return m_Series[serieIndex]; } public T GetSerie(int serieIndex) where T : Serie { if (serieIndex < 0 || serieIndex > m_Series.Count - 1) return null; return m_Series[serieIndex] as T; } public void RemoveSerie(string serieName) { for (int i = m_Series.Count - 1; i >= 0; i--) { var serie = m_Series[i]; if (string.IsNullOrEmpty(serie.serieName) && serie.serieName.Equals(serieName)) RemoveSerie(serie); } } public void RemoveSerie(int serieIndex) { if (serieIndex < 0 || serieIndex > m_Series.Count - 1) return; RemoveSerie(m_Series[serieIndex]); } public void RemoveSerie() where T : Serie { for (int i = m_Series.Count - 1; i >= 0; i--) { var serie = m_Series[i]; if (serie is T) RemoveSerie(serie); } } public void RemoveSerie(Serie serie) { serie.OnRemove(); m_SerieHandlers.Remove(serie.handler); m_Series.Remove(serie); RefreshChart(); } public bool CovertSerie(Serie serie) where T : Serie { return CovertSerie(serie, typeof(T)); } public bool CovertSerie(Serie serie, Type type) { try { var newSerie = type.InvokeMember("CovertSerie", BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public, null, null, new object[] { serie }) as Serie; return ReplaceSerie(serie, newSerie); } catch { Debug.LogError(string.Format("CovertSerie Failed: can't found {0}.CovertSerie(Serie serie)", type.Name)); return false; } } public bool ReplaceSerie(Serie oldSerie, Serie newSerie) { if (oldSerie == null || newSerie == null) return false; var index = m_Series.IndexOf(oldSerie); if (index < 0) return false; AnimationStyleHelper.UpdateSerieAnimation(newSerie); oldSerie.OnRemove(); m_Series.RemoveAt(index); m_Series.Insert(index, newSerie); ResetSeriesIndex(); InitSerieHandlers(); RefreshAllComponent(); RefreshChart(); return true; } /// /// Add a data to serie. /// |If serieName doesn't exist in legend,will be add to legend. /// |添加一个数据到指定的系列中。 /// /// the name of serie /// the data to add /// the name of data /// the unique id of data /// Returns True on success public SerieData AddData(string serieName, double data, string dataName = null, string dataId = null) { var serie = GetSerie(serieName); if (serie != null) { var serieData = serie.AddYData(data, dataName, dataId); RefreshPainter(serie.painter); return serieData; } return null; } /// /// Add a data to serie. /// |添加一个数据到指定的系列中。 /// /// the index of serie /// the data to add /// the name of data /// the unique id of data /// Returns True on success public SerieData AddData(int serieIndex, double data, string dataName = null, string dataId = null) { var serie = GetSerie(serieIndex); if (serie != null) { var serieData = serie.AddYData(data, dataName, dataId); RefreshPainter(serie.painter); return serieData; } return null; } /// /// Add an arbitray dimension data to serie,such as (x,y,z,...). /// |添加多维数据(x,y,z...)到指定的系列中。 /// /// the name of serie /// the (x,y,z,...) data /// the name of data /// the unique id of data /// Returns True on success public SerieData AddData(string serieName, List multidimensionalData, string dataName = null, string dataId = null) { var serie = GetSerie(serieName); if (serie != null) { var serieData = serie.AddData(multidimensionalData, dataName, dataId); RefreshPainter(serie.painter); return serieData; } return null; } /// /// Add an arbitray dimension data to serie,such as (x,y,z,...). /// |添加多维数据(x,y,z...)到指定的系列中。 /// /// the index of serie,index starts at 0 /// the (x,y,z,...) data /// the name of data /// the unique id of data /// Returns True on success public SerieData AddData(int serieIndex, List multidimensionalData, string dataName = null, string dataId = null) { var serie = GetSerie(serieIndex); if (serie != null) { var serieData = serie.AddData(multidimensionalData, dataName, dataId); RefreshPainter(serie.painter); return serieData; } return null; } /// /// Add a (x,y) data to serie. /// |添加(x,y)数据到指定系列中。 /// /// the name of serie /// x data /// y data /// the name of data /// the unique id of data /// Returns True on success public SerieData AddData(string serieName, double xValue, double yValue, string dataName = null, string dataId = null) { var serie = GetSerie(serieName); if (serie != null) { var serieData = serie.AddXYData(xValue, yValue, dataName, dataId); RefreshPainter(serie.painter); return serieData; } return null; } /// /// Add a (x,y) data to serie. /// |添加(x,y)数据到指定系列中。 /// /// the index of serie /// x data /// y data /// the name of data /// the unique id of data /// Returns True on success public SerieData AddData(int serieIndex, double xValue, double yValue, string dataName = null, string dataId = null) { var serie = GetSerie(serieIndex); if (serie != null) { var serieData = serie.AddXYData(xValue, yValue, dataName, dataId); RefreshPainter(serie.painter); return serieData; } return null; } /// /// Add a (time,y) data to serie. /// |添加(time,y)数据到指定的系列中。 /// /// /// /// /// /// /// public SerieData AddData(string serieName, DateTime time, double yValue, string dataName = null, string dataId = null) { var xValue = DateTimeUtil.GetTimestamp(time); return AddData(serieName, xValue, yValue, dataName, dataId); } /// /// Add a (time,y) data to serie. /// |添加(time,y)数据到指定的系列中。 /// /// /// /// /// /// /// public SerieData AddData(int serieIndex, DateTime time, double yValue, string dataName = null, string dataId = null) { var xValue = DateTimeUtil.GetTimestamp(time); return AddData(serieIndex, xValue, yValue, dataName, dataId); } public SerieData AddData(int serieIndex, double open, double close, double lowest, double heighest, string dataName = null, string dataId = null) { var serie = GetSerie(serieIndex); if (serie != null) { var serieData = serie.AddData(open, close, lowest, heighest, dataName, dataId); RefreshPainter(serie.painter); return serieData; } return null; } public SerieData AddData(string serieName, double open, double close, double lowest, double heighest, string dataName = null, string dataId = null) { var serie = GetSerie(serieName); if (serie != null) { var serieData = serie.AddData(open, close, lowest, heighest, dataName, dataId); RefreshPainter(serie.painter); return serieData; } return null; } /// /// Update serie data by serie name. /// |更新指定系列中的指定索引数据。 /// /// the name of serie /// the index of data /// the data will be update public bool UpdateData(string serieName, int dataIndex, double value) { var serie = GetSerie(serieName); if (serie != null) { serie.UpdateYData(dataIndex, value); RefreshPainter(serie); return true; } return false; } /// /// Update serie data by serie index. /// |更新指定系列中的指定索引数据。 /// /// the index of serie /// the index of data /// the data will be update public bool UpdateData(int serieIndex, int dataIndex, double value) { var serie = GetSerie(serieIndex); if (serie != null) { serie.UpdateYData(dataIndex, value); RefreshPainter(serie); return true; } return false; } /// /// 更新指定系列指定索引的数据项的多维数据。 /// /// /// /// 一个数据项的多维数据列表,而不是多个数据项的数据 public bool UpdateData(string serieName, int dataIndex, List multidimensionalData) { var serie = GetSerie(serieName); if (serie != null) { serie.UpdateData(dataIndex, multidimensionalData); RefreshPainter(serie); return true; } return false; } /// /// 更新指定系列指定索引的数据项的多维数据。 /// /// /// /// 一个数据项的多维数据列表,而不是多个数据项的数据 public bool UpdateData(int serieIndex, int dataIndex, List multidimensionalData) { var serie = GetSerie(serieIndex); if (serie != null) { serie.UpdateData(dataIndex, multidimensionalData); RefreshPainter(serie); return true; } return false; } /// /// 更新指定系列指定索引指定维数的数据。维数从0开始。 /// /// /// /// 指定维数,从0开始 /// public bool UpdateData(string serieName, int dataIndex, int dimension, double value) { var serie = GetSerie(serieName); if (serie != null) { serie.UpdateData(dataIndex, dimension, value); RefreshPainter(serie); return true; } return false; } /// /// 更新指定系列指定索引指定维数的数据。维数从0开始。 /// /// /// /// 指定维数,从0开始 /// public bool UpdateData(int serieIndex, int dataIndex, int dimension, double value) { var serie = GetSerie(serieIndex); if (serie != null) { serie.UpdateData(dataIndex, dimension, value); RefreshPainter(serie); return true; } return false; } /// /// Update serie data name. /// |更新指定系列中的指定索引数据名称。 /// /// /// /// public bool UpdateDataName(string serieName, int dataIndex, string dataName) { var serie = GetSerie(serieName); if (serie != null) { serie.UpdateDataName(dataIndex, dataName); return true; } return false; } /// /// Update serie data name. /// |更新指定系列中的指定索引数据名称。 /// /// /// /// public bool UpdateDataName(int serieIndex, int dataIndex, string dataName) { var serie = GetSerie(serieIndex); if (serie != null) { serie.UpdateDataName(dataIndex, dataName); return true; } return false; } public double GetData(string serieName, int dataIndex, int dimension = 1) { var serie = GetSerie(serieName); if (serie != null) { return serie.GetData(dataIndex, dimension); } return 0; } public double GetData(int serieIndex, int dataIndex, int dimension = 1) { var serie = GetSerie(serieIndex); if (serie != null) { return serie.GetData(dataIndex, dimension); } return 0; } public int GetAllSerieDataCount() { var total = 0; foreach (var serie in m_Series) total += serie.dataCount; return total; } /// /// Whether to show serie. /// |设置指定系列是否显示。 /// /// the name of serie /// Active or not public void SetSerieActive(string serieName, bool active) { var serie = GetSerie(serieName); if (serie != null) SetSerieActive(serie, active); } /// /// Whether to show serie. /// |设置指定系列是否显示。 /// /// the index of serie /// Active or not public void SetSerieActive(int serieIndex, bool active) { var serie = GetSerie(serieIndex); if (serie != null) SetSerieActive(serie, active); } public void SetSerieActive(Serie serie, bool active) { serie.show = active; serie.AnimationReset(); if (active) serie.AnimationFadeIn(); UpdateLegendColor(serie.serieName, active); } /// /// Add a category data to xAxis. /// |添加一个类目数据到指定的x轴。 /// /// the category data /// which xAxis should category add to public void AddXAxisData(string category, int xAxisIndex = 0) { var xAxis = GetChartComponent(xAxisIndex); if (xAxis != null) { xAxis.AddData(category); } } /// /// Update category data. /// |更新X轴类目数据。 /// /// the index of category data /// /// which xAxis index to update to public void UpdateXAxisData(int index, string category, int xAxisIndex = 0) { var xAxis = GetChartComponent(xAxisIndex); if (xAxis != null) { xAxis.UpdateData(index, category); } } /// /// Add an icon to xAxis. /// |添加一个图标到指定的x轴。 /// /// /// public void AddXAxisIcon(Sprite icon, int xAxisIndex = 0) { var xAxis = GetChartComponent(xAxisIndex); if (xAxis != null) { xAxis.AddIcon(icon); } } /// /// Update xAxis icon. /// |更新X轴图标。 /// /// /// /// public void UdpateXAxisIcon(int index, Sprite icon, int xAxisIndex = 0) { var xAxis = GetChartComponent(xAxisIndex); if (xAxis != null) { xAxis.UpdateIcon(index, icon); } } /// /// Add a category data to yAxis. /// |添加一个类目数据到指定的y轴。 /// /// the category data /// which yAxis should category add to public void AddYAxisData(string category, int yAxisIndex = 0) { var yAxis = GetChartComponent(yAxisIndex); if (yAxis != null) { yAxis.AddData(category); } } /// /// Update category data. /// |更新Y轴类目数据。 /// /// the index of category data /// /// which yAxis index to update to public void UpdateYAxisData(int index, string category, int yAxisIndex = 0) { var yAxis = GetChartComponent(yAxisIndex); if (yAxis != null) { yAxis.UpdateData(index, category); } } /// /// Add an icon to yAxis. /// |添加一个图标到指定的y轴。 /// /// /// public void AddYAxisIcon(Sprite icon, int yAxisIndex = 0) { var yAxis = GetChartComponent(yAxisIndex); if (yAxis != null) { yAxis.AddIcon(icon); } } /// /// 更新Y轴图标。 /// /// /// /// public void UpdateYAxisIcon(int index, Sprite icon, int yAxisIndex = 0) { var yAxis = GetChartComponent(yAxisIndex); if (yAxis != null) { yAxis.UpdateIcon(index, icon); } } public float GetSerieBarGap() where T : Serie { float gap = 0f; for (int i = 0; i < m_Series.Count; i++) { var serie = m_Series[i]; if (serie is T) { if (serie.barGap != 0) { gap = serie.barGap; } } } return gap; } public double GetSerieSameStackTotalValue(string stack, int dataIndex) where T : Serie { if (string.IsNullOrEmpty(stack)) return 0; double total = 0; foreach (var serie in m_Series) { if (serie is T) { if (stack.Equals(serie.stack)) { total += serie.data[dataIndex].data[1]; } } } return total; } public int GetSerieBarRealCount() where T : Serie { var count = 0; barStackSet.Clear(); for (int i = 0; i < m_Series.Count; i++) { var serie = m_Series[i]; if (!serie.show) continue; if (serie is T) { if (!string.IsNullOrEmpty(serie.stack)) { if (barStackSet.Contains(serie.stack)) continue; barStackSet.Add(serie.stack); } count++; } } return count; } private HashSet barStackSet = new HashSet(); public float GetSerieTotalWidth(float categoryWidth, float gap, int realBarCount) where T : Serie { float total = 0; float lastGap = 0; barStackSet.Clear(); for (int i = 0; i < m_Series.Count; i++) { var serie = m_Series[i]; if (!serie.show) continue; if (serie is T) { if (!string.IsNullOrEmpty(serie.stack)) { if (barStackSet.Contains(serie.stack)) continue; barStackSet.Add(serie.stack); } var width = GetStackBarWidth(categoryWidth, serie, realBarCount); if (gap == -1) { if (width > total) total = width; } else { lastGap = ChartHelper.GetActualValue(gap, width); total += width; total += lastGap; } } } if (total > 0 && gap != -1) total -= lastGap; return total; } public float GetSerieTotalGap(float categoryWidth, float gap, int index) where T : Serie { if (index <= 0) return 0; var total = 0f; var count = 0; var totalRealBarCount = GetSerieBarRealCount(); barStackSet.Clear(); for (int i = 0; i < m_Series.Count; i++) { var serie = m_Series[i]; if (!serie.show) continue; if (serie is T) { if (!string.IsNullOrEmpty(serie.stack)) { if (barStackSet.Contains(serie.stack)) continue; barStackSet.Add(serie.stack); } var width = GetStackBarWidth(categoryWidth, serie, totalRealBarCount); if (gap == -1) { if (width > total) total = width; } else { total += width + ChartHelper.GetActualValue(gap, width); } if (count + 1 >= index) break; else count++; } } return total; } private float GetStackBarWidth(float categoryWidth, Serie now, int realBarCount) where T : Serie { if (string.IsNullOrEmpty(now.stack)) return now.GetBarWidth(categoryWidth, realBarCount); float barWidth = 0; for (int i = 0; i < m_Series.Count; i++) { var serie = m_Series[i]; if ((serie is T) && serie.show && now.stack.Equals(serie.stack)) { if (serie.barWidth > barWidth) barWidth = serie.barWidth; } } if (barWidth == 0) { var width = ChartHelper.GetActualValue(0.6f, categoryWidth); if (realBarCount == 0) return width < 1 ? categoryWidth : width; else return width / realBarCount; } else return ChartHelper.GetActualValue(barWidth, categoryWidth); } private List tempList = new List(); public int GetSerieIndexIfStack(Serie currSerie) where T : Serie { tempList.Clear(); int index = 0; for (int i = 0; i < m_Series.Count; i++) { var serie = m_Series[i]; if (!(serie is T)) continue; if (string.IsNullOrEmpty(serie.stack)) { if (serie.index == currSerie.index) return index; tempList.Add(string.Empty); index++; } else { if (!tempList.Contains(serie.stack)) { if (serie.index == currSerie.index) return index; tempList.Add(serie.stack); index++; } else { if (serie.index == currSerie.index) return tempList.IndexOf(serie.stack); } } } return 0; } internal void InitSerieHandlers() { m_SerieHandlers.Clear(); for (int i = 0; i < m_Series.Count; i++) { var serie = m_Series[i]; serie.index = i; CreateSerieHandler(serie); } } private void CreateSerieHandler(Serie serie) { if (serie == null) throw new ArgumentNullException("serie is null"); if (!serie.GetType().IsDefined(typeof(SerieHandlerAttribute), false)) { Debug.LogError("Serie no Handler:" + serie.GetType()); return; } var attribute = serie.GetType().GetAttribute(); var handler = (SerieHandler) Activator.CreateInstance(attribute.handler); handler.attribute = attribute; handler.chart = this; handler.defaultDimension = 1; handler.SetSerie(serie); serie.handler = handler; m_SerieHandlers.Add(handler); } private Serie InsertSerie(int index, Type type, string serieName, bool show = true, bool addToHead = false) { CheckAddRequireChartComponent(type); var serie = Activator.CreateInstance(type) as Serie; serie.show = show; serie.serieName = serieName; serie.serieType = type.Name; serie.index = m_Series.Count; if (type == typeof(Scatter)) { serie.symbol.show = true; serie.symbol.type = SymbolType.Circle; } else if (type == typeof(Line)) { serie.symbol.show = true; serie.symbol.type = SymbolType.EmptyCircle; } else { serie.symbol.show = false; } InsertSerie(serie, index, addToHead); return serie; } private void ResetSeriesIndex() { #if UNITY_EDITOR && UNITY_2019_1_OR_NEWER UnityEditor.EditorUtility.SetDirty(this); #endif for (int i = 0; i < m_Series.Count; i++) { m_Series[i].index = i; } } private void AddSerieAfterDeserialize(Serie serie) { serie.OnAfterDeserialize(); m_Series.Add(serie); } public string GenerateDefaultSerieName() { return "serie" + m_Series.Count; } public bool IsSerieName(string name) { if (string.IsNullOrEmpty(name)) return false; foreach (var serie in m_Series) { if (name.Equals(serie.serieName)) return true; } return false; } } }