using System; using System.Collections.Generic; using UnityEngine; namespace XCharts.Runtime { /// /// The axis in rectangular coordinate. /// |直角坐标系的坐标轴组件。 /// [System.Serializable] public class Axis : MainComponent { /// /// the type of axis. /// |坐标轴类型。 /// public enum AxisType { /// /// Numerical axis, suitable for continuous data. /// ||数值轴。适用于连续数据。 /// Value, /// /// Category axis, suitable for discrete category data. Data should only be set via data for this type. /// ||类目轴。适用于离散的类目数据,为该类型时必须通过 data 设置类目数据。 /// Category, /// /// Log axis, suitable for log data. /// |对数轴。适用于对数数据。 /// Log, /// /// Time axis, suitable for continuous time series data. /// |时间轴。适用于连续的时序数据。 /// Time } /// /// the type of axis min and max value. /// |坐标轴最大最小刻度显示类型。 /// public enum AxisMinMaxType { /// /// 0 - maximum. /// |0-最大值。 /// Default, /// /// minimum - maximum. /// |最小值-最大值。 /// MinMax, /// /// Customize the minimum and maximum. /// |自定义最小值最大值。 /// Custom } /// /// the position of axis in grid. /// |坐标轴在Grid中的位置 /// public enum AxisPosition { Left, Right, Bottom, Top } [SerializeField] protected bool m_Show = true; [SerializeField] protected AxisType m_Type; [SerializeField] protected AxisMinMaxType m_MinMaxType; [SerializeField] protected int m_GridIndex; [SerializeField] protected int m_PolarIndex; [SerializeField] protected int m_ParallelIndex; [SerializeField] protected AxisPosition m_Position; [SerializeField] protected float m_Offset; [SerializeField] protected double m_Min; [SerializeField] protected double m_Max; [SerializeField] protected int m_SplitNumber = 0; [SerializeField] protected double m_Interval = 0; [SerializeField] protected bool m_BoundaryGap = true; [SerializeField] protected int m_MaxCache = 0; [SerializeField] protected float m_LogBase = 10; [SerializeField] protected bool m_LogBaseE = false; [SerializeField] protected int m_CeilRate = 0; [SerializeField] protected bool m_Inverse = false; [SerializeField] private bool m_Clockwise = true; [SerializeField] private bool m_InsertDataToHead; [SerializeField] protected List m_Icons = new List(); [SerializeField] protected List m_Data = new List(); [SerializeField] protected AxisLine m_AxisLine = AxisLine.defaultAxisLine; [SerializeField] protected AxisName m_AxisName = AxisName.defaultAxisName; [SerializeField] protected AxisTick m_AxisTick = AxisTick.defaultTick; [SerializeField] protected AxisLabel m_AxisLabel = AxisLabel.defaultAxisLabel; [SerializeField] protected AxisSplitLine m_SplitLine = AxisSplitLine.defaultSplitLine; [SerializeField] protected AxisSplitArea m_SplitArea = AxisSplitArea.defaultSplitArea; public AxisContext context = new AxisContext(); /// /// Whether to show axis. /// |是否显示坐标轴。 /// public bool show { get { return m_Show; } set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetAllDirty(); } } /// /// the type of axis. /// |坐标轴类型。 /// public AxisType type { get { return m_Type; } set { if (PropertyUtil.SetStruct(ref m_Type, value)) SetAllDirty(); } } /// /// the type of axis minmax. /// |坐标轴刻度最大最小值显示类型。 /// public AxisMinMaxType minMaxType { get { return m_MinMaxType; } set { if (PropertyUtil.SetStruct(ref m_MinMaxType, value)) SetAllDirty(); } } /// /// The index of the grid on which the axis are located, by default, is in the first grid. /// |坐标轴所在的 grid 的索引,默认位于第一个 grid。 /// public int gridIndex { get { return m_GridIndex; } set { if (PropertyUtil.SetStruct(ref m_GridIndex, value)) SetAllDirty(); } } /// /// The index of the polar on which the axis are located, by default, is in the first polar. /// |坐标轴所在的 ploar 的索引,默认位于第一个 polar。 /// public int polarIndex { get { return m_PolarIndex; } set { if (PropertyUtil.SetStruct(ref m_PolarIndex, value)) SetAllDirty(); } } /// /// The index of the parallel on which the axis are located, by default, is in the first parallel. /// |坐标轴所在的 parallel 的索引,默认位于第一个 parallel。 /// public int parallelIndex { get { return m_ParallelIndex; } set { if (PropertyUtil.SetStruct(ref m_ParallelIndex, value)) SetAllDirty(); } } /// /// the position of axis in grid. /// |坐标轴在Grid中的位置。 /// public AxisPosition position { get { return m_Position; } set { if (PropertyUtil.SetStruct(ref m_Position, value)) SetAllDirty(); } } /// /// the offset of axis from the default position. Useful when the same position has multiple axes. /// |坐标轴相对默认位置的偏移。在相同position有多个坐标轴时有用。 /// public float offset { get { return m_Offset; } set { if (PropertyUtil.SetStruct(ref m_Offset, value)) SetAllDirty(); } } /// /// The minimun value of axis.Valid when `minMaxType` is `Custom` /// |设定的坐标轴刻度最小值,当minMaxType为Custom时有效。 /// public double min { get { return m_Min; } set { if (PropertyUtil.SetStruct(ref m_Min, value)) SetAllDirty(); } } /// /// The maximum value of axis.Valid when `minMaxType` is `Custom` /// |设定的坐标轴刻度最大值,当minMaxType为Custom时有效。 /// public double max { get { return m_Max; } set { if (PropertyUtil.SetStruct(ref m_Max, value)) SetAllDirty(); } } /// /// Number of segments that the axis is split into. /// |坐标轴的期望的分割段数。默认为0表示自动分割。 /// public int splitNumber { get { return m_SplitNumber; } set { if (PropertyUtil.SetStruct(ref m_SplitNumber, value)) SetAllDirty(); } } /// /// Compulsively set segmentation interval for axis.This is unavailable for category axis. /// |强制设置坐标轴分割间隔。无法在类目轴中使用。 /// public double interval { get { return m_Interval; } set { if (PropertyUtil.SetStruct(ref m_Interval, value)) SetAllDirty(); } } /// /// The boundary gap on both sides of a coordinate axis, which is valid only for category axis with type: 'Category'. /// |坐标轴两边是否留白。只对类目轴有效。 /// public bool boundaryGap { get { return IsCategory() ? m_BoundaryGap : false; } set { if (PropertyUtil.SetStruct(ref m_BoundaryGap, value)) SetAllDirty(); } } /// /// Base of logarithm, which is valid only for numeric axes with type: 'Log'. /// |对数轴的底数,只在对数轴(type:'Log')中有效。 /// public float logBase { get { return m_LogBase; } set { if (value <= 0 || value == 1) value = 10; if (PropertyUtil.SetStruct(ref m_LogBase, value)) SetAllDirty(); } } /// /// On the log axis, if base e is the natural number, and is true, logBase fails. /// |对数轴是否以自然数 e 为底数,为 true 时 logBase 失效。 /// public bool logBaseE { get { return m_LogBaseE; } set { if (PropertyUtil.SetStruct(ref m_LogBaseE, value)) SetAllDirty(); } } /// /// The max number of axis data cache. /// |The first data will be remove when the size of axis data is larger then maxCache. /// |可缓存的最大数据量。默认为0没有限制,大于0时超过指定值会移除旧数据再插入新数据。 /// public int maxCache { get { return m_MaxCache; } set { if (PropertyUtil.SetStruct(ref m_MaxCache, value < 0 ? 0 : value)) SetAllDirty(); } } /// /// The ratio of maximum and minimum values rounded upward. The default is 0, which is automatically calculated. /// |最大最小值向上取整的倍率。默认为0时自动计算。 /// public int ceilRate { get { return m_CeilRate; } set { if (PropertyUtil.SetStruct(ref m_CeilRate, value < 0 ? 0 : value)) SetAllDirty(); } } /// /// Whether the axis are reversed or not. Invalid in `Category` axis. /// |是否反向坐标轴。在类目轴中无效。 /// public bool inverse { get { return m_Inverse; } set { if (m_Type == AxisType.Value && PropertyUtil.SetStruct(ref m_Inverse, value)) SetAllDirty(); } } /// /// Whether the positive position of axis is in clockwise. True for clockwise by default. /// |刻度增长是否按顺时针,默认顺时针。 /// public bool clockwise { get { return m_Clockwise; } set { if (PropertyUtil.SetStruct(ref m_Clockwise, value)) SetAllDirty(); } } /// /// Category data, available in type: 'Category' axis. /// |类目数据,在类目轴(type: 'category')中有效。 /// public List data { get { return m_Data; } set { if (value != null) { m_Data = value; SetAllDirty(); } } } /// /// 类目数据对应的图标。 /// public List icons { get { return m_Icons; } set { if (value != null) { m_Icons = value; SetAllDirty(); } } } /// /// axis Line. /// |坐标轴轴线。 /// public AxisLine axisLine { get { return m_AxisLine; } set { if (value != null) { m_AxisLine = value; SetVerticesDirty(); } } } /// /// axis name. /// |坐标轴名称。 /// public AxisName axisName { get { return m_AxisName; } set { if (value != null) { m_AxisName = value; SetComponentDirty(); } } } /// /// axis tick. /// |坐标轴刻度。 /// public AxisTick axisTick { get { return m_AxisTick; } set { if (value != null) { m_AxisTick = value; SetVerticesDirty(); } } } /// /// axis label. /// |坐标轴刻度标签。 /// public AxisLabel axisLabel { get { return m_AxisLabel; } set { if (value != null) { m_AxisLabel = value; SetComponentDirty(); } } } /// /// axis split line. /// |坐标轴分割线。 /// public AxisSplitLine splitLine { get { return m_SplitLine; } set { if (value != null) { m_SplitLine = value; SetVerticesDirty(); } } } /// /// axis split area. /// |坐标轴分割区域。 /// public AxisSplitArea splitArea { get { return m_SplitArea; } set { if (value != null) { m_SplitArea = value; SetVerticesDirty(); } } } /// /// Whether to add new data at the head or at the end of the list. /// |添加新数据时是在列表的头部还是尾部加入。 /// public bool insertDataToHead { get { return m_InsertDataToHead; } set { if (PropertyUtil.SetStruct(ref m_InsertDataToHead, value)) SetAllDirty(); } } public override bool vertsDirty { get { return m_VertsDirty || axisLine.anyDirty || axisTick.anyDirty || splitLine.anyDirty || splitArea.anyDirty; } } public override bool componentDirty { get { return m_ComponentDirty || axisName.anyDirty || axisLabel.anyDirty; } } public override void ClearComponentDirty() { base.ClearComponentDirty(); axisName.ClearComponentDirty(); axisLabel.ClearComponentDirty(); } public override void ClearVerticesDirty() { base.ClearVerticesDirty(); axisLine.ClearVerticesDirty(); axisTick.ClearVerticesDirty(); splitLine.ClearVerticesDirty(); splitArea.ClearVerticesDirty(); } public override void SetComponentDirty() { context.isNeedUpdateFilterData = true; base.SetComponentDirty(); } public Axis Clone() { var axis = new Axis(); axis.show = show; axis.type = type; axis.gridIndex = 0; axis.minMaxType = minMaxType; axis.min = min; axis.max = max; axis.splitNumber = splitNumber; axis.interval = interval; axis.boundaryGap = boundaryGap; axis.maxCache = maxCache; axis.logBase = logBase; axis.logBaseE = logBaseE; axis.ceilRate = ceilRate; axis.insertDataToHead = insertDataToHead; axis.axisLine = axisLine.Clone(); axis.axisName = axisName.Clone(); axis.axisTick = axisTick.Clone(); axis.axisLabel = axisLabel.Clone(); axis.splitLine = splitLine.Clone(); axis.splitArea = splitArea.Clone(); axis.icons = new List(); axis.data = new List(); ChartHelper.CopyList(axis.data, data); return axis; } public void Copy(Axis axis) { show = axis.show; type = axis.type; minMaxType = axis.minMaxType; gridIndex = axis.gridIndex; min = axis.min; max = axis.max; splitNumber = axis.splitNumber; interval = axis.interval; boundaryGap = axis.boundaryGap; maxCache = axis.maxCache; logBase = axis.logBase; logBaseE = axis.logBaseE; ceilRate = axis.ceilRate; insertDataToHead = axis.insertDataToHead; axisLine.Copy(axis.axisLine); axisName.Copy(axis.axisName); axisTick.Copy(axis.axisTick); axisLabel.Copy(axis.axisLabel); splitLine.Copy(axis.splitLine); splitArea.Copy(axis.splitArea); ChartHelper.CopyList(data, axis.data); ChartHelper.CopyList(icons, axis.icons); } /// /// 清空类目数据 /// public override void ClearData() { m_Data.Clear(); m_Icons.Clear(); context.Clear(); SetAllDirty(); } /// /// 是否为类目轴。 /// /// public bool IsCategory() { return m_Type == AxisType.Category; } /// /// 是否为数值轴。 /// /// public bool IsValue() { return m_Type == AxisType.Value; } /// /// 是否为对数轴。 /// /// public bool IsLog() { return m_Type == AxisType.Log; } /// /// 是否为时间轴。 /// public bool IsTime() { return m_Type == AxisType.Time; } public bool IsLeft() { return m_Position == AxisPosition.Left; } public bool IsRight() { return m_Position == AxisPosition.Right; } public bool IsTop() { return m_Position == AxisPosition.Top; } public bool IsBottom() { return m_Position == AxisPosition.Bottom; } public void SetNeedUpdateFilterData() { context.isNeedUpdateFilterData = true; } /// /// 添加一个类目到类目数据列表 /// /// public void AddData(string category) { if (maxCache > 0) { while (m_Data.Count >= maxCache) { RemoveData(m_InsertDataToHead ? m_Data.Count - 1 : 0); } } if (m_InsertDataToHead) m_Data.Insert(0, category); else m_Data.Add(category); SetAllDirty(); } public void RemoveData(int dataIndex) { context.isNeedUpdateFilterData = true; m_Data.RemoveAt(dataIndex); } /// /// 更新类目数据 /// /// /// public void UpdateData(int index, string category) { if (index >= 0 && index < m_Data.Count) { m_Data[index] = category; SetComponentDirty(); } } /// /// 添加图标 /// /// public void AddIcon(Sprite icon) { if (maxCache > 0) { while (m_Icons.Count > maxCache) { m_Icons.RemoveAt(m_InsertDataToHead ? m_Icons.Count - 1 : 0); } } if (m_InsertDataToHead) m_Icons.Insert(0, icon); else m_Icons.Add(icon); SetAllDirty(); } /// /// 更新图标 /// /// /// public void UpdateIcon(int index, Sprite icon) { if (index >= 0 && index < m_Icons.Count) { m_Icons[index] = icon; SetComponentDirty(); } } /// /// 获得指定索引的类目数据 /// /// /// public string GetData(int index) { if (index >= 0 && index < m_Data.Count) return m_Data[index]; else return null; } /// /// 获得在dataZoom范围内指定索引的类目数据 /// /// 类目数据索引 /// 区域缩放 /// public string GetData(int index, DataZoom dataZoom) { var showData = GetDataList(dataZoom); if (index >= 0 && index < showData.Count) return showData[index]; else return ""; } public Sprite GetIcon(int index) { if (index >= 0 && index < m_Icons.Count) return m_Icons[index]; else return null; } /// /// 获得值在坐标轴上的距离 /// /// /// /// public float GetDistance(double value, float axisLength) { if (context.minMaxRange == 0) return 0; if (IsCategory() && boundaryGap) { var each = axisLength / data.Count; return (float) (each * (value + 0.5f)); } else { return axisLength * (float) ((value - context.minValue) / context.minMaxRange); } } /// /// 获得指定区域缩放的类目数据列表 /// /// 区域缩放 /// internal List GetDataList(DataZoom dataZoom) { if (dataZoom != null && dataZoom.enable && dataZoom.IsContainsAxis(this)) { UpdateFilterData(dataZoom); return context.filterData; } else { return m_Data.Count > 0 ? m_Data : context.runtimeData; } } internal List GetDataList() { return m_Data.Count > 0 ? m_Data : context.runtimeData; } /// /// 更新dataZoom对应的类目数据列表 /// /// internal void UpdateFilterData(DataZoom dataZoom) { if (dataZoom != null && dataZoom.enable && dataZoom.IsContainsAxis(this)) { var data = GetDataList(); context.UpdateFilterData(data, dataZoom); } } /// /// 获得类目数据个数 /// /// /// internal int GetDataCount(DataZoom dataZoom) { return IsCategory() ? GetDataList(dataZoom).Count : 0; } /// /// 更新刻度标签文字 /// /// internal void UpdateLabelText(float coordinateWidth, DataZoom dataZoom, bool forcePercent) { for (int i = 0; i < context.labelObjectList.Count; i++) { if (context.labelObjectList[i] != null) { var text = AxisHelper.GetLabelName(this, coordinateWidth, i, context.minValue, context.maxValue, dataZoom, forcePercent); context.labelObjectList[i].SetText(text); } } } internal Vector3 GetLabelObjectPosition(int index) { if (context.labelObjectList != null && index < context.labelObjectList.Count) return context.labelObjectList[index].GetPosition(); else return Vector3.zero; } internal void UpdateMinMaxValue(double minValue, double maxValue) { context.minValue = minValue; context.maxValue = maxValue; double tempRange = maxValue - minValue; if (context.minMaxRange != tempRange) { context.minMaxRange = tempRange; if (type == Axis.AxisType.Value && interval > 0) { SetComponentDirty(); } } } public float GetLogValue(double value) { if (value <= 0 || value == 1) return 0; else return logBaseE ? (float) Math.Log(value) : (float) Math.Log(value, logBase); } public int GetLogMinIndex() { return logBaseE ? (int) Math.Log(context.minValue) : (int) Math.Log(context.minValue, logBase); } public int GetLogMaxIndex() { return logBaseE ? (int) Math.Log(context.maxValue) : (int) Math.Log(context.maxValue, logBase); } public double GetLabelValue(int index) { if (index < 0) return context.minValue; else if (index > context.labelValueList.Count - 1) return context.maxValue; else return context.labelValueList[index]; } public double GetLastLabelValue() { if (context.labelValueList.Count > 0) return context.labelValueList[context.labelValueList.Count - 1]; else return 0; } } }