#define Graph_And_Chart_PRO using ChartAndGraph.DataSource; using ChartAndGraph.Exceptions; using System; using System.Collections.Generic; using System.Linq; using System.Text; using UnityEngine; namespace ChartAndGraph { /// /// Holds and manages chart data in a table like fashion, The data in this class can be used with pie and bar chart types. /// internal class ChartSparseDataSource : ChartDataSourceBase { struct KeyElement { public KeyElement(ChartDataRow row, ChartDataColumn column) : this() { Row = row; Column = column; } public bool IsIn(IDataItem item) { if (Row == item || Column == item) return true; return false; } public bool IsInRow(ChartDataRow row) { return row == Row; } public bool IsInColumn(ChartDataColumn column) { return column == Column; } static public bool operator == (KeyElement a,KeyElement b) { return a.Equals(b); } static public bool operator !=(KeyElement a, KeyElement b) { return !a.Equals(b); } public override bool Equals(object obj) { if (obj is KeyElement) { KeyElement elem = (KeyElement)obj; if (elem.Row == Row && elem.Column == Column) return true; return false; } return base.Equals(obj); } public override int GetHashCode() { return Column.GetHashCode() ^ Row.GetHashCode(); } public ChartDataRow Row { get; private set; } public ChartDataColumn Column { get; private set; } } public ChartSparseDataSource() { mColumns = new ChartColumnCollection(); mRows = new ChartRowCollection(); mColumns.NameChanged += MColumns_NameChanged; mRows.NameChanged += MRows_NameChanged; mColumns.OrderChanged += OrderChanged; mColumns.ItemsReplaced += MColumns_ItemsReplaced; ; mRows.OrderChanged += OrderChanged; mColumns.ItemRemoved += Columns_ItemRemoved; mRows.ItemRemoved += Rows_ItemRemoved; } private void MColumns_ItemsReplaced(string first, int firstIndex, string second, int secondIndex) { mRawData = null; // data is invalidated. mChartDataToIndex.Clear(); // clear the index dictionary if (mSuspendEvents == false) OnItemsReplaced(first, firstIndex, second, secondIndex); else mFireEvent = true; } private void MRows_NameChanged(string arg1, IDataItem arg2) { } private void MColumns_NameChanged(string arg1, IDataItem arg2) { } Dictionary mData = new Dictionary(); Dictionary mChartDataToIndex = new Dictionary(); KeyValuePair? mMaxValue = null, mMinValue = null; bool mFireEvent = false; bool mSuspendEvents = false; public bool SuspendEvents { get { return mSuspendEvents; } set { if (mSuspendEvents == true && value == false) { if (mFireEvent) { OnDataStructureChanged(); mFireEvent = false; } } mSuspendEvents = value; } } double[,] mRawData; public ChartColumnCollection mColumns; public ChartRowCollection mRows; public override ChartColumnCollection Columns { get { return mColumns; } } public override ChartRowCollection Rows { get { return mRows; } } void OrderChanged(object sender, EventArgs e) { // the structer of the table has changed mRawData = null; // data is invalidated. mChartDataToIndex.Clear(); // clear the index dictionary if (mSuspendEvents == false) OnDataStructureChanged(); else mFireEvent = true; } private void FindMinMaxValue() { mMaxValue = null; mMinValue = null; KeyValuePair? maxValue = null; KeyValuePair? minValue = null; foreach(KeyValuePair pair in mData) { if (maxValue.HasValue == false || maxValue.Value.Value < pair.Value) maxValue = pair; if (minValue.HasValue == false || minValue.Value.Value > pair.Value) minValue = pair; } if (maxValue.HasValue) mMaxValue = maxValue.Value; if (minValue.HasValue) mMinValue = minValue.Value; } public void Clear() { Columns.Clear(); Rows.Clear(); } /// /// Raw data is prepared and held as long as the structed or order of the table has not changed /// private void PrepareRawData() { mChartDataToIndex.Clear(); for (int i=0; i pair in mData) { int columnIndex; int rowIndex; if (mChartDataToIndex.TryGetValue(pair.Key.Column, out columnIndex) == false) continue; if (mChartDataToIndex.TryGetValue(pair.Key.Row, out rowIndex) == false) continue; mRawData[rowIndex, columnIndex] = pair.Value; } FindMinMaxValue(); } private bool HasZeroItems() { int total = mRows.Count* mColumns.Count; if (mData.Count < total) return true; return false; } internal double? getRawMaxValue() { EnsureRawData(); if (mMaxValue.HasValue == false) return null; double maxValue; if (mData.TryGetValue(mMaxValue.Value.Key, out maxValue) == false) return null; if (HasZeroItems() && maxValue < 0.0) return 0.0; return maxValue; } internal double? getRawMinValue() { EnsureRawData(); if (mMinValue.HasValue == false) return null; double minValue; if (mData.TryGetValue(mMinValue.Value.Key, out minValue) == false) return null; if (HasZeroItems() && minValue > 0.0) return 0.0; return minValue; } private void ItemRemoved(IDataItem item) { Dictionary newData = new Dictionary(); foreach(KeyValuePair pair in mData) { if (pair.Key.IsIn(item) == false) newData.Add(pair.Key, pair.Value); } mData = newData; mRawData = null; if (mSuspendEvents == false) OnDataStructureChanged(); else mFireEvent = true; } void Rows_ItemRemoved(ChartDataRow obj) { ItemRemoved(obj); } void Columns_ItemRemoved(ChartDataColumn obj) { ItemRemoved(obj); } void EnsureRawData() { if (mRawData == null) // dynamically check if the raw data should be regenerated PrepareRawData(); } public override double[,] getRawData() { EnsureRawData(); return mRawData; } private bool VerifyMinMaxValue(KeyElement element,double value) { bool findMinMax = false; bool res = false; if (mMaxValue.HasValue == false || mMaxValue.Value.Value < value) { mMaxValue = new KeyValuePair(element, value); res = true; } else { if (mMaxValue.Value.Key == element) findMinMax = true; } if (mMinValue.HasValue == false || mMinValue.Value.Value > value) { mMinValue = new KeyValuePair(element, value); res = true; } else { if (mMinValue.Value.Key == element) findMinMax = true; } if (findMinMax) { FindMinMaxValue(); res = true; } return res; } private void InnerSetValue(ChartDataColumn column, ChartDataRow row, double amount) { EnsureRawData(); int columnIndex, rowIndex; if (mChartDataToIndex.TryGetValue(column, out columnIndex) == false) throw new ChartException("value cannot be set"); // should never happen if (mChartDataToIndex.TryGetValue(row, out rowIndex) == false) throw new ChartException("value cannot be set"); // should never happen mRawData[rowIndex, columnIndex] = amount; KeyElement elem = new KeyElement(row, column); double oldValue; if (mData.TryGetValue(elem, out oldValue) == false) oldValue = 0.0; mData[elem] = amount; bool minMaxChanged = VerifyMinMaxValue(elem, amount); if (mSuspendEvents == false) OnDataValueChanged(new DataValueChangedEventArgs(rowIndex,columnIndex, 0.0,amount, minMaxChanged)); else mFireEvent = true; } private double InnerGetValue(ChartDataColumn column,ChartDataRow row) { KeyElement elem = new KeyElement(row, column); double res; if (mData.TryGetValue(elem, out res) == false) return 0.0; return res; } /// /// Get the value currently for the specified column and row /// /// the name of the column /// the name of the row /// public double GetValue(String ColumnName,String RowName) { ChartDataColumn column = mColumns[ColumnName]; ChartDataRow row = mRows[RowName]; return InnerGetValue(column, row); } /// /// Get the value currently for the specified column and row /// /// the name of the column /// the index of the row /// public double GetValue(String ColumnName, int rowIndex) { ChartDataColumn column = mColumns[ColumnName]; ChartDataRow row = mRows[rowIndex]; return InnerGetValue(column, row); } /// /// Get the value currently for the specified column and row /// /// the index of the column /// the index of the row /// public double GetValue(int columnIndex, int rowIndex) { ChartDataColumn column = mColumns[columnIndex]; ChartDataRow row = mRows[rowIndex]; return InnerGetValue(column, row); } public void AddLabel(string columnName,int rowIndex,string text) { } /// /// Sets the value for the specified column and row /// /// the name of the column /// the name of the row /// the new value public void SetValue(string ColumnName, string RowName, double amount) { ChartDataColumn column = mColumns[ColumnName]; ChartDataRow row = mRows[RowName]; InnerSetValue(column, row, amount); } /// /// Sets the value for the specified column and row. /// /// the name of the column /// the index of the row /// the new value public void SetValue(String ColumnName, int rowIndex, double amount) { ChartDataColumn column = mColumns[ColumnName]; ChartDataRow row = mRows[rowIndex]; InnerSetValue(column, row, amount); } /// /// Sets the value for the specified column and row. /// /// the index of the column /// the index of the row /// the new value public void SetValue(int columnIndex, int rowIndex, double amount) { ChartDataColumn column = mColumns[columnIndex]; ChartDataRow row = mRows[rowIndex]; InnerSetValue(column, row, amount); } } }