mirror of
https://github.com/XCharts-Team/XCharts.git
synced 2026-06-18 08:53:42 +00:00
Compare commits
23 Commits
master
...
3deda8d9cc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3deda8d9cc | ||
|
|
b9c72c8cb9 | ||
|
|
d76b474a61 | ||
|
|
2acb841d2e | ||
|
|
8af1796ff8 | ||
|
|
78a07aa2ae | ||
|
|
fe867d18e6 | ||
|
|
757ccb04fb | ||
|
|
b4cb62241f | ||
|
|
0fb1cab302 | ||
|
|
ead9034870 | ||
|
|
20c87265a5 | ||
|
|
1e31cf3010 | ||
|
|
d33e2f66ef | ||
|
|
ff4fbb2176 | ||
|
|
9fc3ff862f | ||
|
|
bc31f86fcf | ||
|
|
c6412f5d78 | ||
|
|
0e60a26333 | ||
|
|
59bb60950b | ||
|
|
b3320bd2cd | ||
|
|
71cfbc15f3 | ||
|
|
8fbda1fa73 |
@@ -81,25 +81,6 @@ slug: /changelog
|
|||||||
|
|
||||||
## master
|
## master
|
||||||
|
|
||||||
* (2026.06.03) 修复`GetMinMaxData`在`showDataDimension`未初始化时返回异常的问题 (#365)
|
|
||||||
* (2026.06.03) 增加`Tooltip`通过`TitleLabelStyle`的`TextStyle`的`Alignment`设置标题的对齐方式 (#363)
|
|
||||||
* (2026.05.25) 增加`DataZoom`的`filterAxisRange`设置坐标轴的范围计算是否受`DataZoom`的影响
|
|
||||||
* (2026.05.23) 优化`DataZoom`的`Marquee`框选功能
|
|
||||||
* (2026.05.23) 修复`DataZoom`内绘制的折线图可能会超出范围的问题
|
|
||||||
* (2026.05.23) 修复`Axis`的`inverse`没能正确反转的问题
|
|
||||||
* (2026.05.23) 增加`LabelStyle`的`showCondition`,`showFilter`,`showThreshold`可控制`label`显示和隐藏
|
|
||||||
* (2026.05.22) 增加`LabelStyle`的`minGap`可避免`label`过于密集
|
|
||||||
* (2026.05.17) 修复`DataZoom`点击时指示区域不准的问题
|
|
||||||
* (2026.05.17) 增加`Legend`的`Width`和`Height`可设置固定宽高
|
|
||||||
* (2026.05.17) 修复`Serie`的`EndLabel`在`Y`轴是`MinMax`类型时显示的数值不对的问题
|
|
||||||
* (2026.05.16) 修复`Candlestick`按昨收判断涨跌颜色,一字涨停/跌停显示不对的问题 (#362)
|
|
||||||
* (2026.03.29) 修复`Legend`的`Background`区域在`Horizonal`模式下不对的问题
|
|
||||||
* (2026.03.25) 增加`Chart`的`Json`导出导入
|
|
||||||
* (2026.03.10) 增加`Sankey`的线条tooltip触发显示
|
|
||||||
* (2026.03.10) 增加`UITable`的排序功能支持
|
|
||||||
* (2026.03.07) 修复`UITable`在尺寸变化时背景没有实时刷新的问题
|
|
||||||
* (2026.03.06) 优化`UITable`支持万级以上数据量不卡顿
|
|
||||||
|
|
||||||
## v3.15.0
|
## v3.15.0
|
||||||
|
|
||||||
版本要点:
|
版本要点:
|
||||||
|
|||||||
@@ -19,8 +19,6 @@ namespace XCharts.Editor
|
|||||||
public static readonly GUIContent btnSaveAsImage = new GUIContent("Save As Image", "");
|
public static readonly GUIContent btnSaveAsImage = new GUIContent("Save As Image", "");
|
||||||
public static readonly GUIContent btnCheckWarning = new GUIContent("Check Warning", "");
|
public static readonly GUIContent btnCheckWarning = new GUIContent("Check Warning", "");
|
||||||
public static readonly GUIContent btnHideWarning = new GUIContent("Hide Warning", "");
|
public static readonly GUIContent btnHideWarning = new GUIContent("Hide Warning", "");
|
||||||
public static readonly GUIContent btnImportJsonData = new GUIContent("Import Json", "");
|
|
||||||
public static readonly GUIContent btnExportJsonData = new GUIContent("Export Json", "");
|
|
||||||
}
|
}
|
||||||
protected BaseChart m_Chart;
|
protected BaseChart m_Chart;
|
||||||
protected SerializedProperty m_Script;
|
protected SerializedProperty m_Script;
|
||||||
@@ -38,7 +36,6 @@ namespace XCharts.Editor
|
|||||||
private bool m_BaseFoldout;
|
private bool m_BaseFoldout;
|
||||||
|
|
||||||
private bool m_CheckWarning = false;
|
private bool m_CheckWarning = false;
|
||||||
private bool m_ExportPending = false;
|
|
||||||
private int m_LastComponentCount = 0;
|
private int m_LastComponentCount = 0;
|
||||||
private int m_LastSerieCount = 0;
|
private int m_LastSerieCount = 0;
|
||||||
private string m_VersionString = "";
|
private string m_VersionString = "";
|
||||||
@@ -303,14 +300,6 @@ namespace XCharts.Editor
|
|||||||
m_CheckWarning = false;
|
m_CheckWarning = false;
|
||||||
}
|
}
|
||||||
EditorGUILayout.EndHorizontal();
|
EditorGUILayout.EndHorizontal();
|
||||||
if (GUILayout.Button(Styles.btnImportJsonData))
|
|
||||||
{
|
|
||||||
ChartJsonImportWindow.ShowWindow(m_Chart);
|
|
||||||
}
|
|
||||||
if (GUILayout.Button(Styles.btnExportJsonData))
|
|
||||||
{
|
|
||||||
RequestExportJsonData();
|
|
||||||
}
|
|
||||||
sb.Length = 0;
|
sb.Length = 0;
|
||||||
sb.AppendFormat("v{0}", XChartsMgr.fullVersion);
|
sb.AppendFormat("v{0}", XChartsMgr.fullVersion);
|
||||||
if (!string.IsNullOrEmpty(m_Chart.warningInfo))
|
if (!string.IsNullOrEmpty(m_Chart.warningInfo))
|
||||||
@@ -332,47 +321,8 @@ namespace XCharts.Editor
|
|||||||
m_CheckWarning = true;
|
m_CheckWarning = true;
|
||||||
m_Chart.CheckWarning();
|
m_Chart.CheckWarning();
|
||||||
}
|
}
|
||||||
if (GUILayout.Button(Styles.btnImportJsonData))
|
|
||||||
{
|
|
||||||
ChartJsonImportWindow.ShowWindow(m_Chart);
|
|
||||||
}
|
|
||||||
if (GUILayout.Button(Styles.btnExportJsonData))
|
|
||||||
{
|
|
||||||
RequestExportJsonData();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RequestExportJsonData()
|
|
||||||
{
|
|
||||||
if (m_ExportPending) return;
|
|
||||||
m_ExportPending = true;
|
|
||||||
var chart = m_Chart;
|
|
||||||
EditorApplication.delayCall += delegate()
|
|
||||||
{
|
|
||||||
m_ExportPending = false;
|
|
||||||
ExportJsonData(chart);
|
|
||||||
};
|
|
||||||
GUIUtility.ExitGUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ExportJsonData(BaseChart chart)
|
|
||||||
{
|
|
||||||
if (chart == null) return;
|
|
||||||
var json = chart.ExportToJson(true);
|
|
||||||
var defaultName = chart.gameObject.name + ".json";
|
|
||||||
var path = EditorUtility.SaveFilePanel("Save Chart JSON", "", defaultName, "json");
|
|
||||||
if (string.IsNullOrEmpty(path)) return;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
System.IO.File.WriteAllText(path, json);
|
|
||||||
Debug.Log("[XCharts] JSON exported to: " + path);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Debug.LogError("[XCharts] Failed to save JSON: " + ex.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,15 +113,6 @@ namespace XCharts.Editor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void PropertyFlagsField(SerializedProperty prop, string relativePropName, System.Type enumType)
|
|
||||||
{
|
|
||||||
if (IngorePropertys.Contains(relativePropName)) return;
|
|
||||||
if (!ChartEditorHelper.PropertyFlagsField(ref m_DrawRect, m_Heights, m_KeyName, prop, relativePropName, enumType))
|
|
||||||
{
|
|
||||||
Debug.LogError("PropertyFlagsField ERROR:" + prop.displayName + ", " + relativePropName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void PropertyFieldLimitMin(SerializedProperty prop, string relativePropName, float minValue)
|
protected void PropertyFieldLimitMin(SerializedProperty prop, string relativePropName, float minValue)
|
||||||
{
|
{
|
||||||
if (IngorePropertys.Contains(relativePropName)) return;
|
if (IngorePropertys.Contains(relativePropName)) return;
|
||||||
|
|||||||
@@ -26,10 +26,6 @@ namespace XCharts.Editor
|
|||||||
PropertyField(prop, "m_Height");
|
PropertyField(prop, "m_Height");
|
||||||
PropertyField(prop, "m_FixedX");
|
PropertyField(prop, "m_FixedX");
|
||||||
PropertyField(prop, "m_FixedY");
|
PropertyField(prop, "m_FixedY");
|
||||||
PropertyField(prop, "m_ShowCondition");
|
|
||||||
PropertyField(prop, "m_ShowFilter");
|
|
||||||
PropertyField(prop, "m_ShowThreshold");
|
|
||||||
PropertyField(prop, "m_ShowMinGap");
|
|
||||||
PropertyField(prop, "m_Icon");
|
PropertyField(prop, "m_Icon");
|
||||||
PropertyField(prop, "m_Background");
|
PropertyField(prop, "m_Background");
|
||||||
PropertyField(prop, "m_TextStyle");
|
PropertyField(prop, "m_TextStyle");
|
||||||
|
|||||||
@@ -28,11 +28,10 @@ namespace XCharts.Editor
|
|||||||
PropertyField("m_ScrollSensitivity");
|
PropertyField("m_ScrollSensitivity");
|
||||||
PropertyField("m_RangeMode");
|
PropertyField("m_RangeMode");
|
||||||
PropertyField(m_Start);
|
PropertyField(m_Start);
|
||||||
PropertyField("m_StartLock");
|
|
||||||
PropertyField(m_End);
|
PropertyField(m_End);
|
||||||
|
PropertyField("m_StartLock");
|
||||||
PropertyField("m_EndLock");
|
PropertyField("m_EndLock");
|
||||||
PropertyField(m_MinZoomRatio);
|
PropertyField(m_MinZoomRatio);
|
||||||
PropertyField("m_FilterAxisRange");
|
|
||||||
if (m_Start.floatValue < 0) m_Start.floatValue = 0;
|
if (m_Start.floatValue < 0) m_Start.floatValue = 0;
|
||||||
if (m_End.floatValue > 100) m_End.floatValue = 100;
|
if (m_End.floatValue > 100) m_End.floatValue = 100;
|
||||||
if (m_MinZoomRatio.floatValue < 0) m_MinZoomRatio.floatValue = 0;
|
if (m_MinZoomRatio.floatValue < 0) m_MinZoomRatio.floatValue = 0;
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ namespace XCharts.Editor
|
|||||||
{
|
{
|
||||||
++EditorGUI.indentLevel;
|
++EditorGUI.indentLevel;
|
||||||
PropertyField("m_IconType");
|
PropertyField("m_IconType");
|
||||||
PropertyField("m_Width");
|
|
||||||
PropertyField("m_Height");
|
|
||||||
PropertyField("m_ItemWidth");
|
PropertyField("m_ItemWidth");
|
||||||
PropertyField("m_ItemHeight");
|
PropertyField("m_ItemHeight");
|
||||||
PropertyField("m_ItemGap");
|
PropertyField("m_ItemGap");
|
||||||
|
|||||||
@@ -13,13 +13,10 @@ namespace XCharts.Editor
|
|||||||
public static readonly GUIContent btnAddComponent = new GUIContent("Add Main Component", "");
|
public static readonly GUIContent btnAddComponent = new GUIContent("Add Main Component", "");
|
||||||
public static readonly GUIContent btnRebuildChartObject = new GUIContent("Rebuild Object", "");
|
public static readonly GUIContent btnRebuildChartObject = new GUIContent("Rebuild Object", "");
|
||||||
public static readonly GUIContent btnSaveAsImage = new GUIContent("Save As Image", "");
|
public static readonly GUIContent btnSaveAsImage = new GUIContent("Save As Image", "");
|
||||||
public static readonly GUIContent btnImportJsonData = new GUIContent("Import Json", "");
|
|
||||||
public static readonly GUIContent btnExportJsonData = new GUIContent("Export Json", "");
|
|
||||||
public static readonly GUIContent btnCheckWarning = new GUIContent("Check Warning", "");
|
public static readonly GUIContent btnCheckWarning = new GUIContent("Check Warning", "");
|
||||||
public static readonly GUIContent btnHideWarning = new GUIContent("Hide Warning", "");
|
public static readonly GUIContent btnHideWarning = new GUIContent("Hide Warning", "");
|
||||||
}
|
}
|
||||||
public UIComponent m_UIComponent;
|
public UIComponent m_UIComponent;
|
||||||
private bool m_ExportPending;
|
|
||||||
|
|
||||||
public static T AddUIComponent<T>(string chartName) where T : UIComponent
|
public static T AddUIComponent<T>(string chartName) where T : UIComponent
|
||||||
{
|
{
|
||||||
@@ -59,14 +56,6 @@ namespace XCharts.Editor
|
|||||||
{
|
{
|
||||||
m_UIComponent.SaveAsImage("png", "", 4f);
|
m_UIComponent.SaveAsImage("png", "", 4f);
|
||||||
}
|
}
|
||||||
if (GUILayout.Button(Styles.btnImportJsonData))
|
|
||||||
{
|
|
||||||
UIComponentJsonImportWindow.ShowWindow(m_UIComponent);
|
|
||||||
}
|
|
||||||
if (GUILayout.Button(Styles.btnExportJsonData))
|
|
||||||
{
|
|
||||||
RequestExportJsonData();
|
|
||||||
}
|
|
||||||
OnDebugEndInspectorGUI();
|
OnDebugEndInspectorGUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,37 +89,6 @@ namespace XCharts.Editor
|
|||||||
EditorGUILayout.PropertyField(property, title);
|
EditorGUILayout.PropertyField(property, title);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RequestExportJsonData()
|
|
||||||
{
|
|
||||||
if (m_ExportPending) return;
|
|
||||||
m_ExportPending = true;
|
|
||||||
var target = m_UIComponent;
|
|
||||||
EditorApplication.delayCall += delegate ()
|
|
||||||
{
|
|
||||||
m_ExportPending = false;
|
|
||||||
ExportJsonData(target);
|
|
||||||
};
|
|
||||||
GUIUtility.ExitGUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ExportJsonData(UIComponent target)
|
|
||||||
{
|
|
||||||
if (target == null) return;
|
|
||||||
var json = target.ExportToJson(true);
|
|
||||||
var defaultName = target.gameObject.name + ".json";
|
|
||||||
var path = EditorUtility.SaveFilePanel("Save UI Component JSON", "", defaultName, "json");
|
|
||||||
if (string.IsNullOrEmpty(path)) return;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
System.IO.File.WriteAllText(path, json);
|
|
||||||
Debug.Log("[XCharts] UI JSON exported to: " + path);
|
|
||||||
}
|
|
||||||
catch (System.Exception ex)
|
|
||||||
{
|
|
||||||
Debug.LogError("[XCharts] Failed to save UI JSON: " + ex.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void PropertyListField(string relativePropName, bool showOrder = true, params HeaderMenuInfo[] menus)
|
protected void PropertyListField(string relativePropName, bool showOrder = true, params HeaderMenuInfo[] menus)
|
||||||
{
|
{
|
||||||
var m_DrawRect = GUILayoutUtility.GetRect(1f, 17f);
|
var m_DrawRect = GUILayoutUtility.GetRect(1f, 17f);
|
||||||
|
|||||||
@@ -507,28 +507,6 @@ namespace XCharts.Editor
|
|||||||
{
|
{
|
||||||
return PropertyField(ref drawRect, heights, key, parentProp.FindPropertyRelative(relativeName));
|
return PropertyField(ref drawRect, heights, key, parentProp.FindPropertyRelative(relativeName));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool PropertyFlagsField(ref Rect drawRect, Dictionary<string, float> heights, string key,
|
|
||||||
SerializedProperty parentProp, string relativeName, System.Type enumType)
|
|
||||||
{
|
|
||||||
return PropertyFlagsField(ref drawRect, heights, key, parentProp.FindPropertyRelative(relativeName), enumType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool PropertyFlagsField(ref Rect drawRect, Dictionary<string, float> heights, string key,
|
|
||||||
SerializedProperty prop, System.Type enumType)
|
|
||||||
{
|
|
||||||
if (prop == null) return false;
|
|
||||||
var label = GetContent(prop.displayName);
|
|
||||||
var enumValue = (System.Enum)System.Enum.ToObject(enumType, prop.intValue);
|
|
||||||
EditorGUI.BeginChangeCheck();
|
|
||||||
var newValue = EditorGUI.EnumFlagsField(drawRect, label, enumValue);
|
|
||||||
if (EditorGUI.EndChangeCheck())
|
|
||||||
prop.intValue = (int)(object)newValue;
|
|
||||||
var hig = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
|
|
||||||
drawRect.y += hig;
|
|
||||||
heights[key] += hig;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
public static bool PropertyFieldWithMinValue(ref Rect drawRect, Dictionary<string, float> heights, string key,
|
public static bool PropertyFieldWithMinValue(ref Rect drawRect, Dictionary<string, float> heights, string key,
|
||||||
SerializedProperty parentProp, string relativeName, float minValue)
|
SerializedProperty parentProp, string relativeName, float minValue)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,201 +0,0 @@
|
|||||||
using UnityEditor;
|
|
||||||
using UnityEngine;
|
|
||||||
using XCharts.Runtime;
|
|
||||||
|
|
||||||
namespace XCharts.Editor
|
|
||||||
{
|
|
||||||
public class ChartJsonImportWindow : EditorWindow
|
|
||||||
{
|
|
||||||
private const int TEXTAREA_SAFE_CHAR_LIMIT = 8000;
|
|
||||||
private const int LARGE_JSON_PREVIEW_CHAR_LIMIT = 4000;
|
|
||||||
|
|
||||||
private static BaseChart s_TargetChart;
|
|
||||||
private string m_JsonInput = "";
|
|
||||||
private Vector2 m_ScrollPos;
|
|
||||||
private bool m_ShowPreview = false;
|
|
||||||
private string m_PreviewText = "";
|
|
||||||
private bool m_OpenFilePending = false;
|
|
||||||
private bool m_PreviewPending = false;
|
|
||||||
private bool m_ImportPending = false;
|
|
||||||
|
|
||||||
public static void ShowWindow(BaseChart targetChart)
|
|
||||||
{
|
|
||||||
s_TargetChart = targetChart;
|
|
||||||
var window = GetWindow<ChartJsonImportWindow>("Import Chart JSON");
|
|
||||||
window.minSize = new Vector2(600, 400);
|
|
||||||
window.Show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnGUI()
|
|
||||||
{
|
|
||||||
if (s_TargetChart == null)
|
|
||||||
{
|
|
||||||
EditorGUILayout.HelpBox("Target chart is null. Please select a chart first.", MessageType.Error);
|
|
||||||
if (GUILayout.Button("Close")) Close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (m_JsonInput == null) m_JsonInput = "";
|
|
||||||
EditorGUILayout.LabelField("Target Chart: " + s_TargetChart.gameObject.name, EditorStyles.boldLabel);
|
|
||||||
GUILayout.Space(10);
|
|
||||||
EditorGUILayout.LabelField("Paste JSON Data:", EditorStyles.boldLabel);
|
|
||||||
using (var scroll = new EditorGUILayout.ScrollViewScope(m_ScrollPos, GUILayout.Height(250)))
|
|
||||||
{
|
|
||||||
m_ScrollPos = scroll.scrollPosition;
|
|
||||||
if (m_JsonInput.Length <= TEXTAREA_SAFE_CHAR_LIMIT)
|
|
||||||
{
|
|
||||||
m_JsonInput = EditorGUILayout.TextArea(m_JsonInput, GUILayout.ExpandHeight(true), GUILayout.ExpandWidth(true));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var preview = m_JsonInput.Substring(0, LARGE_JSON_PREVIEW_CHAR_LIMIT);
|
|
||||||
EditorGUILayout.HelpBox("JSON content is very large. To avoid editor text rendering limits, only a preview is shown below. Import uses full content.", MessageType.Info);
|
|
||||||
EditorGUILayout.TextArea(preview, GUILayout.ExpandHeight(true), GUILayout.ExpandWidth(true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EditorGUILayout.HelpBox("Paste JSON directly, or click Open Json File.", MessageType.Info);
|
|
||||||
GUILayout.Space(10);
|
|
||||||
using (new EditorGUILayout.HorizontalScope())
|
|
||||||
{
|
|
||||||
if (GUILayout.Button("Open Json File", GUILayout.Width(120)))
|
|
||||||
{
|
|
||||||
RequestOpenJsonFile();
|
|
||||||
}
|
|
||||||
if (GUILayout.Button("Preview", GUILayout.Width(100)))
|
|
||||||
{
|
|
||||||
RequestPreviewJson();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (m_ShowPreview && !string.IsNullOrEmpty(m_PreviewText))
|
|
||||||
{
|
|
||||||
EditorGUILayout.TextArea(m_PreviewText, GUILayout.Height(150));
|
|
||||||
}
|
|
||||||
GUILayout.Space(10);
|
|
||||||
using (new EditorGUILayout.HorizontalScope())
|
|
||||||
{
|
|
||||||
if (GUILayout.Button("Import", GUILayout.Height(40), GUILayout.Width(150)))
|
|
||||||
{
|
|
||||||
RequestImportJson();
|
|
||||||
}
|
|
||||||
if (GUILayout.Button("Cancel", GUILayout.Height(40), GUILayout.Width(150))) Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RequestOpenJsonFile()
|
|
||||||
{
|
|
||||||
if (m_OpenFilePending) return;
|
|
||||||
m_OpenFilePending = true;
|
|
||||||
EditorApplication.delayCall += delegate ()
|
|
||||||
{
|
|
||||||
m_OpenFilePending = false;
|
|
||||||
if (this == null) return;
|
|
||||||
OpenJsonFile();
|
|
||||||
Repaint();
|
|
||||||
};
|
|
||||||
GUIUtility.ExitGUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RequestPreviewJson()
|
|
||||||
{
|
|
||||||
if (m_PreviewPending) return;
|
|
||||||
m_PreviewPending = true;
|
|
||||||
EditorApplication.delayCall += delegate ()
|
|
||||||
{
|
|
||||||
m_PreviewPending = false;
|
|
||||||
if (this == null) return;
|
|
||||||
PreviewJson();
|
|
||||||
Repaint();
|
|
||||||
};
|
|
||||||
GUIUtility.ExitGUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RequestImportJson()
|
|
||||||
{
|
|
||||||
if (m_ImportPending) return;
|
|
||||||
m_ImportPending = true;
|
|
||||||
EditorApplication.delayCall += delegate ()
|
|
||||||
{
|
|
||||||
m_ImportPending = false;
|
|
||||||
if (this == null) return;
|
|
||||||
ImportJson();
|
|
||||||
};
|
|
||||||
GUIUtility.ExitGUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PreviewJson()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(m_JsonInput))
|
|
||||||
{
|
|
||||||
EditorUtility.DisplayDialog("Error", "JSON input is empty.", "OK");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var json = JsonUtility.FromJson<XCharts.Runtime.ChartJson>(m_JsonInput);
|
|
||||||
if (json == null)
|
|
||||||
{
|
|
||||||
m_PreviewText = "Invalid JSON or unsupported schema.";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var componentCount = json.components != null ? json.components.Count : 0;
|
|
||||||
var seriesCount = json.series != null ? json.series.Count : 0;
|
|
||||||
m_PreviewText = "Chart Type: " + json.chartType + "\nComponents: " + componentCount + "\nSeries: " + seriesCount + "\n(Full validation on import)";
|
|
||||||
}
|
|
||||||
m_ShowPreview = true;
|
|
||||||
}
|
|
||||||
catch (System.Exception ex)
|
|
||||||
{
|
|
||||||
EditorUtility.DisplayDialog("Preview Error", "Invalid JSON: " + ex.Message, "OK");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ImportJson()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(m_JsonInput))
|
|
||||||
{
|
|
||||||
EditorUtility.DisplayDialog("Error", "JSON input is empty. Please paste JSON data.", "OK");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Undo.RecordObject(s_TargetChart, "Import Chart JSON");
|
|
||||||
s_TargetChart.ImportFromJson(m_JsonInput);
|
|
||||||
s_TargetChart.RebuildChartObject();
|
|
||||||
s_TargetChart.RefreshAllComponent();
|
|
||||||
s_TargetChart.RefreshChart();
|
|
||||||
EditorUtility.SetDirty(s_TargetChart);
|
|
||||||
UnityEditor.SceneView.RepaintAll();
|
|
||||||
var chart = s_TargetChart;
|
|
||||||
EditorApplication.delayCall += delegate ()
|
|
||||||
{
|
|
||||||
if (chart == null) return;
|
|
||||||
chart.RefreshAllComponent();
|
|
||||||
chart.RefreshChart();
|
|
||||||
UnityEditor.SceneView.RepaintAll();
|
|
||||||
};
|
|
||||||
EditorUtility.DisplayDialog("Success", "Chart '" + s_TargetChart.gameObject.name + "' imported successfully!", "OK");
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
catch (System.Exception ex)
|
|
||||||
{
|
|
||||||
EditorUtility.DisplayDialog("Import Error", "Failed to import JSON:\n" + ex.Message + "\n\n" + ex.StackTrace, "OK");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OpenJsonFile()
|
|
||||||
{
|
|
||||||
var path = EditorUtility.OpenFilePanel("Open Chart JSON", "", "json");
|
|
||||||
if (string.IsNullOrEmpty(path)) return;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_JsonInput = System.IO.File.ReadAllText(path);
|
|
||||||
m_ShowPreview = false;
|
|
||||||
m_PreviewText = "";
|
|
||||||
}
|
|
||||||
catch (System.Exception ex)
|
|
||||||
{
|
|
||||||
EditorUtility.DisplayDialog("Open File Error", "Failed to read JSON file:\n" + ex.Message, "OK");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 21f2eafb07ab34d4abf575784acc56a3
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,193 +0,0 @@
|
|||||||
using UnityEditor;
|
|
||||||
using UnityEngine;
|
|
||||||
using XCharts.Runtime;
|
|
||||||
|
|
||||||
namespace XCharts.Editor
|
|
||||||
{
|
|
||||||
public class UIComponentJsonImportWindow : EditorWindow
|
|
||||||
{
|
|
||||||
private const int TEXTAREA_SAFE_CHAR_LIMIT = 8000;
|
|
||||||
private const int LARGE_JSON_PREVIEW_CHAR_LIMIT = 4000;
|
|
||||||
|
|
||||||
private static UIComponent s_TargetComponent;
|
|
||||||
private string m_JsonInput = "";
|
|
||||||
private Vector2 m_ScrollPos;
|
|
||||||
private bool m_ShowPreview = false;
|
|
||||||
private string m_PreviewText = "";
|
|
||||||
private bool m_OpenFilePending = false;
|
|
||||||
private bool m_PreviewPending = false;
|
|
||||||
private bool m_ImportPending = false;
|
|
||||||
|
|
||||||
public static void ShowWindow(UIComponent target)
|
|
||||||
{
|
|
||||||
s_TargetComponent = target;
|
|
||||||
var window = GetWindow<UIComponentJsonImportWindow>("Import UI JSON");
|
|
||||||
window.minSize = new Vector2(600, 400);
|
|
||||||
window.Show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnGUI()
|
|
||||||
{
|
|
||||||
if (s_TargetComponent == null)
|
|
||||||
{
|
|
||||||
EditorGUILayout.HelpBox("Target UI component is null. Please select a component first.", MessageType.Error);
|
|
||||||
if (GUILayout.Button("Close")) Close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_JsonInput == null) m_JsonInput = "";
|
|
||||||
EditorGUILayout.LabelField("Target: " + s_TargetComponent.gameObject.name + " (" + s_TargetComponent.GetType().Name + ")", EditorStyles.boldLabel);
|
|
||||||
GUILayout.Space(10);
|
|
||||||
EditorGUILayout.LabelField("Paste JSON Data:", EditorStyles.boldLabel);
|
|
||||||
|
|
||||||
using (var scroll = new EditorGUILayout.ScrollViewScope(m_ScrollPos, GUILayout.Height(250)))
|
|
||||||
{
|
|
||||||
m_ScrollPos = scroll.scrollPosition;
|
|
||||||
if (m_JsonInput.Length <= TEXTAREA_SAFE_CHAR_LIMIT)
|
|
||||||
{
|
|
||||||
m_JsonInput = EditorGUILayout.TextArea(m_JsonInput, GUILayout.ExpandHeight(true), GUILayout.ExpandWidth(true));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var preview = m_JsonInput.Substring(0, LARGE_JSON_PREVIEW_CHAR_LIMIT);
|
|
||||||
EditorGUILayout.HelpBox("JSON content is very large. Only a preview is shown below. Import uses full content.", MessageType.Info);
|
|
||||||
EditorGUILayout.TextArea(preview, GUILayout.ExpandHeight(true), GUILayout.ExpandWidth(true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditorGUILayout.HelpBox("Paste JSON directly, or click Open Json File.", MessageType.Info);
|
|
||||||
GUILayout.Space(10);
|
|
||||||
|
|
||||||
using (new EditorGUILayout.HorizontalScope())
|
|
||||||
{
|
|
||||||
if (GUILayout.Button("Open Json File", GUILayout.Width(120))) RequestOpenJsonFile();
|
|
||||||
if (GUILayout.Button("Preview", GUILayout.Width(100))) RequestPreviewJson();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_ShowPreview && !string.IsNullOrEmpty(m_PreviewText))
|
|
||||||
{
|
|
||||||
EditorGUILayout.TextArea(m_PreviewText, GUILayout.Height(120));
|
|
||||||
}
|
|
||||||
|
|
||||||
GUILayout.Space(10);
|
|
||||||
using (new EditorGUILayout.HorizontalScope())
|
|
||||||
{
|
|
||||||
if (GUILayout.Button("Import", GUILayout.Height(40), GUILayout.Width(150))) RequestImportJson();
|
|
||||||
if (GUILayout.Button("Cancel", GUILayout.Height(40), GUILayout.Width(150))) Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RequestOpenJsonFile()
|
|
||||||
{
|
|
||||||
if (m_OpenFilePending) return;
|
|
||||||
m_OpenFilePending = true;
|
|
||||||
EditorApplication.delayCall += delegate ()
|
|
||||||
{
|
|
||||||
m_OpenFilePending = false;
|
|
||||||
if (this == null) return;
|
|
||||||
OpenJsonFile();
|
|
||||||
Repaint();
|
|
||||||
};
|
|
||||||
GUIUtility.ExitGUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RequestPreviewJson()
|
|
||||||
{
|
|
||||||
if (m_PreviewPending) return;
|
|
||||||
m_PreviewPending = true;
|
|
||||||
EditorApplication.delayCall += delegate ()
|
|
||||||
{
|
|
||||||
m_PreviewPending = false;
|
|
||||||
if (this == null) return;
|
|
||||||
PreviewJson();
|
|
||||||
Repaint();
|
|
||||||
};
|
|
||||||
GUIUtility.ExitGUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RequestImportJson()
|
|
||||||
{
|
|
||||||
if (m_ImportPending) return;
|
|
||||||
m_ImportPending = true;
|
|
||||||
EditorApplication.delayCall += delegate ()
|
|
||||||
{
|
|
||||||
m_ImportPending = false;
|
|
||||||
if (this == null) return;
|
|
||||||
ImportJson();
|
|
||||||
};
|
|
||||||
GUIUtility.ExitGUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PreviewJson()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(m_JsonInput))
|
|
||||||
{
|
|
||||||
EditorUtility.DisplayDialog("Error", "JSON input is empty.", "OK");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var json = JsonUtility.FromJson<UIComponentJson>(m_JsonInput);
|
|
||||||
if (json == null)
|
|
||||||
m_PreviewText = "Invalid JSON or unsupported schema.";
|
|
||||||
else
|
|
||||||
m_PreviewText = "Component Type: " + json.componentType + "\nSchema: " + json.schemaVersion + "\nVersion: " + json.componentVersion;
|
|
||||||
m_ShowPreview = true;
|
|
||||||
}
|
|
||||||
catch (System.Exception ex)
|
|
||||||
{
|
|
||||||
EditorUtility.DisplayDialog("Preview Error", "Invalid JSON: " + ex.Message, "OK");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ImportJson()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(m_JsonInput))
|
|
||||||
{
|
|
||||||
EditorUtility.DisplayDialog("Error", "JSON input is empty. Please paste JSON data.", "OK");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Undo.RecordObject(s_TargetComponent, "Import UI Component JSON");
|
|
||||||
s_TargetComponent.ImportFromJson(m_JsonInput);
|
|
||||||
s_TargetComponent.RebuildChartObject();
|
|
||||||
s_TargetComponent.RefreshAllComponent();
|
|
||||||
s_TargetComponent.RefreshGraph();
|
|
||||||
EditorUtility.SetDirty(s_TargetComponent);
|
|
||||||
SceneView.RepaintAll();
|
|
||||||
var target = s_TargetComponent;
|
|
||||||
EditorApplication.delayCall += delegate ()
|
|
||||||
{
|
|
||||||
if (target == null) return;
|
|
||||||
target.RefreshAllComponent();
|
|
||||||
target.RefreshGraph();
|
|
||||||
SceneView.RepaintAll();
|
|
||||||
};
|
|
||||||
EditorUtility.DisplayDialog("Success", "UI component imported successfully!", "OK");
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
catch (System.Exception ex)
|
|
||||||
{
|
|
||||||
EditorUtility.DisplayDialog("Import Error", "Failed to import JSON:\n" + ex.Message + "\n\n" + ex.StackTrace, "OK");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OpenJsonFile()
|
|
||||||
{
|
|
||||||
var path = EditorUtility.OpenFilePanel("Open UI Component JSON", "", "json");
|
|
||||||
if (string.IsNullOrEmpty(path)) return;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_JsonInput = System.IO.File.ReadAllText(path);
|
|
||||||
m_ShowPreview = false;
|
|
||||||
m_PreviewText = "";
|
|
||||||
}
|
|
||||||
catch (System.Exception ex)
|
|
||||||
{
|
|
||||||
EditorUtility.DisplayDialog("Open File Error", "Failed to read JSON file:\n" + ex.Message, "OK");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 68157a6f7d4e94ccc8ccbb4913d187f3
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -103,35 +103,22 @@ namespace XCharts.Runtime
|
|||||||
internal void UpdateFilterData(List<string> data, DataZoom dataZoom)
|
internal void UpdateFilterData(List<string> data, DataZoom dataZoom)
|
||||||
{
|
{
|
||||||
int start = 0, end = 0;
|
int start = 0, end = 0;
|
||||||
// Use (N-1) intervals to match shadow drawing (scaleWid = width/(N-1)).
|
var range = Mathf.RoundToInt(data.Count * (dataZoom.end - dataZoom.start) / 100);
|
||||||
// CeilToInt for start, FloorToInt for end, so filter aligns exactly with filler.
|
if (range <= 0)
|
||||||
int n = data.Count - 1;
|
range = 1;
|
||||||
int startIndex, endIndex;
|
|
||||||
if (n > 0)
|
if (dataZoom.context.invert)
|
||||||
{
|
{
|
||||||
if (dataZoom.context.invert)
|
end = Mathf.RoundToInt(data.Count * dataZoom.end / 100);
|
||||||
{
|
start = end - range;
|
||||||
startIndex = Mathf.CeilToInt((float)n * (100 - dataZoom.end) / 100);
|
if (start < 0) start = 0;
|
||||||
endIndex = Mathf.FloorToInt((float)n * (100 - dataZoom.start) / 100);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
startIndex = Mathf.CeilToInt((float)n * dataZoom.start / 100);
|
|
||||||
endIndex = Mathf.FloorToInt((float)n * dataZoom.end / 100);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
startIndex = 0;
|
start = Mathf.RoundToInt(data.Count * dataZoom.start / 100);
|
||||||
endIndex = 0;
|
end = start + range;
|
||||||
|
if (end > data.Count) end = data.Count;
|
||||||
}
|
}
|
||||||
var range = endIndex - startIndex + 1;
|
|
||||||
if (range <= 0)
|
|
||||||
range = 1;
|
|
||||||
start = startIndex;
|
|
||||||
if (start < 0) start = 0;
|
|
||||||
end = start + range;
|
|
||||||
if (end > data.Count) end = data.Count;
|
|
||||||
|
|
||||||
var minZoomRatio = (int)(data.Count * dataZoom.minZoomRatio);
|
var minZoomRatio = (int)(data.Count * dataZoom.minZoomRatio);
|
||||||
if (start != filterStart ||
|
if (start != filterStart ||
|
||||||
|
|||||||
@@ -122,9 +122,10 @@ namespace XCharts
|
|||||||
{
|
{
|
||||||
if (axis is YAxis)
|
if (axis is YAxis)
|
||||||
{
|
{
|
||||||
var yValue = axis.context.minValue + (chart.pointerPos.y - grid.context.y) / grid.context.height * axis.context.minMaxRange;
|
var yRate = axis.context.minMaxRange / grid.context.height;
|
||||||
if (axis.inverse)
|
var yValue = yRate * (chart.pointerPos.y - grid.context.y - axis.context.offset);
|
||||||
yValue = -yValue;
|
if (axis.context.minValue > 0)
|
||||||
|
yValue += axis.context.minValue;
|
||||||
|
|
||||||
var labelX = axis.GetLabelObjectPosition(0).x;
|
var labelX = axis.GetLabelObjectPosition(0).x;
|
||||||
axis.context.pointerValue = yValue;
|
axis.context.pointerValue = yValue;
|
||||||
@@ -149,9 +150,10 @@ namespace XCharts
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
xValue = axis.context.minValue + (chart.pointerPos.x - grid.context.x) / grid.context.width * axis.context.minMaxRange;
|
var xRate = axis.context.minMaxRange / grid.context.width;
|
||||||
if (axis.inverse)
|
xValue = xRate * (chart.pointerPos.x - grid.context.x - axis.context.offset);
|
||||||
xValue = -xValue;
|
if (axis.context.minValue > 0)
|
||||||
|
xValue += axis.context.minValue;
|
||||||
}
|
}
|
||||||
var labelY = axis.GetLabelObjectPosition(0).y;
|
var labelY = axis.GetLabelObjectPosition(0).y;
|
||||||
axis.context.pointerValue = xValue;
|
axis.context.pointerValue = xValue;
|
||||||
@@ -186,20 +188,15 @@ namespace XCharts
|
|||||||
double tempMinValue;
|
double tempMinValue;
|
||||||
double tempMaxValue;
|
double tempMaxValue;
|
||||||
axis.context.needAnimation = Application.isPlaying && axis.animation.show;
|
axis.context.needAnimation = Application.isPlaying && axis.animation.show;
|
||||||
if (axis.inverse != axis.context.lastCheckInverse)
|
|
||||||
{
|
|
||||||
foreach (var serie in chart.series)
|
|
||||||
serie.context.InvalidateMinMaxCache();
|
|
||||||
}
|
|
||||||
chart.GetSeriesMinMaxValue(axis, axisIndex, out tempMinValue, out tempMaxValue);
|
chart.GetSeriesMinMaxValue(axis, axisIndex, out tempMinValue, out tempMaxValue);
|
||||||
|
|
||||||
var dataZoom = chart.GetDataZoomOfAxis(axis);
|
var dataZoom = chart.GetDataZoomOfAxis(axis);
|
||||||
if (dataZoom != null && dataZoom.enable)
|
if (dataZoom != null && dataZoom.enable)
|
||||||
{
|
{
|
||||||
if (axis is XAxis)
|
if (axis is XAxis)
|
||||||
dataZoom.SetXAxisIndexValueInfo(axisIndex, ref tempMinValue, ref tempMaxValue, axis.inverse);
|
dataZoom.SetXAxisIndexValueInfo(axisIndex, ref tempMinValue, ref tempMaxValue);
|
||||||
else
|
else
|
||||||
dataZoom.SetYAxisIndexValueInfo(axisIndex, ref tempMinValue, ref tempMaxValue, axis.inverse);
|
dataZoom.SetYAxisIndexValueInfo(axisIndex, ref tempMinValue, ref tempMaxValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tempMinValue != axis.context.destMinValue ||
|
if (tempMinValue != axis.context.destMinValue ||
|
||||||
|
|||||||
@@ -495,17 +495,17 @@ namespace XCharts.Runtime
|
|||||||
public static double GetAxisPositionValue(GridCoord grid, Axis axis, Vector3 pos)
|
public static double GetAxisPositionValue(GridCoord grid, Axis axis, Vector3 pos)
|
||||||
{
|
{
|
||||||
if (axis is YAxis)
|
if (axis is YAxis)
|
||||||
return GetAxisPositionValue(pos.y, grid.context.height, axis.context.minMaxRange, grid.context.y, axis.context.offset, axis.context.minValue);
|
return GetAxisPositionValue(pos.y, grid.context.height, axis.context.minMaxRange, grid.context.y, axis.context.offset);
|
||||||
else if (axis is XAxis)
|
else if (axis is XAxis)
|
||||||
return GetAxisPositionValue(pos.x, grid.context.width, axis.context.minMaxRange, grid.context.x, axis.context.offset, axis.context.minValue);
|
return GetAxisPositionValue(pos.x, grid.context.width, axis.context.minMaxRange, grid.context.x, axis.context.offset);
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double GetAxisPositionValue(float xy, float axisLength, double axisRange, float axisStart, float axisOffset, double minValue = 0)
|
public static double GetAxisPositionValue(float xy, float axisLength, double axisRange, float axisStart, float axisOffset)
|
||||||
{
|
{
|
||||||
var yRate = axisRange / axisLength;
|
var yRate = axisRange / axisLength;
|
||||||
return minValue + yRate * (xy - axisStart - axisOffset);
|
return yRate * (xy - axisStart - axisOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace XCharts.Runtime
|
|||||||
[SerializeField] private float m_BorderWidth;
|
[SerializeField] private float m_BorderWidth;
|
||||||
[SerializeField] private Color32 m_BorderColor;
|
[SerializeField] private Color32 m_BorderColor;
|
||||||
[SerializeField] private bool m_RoundedCorner = true;
|
[SerializeField] private bool m_RoundedCorner = true;
|
||||||
[SerializeField] private float[] m_CornerRadius = new float[] { 10, 10, 10, 10 };
|
[SerializeField] private float[] m_CornerRadius = new float[] { 0, 0, 0, 0 };
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// whether the border is visible.
|
/// whether the border is visible.
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace XCharts.Runtime
|
|||||||
[System.Serializable]
|
[System.Serializable]
|
||||||
public class MarqueeStyle : ChildComponent
|
public class MarqueeStyle : ChildComponent
|
||||||
{
|
{
|
||||||
[SerializeField][Since("v3.5.0")] private bool m_Apply = true;
|
[SerializeField][Since("v3.5.0")] private bool m_Apply = false;
|
||||||
[SerializeField][Since("v3.5.0")] private bool m_RealRect = false;
|
[SerializeField][Since("v3.5.0")] private bool m_RealRect = false;
|
||||||
[SerializeField][Since("v3.5.0")] private AreaStyle m_AreaStyle = new AreaStyle();
|
[SerializeField][Since("v3.5.0")] private AreaStyle m_AreaStyle = new AreaStyle();
|
||||||
[SerializeField][Since("v3.5.0")] private LineStyle m_LineStyle = new LineStyle();
|
[SerializeField][Since("v3.5.0")] private LineStyle m_LineStyle = new LineStyle();
|
||||||
@@ -52,7 +52,7 @@ namespace XCharts.Runtime
|
|||||||
/// Custom checkboxes select ongoing callbacks.
|
/// Custom checkboxes select ongoing callbacks.
|
||||||
/// ||自定义选取框选取进行时的回调。
|
/// ||自定义选取框选取进行时的回调。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Action<DataZoom> onGoing { set { m_OnGoing = value; } get { return m_OnGoing; } }
|
public Action<DataZoom> onGoing { set { m_OnStart = value; } get { return m_OnStart; } }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Customize the callback at the end of the selection.
|
/// Customize the callback at the end of the selection.
|
||||||
/// ||自定义选取框结束选取时的回调。
|
/// ||自定义选取框结束选取时的回调。
|
||||||
|
|||||||
@@ -92,7 +92,6 @@ namespace XCharts.Runtime
|
|||||||
[SerializeField][Since("v3.5.0")] private MarqueeStyle m_MarqueeStyle = new MarqueeStyle();
|
[SerializeField][Since("v3.5.0")] private MarqueeStyle m_MarqueeStyle = new MarqueeStyle();
|
||||||
[SerializeField][Since("v3.6.0")] private bool m_StartLock;
|
[SerializeField][Since("v3.6.0")] private bool m_StartLock;
|
||||||
[SerializeField][Since("v3.6.0")] private bool m_EndLock;
|
[SerializeField][Since("v3.6.0")] private bool m_EndLock;
|
||||||
[SerializeField][Since("v3.12.0")] private bool m_FilterAxisRange = true;
|
|
||||||
|
|
||||||
public DataZoomContext context = new DataZoomContext();
|
public DataZoomContext context = new DataZoomContext();
|
||||||
private CustomDataZoomStartEndFunction m_StartEndFunction;
|
private CustomDataZoomStartEndFunction m_StartEndFunction;
|
||||||
@@ -326,16 +325,6 @@ namespace XCharts.Runtime
|
|||||||
set { if (PropertyUtil.SetStruct(ref m_EndLock, value)) SetVerticesDirty(); }
|
set { if (PropertyUtil.SetStruct(ref m_EndLock, value)) SetVerticesDirty(); }
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether dataZoom filters the axis min/max range. When true, the axis scale adapts to the current zoom window.
|
|
||||||
/// When false, the axis always shows the full data range regardless of the zoom position.
|
|
||||||
/// ||是否根据DataZoom的缩放窗口过滤坐标轴的最大最小值范围。为true时坐标轴范围随缩放窗口变化;为false时坐标轴始终显示全部数据范围。
|
|
||||||
/// </summary>
|
|
||||||
public bool filterAxisRange
|
|
||||||
{
|
|
||||||
get { return m_FilterAxisRange; }
|
|
||||||
set { if (PropertyUtil.SetStruct(ref m_FilterAxisRange, value)) SetVerticesDirty(); }
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// The end percentage of the window out of the data extent, in the range of 0 ~ 100.
|
/// The end percentage of the window out of the data extent, in the range of 0 ~ 100.
|
||||||
/// ||数据窗口范围的结束百分比。范围是:0 ~ 100。
|
/// ||数据窗口范围的结束百分比。范围是:0 ~ 100。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -426,7 +415,6 @@ namespace XCharts.Runtime
|
|||||||
public double rawMax;
|
public double rawMax;
|
||||||
public double min;
|
public double min;
|
||||||
public double max;
|
public double max;
|
||||||
public bool isInverse;
|
|
||||||
}
|
}
|
||||||
private Dictionary<int, AxisIndexValueInfo> m_XAxisIndexInfos = new Dictionary<int, AxisIndexValueInfo>();
|
private Dictionary<int, AxisIndexValueInfo> m_XAxisIndexInfos = new Dictionary<int, AxisIndexValueInfo>();
|
||||||
private Dictionary<int, AxisIndexValueInfo> m_YAxisIndexInfos = new Dictionary<int, AxisIndexValueInfo>();
|
private Dictionary<int, AxisIndexValueInfo> m_YAxisIndexInfos = new Dictionary<int, AxisIndexValueInfo>();
|
||||||
@@ -700,7 +688,7 @@ namespace XCharts.Runtime
|
|||||||
context.height = chartHeight - runtimeTop - runtimeBottom;
|
context.height = chartHeight - runtimeTop - runtimeBottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void SetXAxisIndexValueInfo(int xAxisIndex, ref double min, ref double max, bool isInverse = false)
|
internal void SetXAxisIndexValueInfo(int xAxisIndex, ref double min, ref double max)
|
||||||
{
|
{
|
||||||
AxisIndexValueInfo info;
|
AxisIndexValueInfo info;
|
||||||
if (!m_XAxisIndexInfos.TryGetValue(xAxisIndex, out info))
|
if (!m_XAxisIndexInfos.TryGetValue(xAxisIndex, out info))
|
||||||
@@ -710,14 +698,13 @@ namespace XCharts.Runtime
|
|||||||
}
|
}
|
||||||
info.rawMin = min;
|
info.rawMin = min;
|
||||||
info.rawMax = max;
|
info.rawMax = max;
|
||||||
info.isInverse = isInverse;
|
|
||||||
info.min = min + (max - min) * start / 100;
|
info.min = min + (max - min) * start / 100;
|
||||||
info.max = min + (max - min) * end / 100;
|
info.max = min + (max - min) * end / 100;
|
||||||
min = info.min;
|
min = info.min;
|
||||||
max = info.max;
|
max = info.max;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void SetYAxisIndexValueInfo(int yAxisIndex, ref double min, ref double max, bool isInverse = false)
|
internal void SetYAxisIndexValueInfo(int yAxisIndex, ref double min, ref double max)
|
||||||
{
|
{
|
||||||
AxisIndexValueInfo info;
|
AxisIndexValueInfo info;
|
||||||
if (!m_YAxisIndexInfos.TryGetValue(yAxisIndex, out info))
|
if (!m_YAxisIndexInfos.TryGetValue(yAxisIndex, out info))
|
||||||
@@ -727,7 +714,6 @@ namespace XCharts.Runtime
|
|||||||
}
|
}
|
||||||
info.rawMin = min;
|
info.rawMin = min;
|
||||||
info.rawMax = max;
|
info.rawMax = max;
|
||||||
info.isInverse = isInverse;
|
|
||||||
info.min = min + (max - min) * start / 100;
|
info.min = min + (max - min) * start / 100;
|
||||||
info.max = min + (max - min) * end / 100;
|
info.max = min + (max - min) * end / 100;
|
||||||
min = info.min;
|
min = info.min;
|
||||||
@@ -752,14 +738,6 @@ namespace XCharts.Runtime
|
|||||||
var range = info.rawMax - info.rawMin;
|
var range = info.rawMax - info.rawMin;
|
||||||
min = info.rawMin + range * m_Start / 100;
|
min = info.rawMin + range * m_Start / 100;
|
||||||
max = info.rawMin + range * m_End / 100;
|
max = info.rawMin + range * m_End / 100;
|
||||||
if (info.isInverse)
|
|
||||||
{
|
|
||||||
// Internal values are negated; convert back to original for data comparison
|
|
||||||
var originalMin = -max;
|
|
||||||
var originalMax = -min;
|
|
||||||
min = originalMin;
|
|
||||||
max = originalMax;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -775,14 +753,6 @@ namespace XCharts.Runtime
|
|||||||
var range = info.rawMax - info.rawMin;
|
var range = info.rawMax - info.rawMin;
|
||||||
min = info.rawMin + range * m_Start / 100;
|
min = info.rawMin + range * m_Start / 100;
|
||||||
max = info.rawMin + range * m_End / 100;
|
max = info.rawMin + range * m_End / 100;
|
||||||
if (info.isInverse)
|
|
||||||
{
|
|
||||||
// Internal values are negated; convert back to original for data comparison
|
|
||||||
var originalMin = -max;
|
|
||||||
var originalMax = -min;
|
|
||||||
min = originalMin;
|
|
||||||
max = originalMax;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
@@ -20,9 +19,6 @@ namespace XCharts.Runtime
|
|||||||
private float m_DataZoomLastEndIndex;
|
private float m_DataZoomLastEndIndex;
|
||||||
private float m_LastStart;
|
private float m_LastStart;
|
||||||
private float m_LastEnd;
|
private float m_LastEnd;
|
||||||
private List<double> _sampleSumPrefixCache;
|
|
||||||
private int _sampleSumPrefixMaxCount = 0;
|
|
||||||
private bool _sampleSumPrefixInverse = false;
|
|
||||||
|
|
||||||
public override void InitComponent()
|
public override void InitComponent()
|
||||||
{
|
{
|
||||||
@@ -117,7 +113,7 @@ namespace XCharts.Runtime
|
|||||||
dataZoom.context.isCoordinateDrag = true;
|
dataZoom.context.isCoordinateDrag = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (dataZoom.supportMarquee && grid.Contains(pos))
|
if (dataZoom.supportMarquee)
|
||||||
{
|
{
|
||||||
dataZoom.context.isMarqueeDrag = true;
|
dataZoom.context.isMarqueeDrag = true;
|
||||||
dataZoom.context.marqueeStartPos = pos;
|
dataZoom.context.marqueeStartPos = pos;
|
||||||
@@ -167,7 +163,7 @@ namespace XCharts.Runtime
|
|||||||
|
|
||||||
var dataZoom = component;
|
var dataZoom = component;
|
||||||
var grid = chart.GetGridOfDataZoom(dataZoom);
|
var grid = chart.GetGridOfDataZoom(dataZoom);
|
||||||
if (dataZoom.supportMarquee && dataZoom.context.isMarqueeDrag)
|
if (dataZoom.supportMarquee)
|
||||||
{
|
{
|
||||||
Vector2 pos;
|
Vector2 pos;
|
||||||
if (!chart.ScreenPointToChartPoint(eventData.position, out pos))
|
if (!chart.ScreenPointToChartPoint(eventData.position, out pos))
|
||||||
@@ -211,38 +207,16 @@ namespace XCharts.Runtime
|
|||||||
|
|
||||||
var dataZoom = component;
|
var dataZoom = component;
|
||||||
|
|
||||||
if (dataZoom.supportMarquee && dataZoom.context.isMarqueeDrag)
|
if (dataZoom.supportMarquee)
|
||||||
{
|
{
|
||||||
dataZoom.context.isMarqueeDrag = false;
|
dataZoom.context.isMarqueeDrag = false;
|
||||||
if (dataZoom.marqueeStyle.apply)
|
if (dataZoom.marqueeStyle.apply)
|
||||||
{
|
{
|
||||||
var grid = chart.GetGridOfDataZoom(dataZoom);
|
var grid = chart.GetGridOfDataZoom(dataZoom);
|
||||||
var currentRange = dataZoom.end - dataZoom.start;
|
var start = (dataZoom.context.marqueeRect.x - grid.context.x) / grid.context.width * 100;
|
||||||
var startRatio = (dataZoom.context.marqueeRect.x - grid.context.x) / grid.context.width;
|
var end = (dataZoom.context.marqueeRect.x - grid.context.x + dataZoom.context.marqueeRect.width) / grid.context.width * 100;
|
||||||
var endRatio = (dataZoom.context.marqueeRect.x - grid.context.x + dataZoom.context.marqueeRect.width) / grid.context.width;
|
UpdateDataZoomRange(dataZoom, start, end, grid);
|
||||||
var start = dataZoom.start + startRatio * currentRange;
|
|
||||||
var end = dataZoom.start + endRatio * currentRange;
|
|
||||||
if (start > end)
|
|
||||||
{
|
|
||||||
var temp = start;
|
|
||||||
start = end;
|
|
||||||
end = temp;
|
|
||||||
}
|
|
||||||
if (start < 0) start = 0;
|
|
||||||
if (end > 100) end = 100;
|
|
||||||
if (end - start >= 1)
|
|
||||||
{
|
|
||||||
// Bypass minZoomRatio for marquee: the user explicitly selected this region.
|
|
||||||
if (!dataZoom.startLock) dataZoom.start = start;
|
|
||||||
if (!dataZoom.endLock) dataZoom.end = end;
|
|
||||||
m_LastStart = dataZoom.start;
|
|
||||||
m_LastEnd = dataZoom.end;
|
|
||||||
chart.OnDataZoomRangeChanged(dataZoom);
|
|
||||||
chart.RefreshChart();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
dataZoom.context.marqueeRect = Rect.zero;
|
|
||||||
dataZoom.SetVerticesDirty();
|
|
||||||
if (dataZoom.marqueeStyle.onEnd != null)
|
if (dataZoom.marqueeStyle.onEnd != null)
|
||||||
{
|
{
|
||||||
dataZoom.marqueeStyle.onEnd(dataZoom);
|
dataZoom.marqueeStyle.onEnd(dataZoom);
|
||||||
@@ -273,55 +247,30 @@ namespace XCharts.Runtime
|
|||||||
var dataZoom = component;
|
var dataZoom = component;
|
||||||
var grid = chart.GetGridOfDataZoom(dataZoom);
|
var grid = chart.GetGridOfDataZoom(dataZoom);
|
||||||
if (dataZoom.IsInStartZoom(localPos) ||
|
if (dataZoom.IsInStartZoom(localPos) ||
|
||||||
dataZoom.IsInEndZoom(localPos) ||
|
dataZoom.IsInEndZoom(localPos))
|
||||||
dataZoom.IsInSelectedZoom(localPos))
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dataZoom.IsInZoom(localPos))
|
if (dataZoom.IsInZoom(localPos) &&
|
||||||
|
!dataZoom.IsInSelectedZoom(localPos))
|
||||||
{
|
{
|
||||||
var start = dataZoom.start;
|
var pointerX = localPos.x;
|
||||||
var end = dataZoom.end;
|
var selectWidth = grid.context.width * (dataZoom.end - dataZoom.start) / 100;
|
||||||
switch (dataZoom.orient)
|
var startX = pointerX - selectWidth / 2;
|
||||||
|
var endX = pointerX + selectWidth / 2;
|
||||||
|
if (startX < grid.context.x)
|
||||||
{
|
{
|
||||||
case Orient.Horizonal:
|
startX = grid.context.x;
|
||||||
var pointerX = localPos.x;
|
endX = grid.context.x + selectWidth;
|
||||||
var selectWidth = dataZoom.context.width * (dataZoom.end - dataZoom.start) / 100;
|
|
||||||
var startX = pointerX - selectWidth / 2;
|
|
||||||
var endX = pointerX + selectWidth / 2;
|
|
||||||
if (startX < dataZoom.context.x)
|
|
||||||
{
|
|
||||||
startX = dataZoom.context.x;
|
|
||||||
endX = dataZoom.context.x + selectWidth;
|
|
||||||
}
|
|
||||||
else if (endX > dataZoom.context.x + dataZoom.context.width)
|
|
||||||
{
|
|
||||||
endX = dataZoom.context.x + dataZoom.context.width;
|
|
||||||
startX = dataZoom.context.x + dataZoom.context.width - selectWidth;
|
|
||||||
}
|
|
||||||
start = (startX - dataZoom.context.x) / dataZoom.context.width * 100;
|
|
||||||
end = (endX - dataZoom.context.x) / dataZoom.context.width * 100;
|
|
||||||
break;
|
|
||||||
case Orient.Vertical:
|
|
||||||
var pointerY = localPos.y;
|
|
||||||
var selectHeight = dataZoom.context.height * (dataZoom.end - dataZoom.start) / 100;
|
|
||||||
var startY = pointerY - selectHeight / 2;
|
|
||||||
var endY = pointerY + selectHeight / 2;
|
|
||||||
if (startY < dataZoom.context.y)
|
|
||||||
{
|
|
||||||
startY = dataZoom.context.y;
|
|
||||||
endY = dataZoom.context.y + selectHeight;
|
|
||||||
}
|
|
||||||
else if (endY > dataZoom.context.y + dataZoom.context.height)
|
|
||||||
{
|
|
||||||
endY = dataZoom.context.y + dataZoom.context.height;
|
|
||||||
startY = dataZoom.context.y + dataZoom.context.height - selectHeight;
|
|
||||||
}
|
|
||||||
start = (startY - dataZoom.context.y) / dataZoom.context.height * 100;
|
|
||||||
end = (endY - dataZoom.context.y) / dataZoom.context.height * 100;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
else if (endX > grid.context.x + grid.context.width)
|
||||||
|
{
|
||||||
|
endX = grid.context.x + grid.context.width;
|
||||||
|
startX = grid.context.x + grid.context.width - selectWidth;
|
||||||
|
}
|
||||||
|
var start = (startX - grid.context.x) / grid.context.width * 100;
|
||||||
|
var end = (endX - grid.context.x) / grid.context.width * 100;
|
||||||
UpdateDataZoomRange(dataZoom, start, end, grid);
|
UpdateDataZoomRange(dataZoom, start, end, grid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -345,7 +294,7 @@ namespace XCharts.Runtime
|
|||||||
if ((dataZoom.supportInside && dataZoom.supportInsideScroll && grid.Contains(pos)) ||
|
if ((dataZoom.supportInside && dataZoom.supportInsideScroll && grid.Contains(pos)) ||
|
||||||
dataZoom.IsInZoom(pos))
|
dataZoom.IsInZoom(pos))
|
||||||
{
|
{
|
||||||
ScaleDataZoom(dataZoom, eventData.scrollDelta.y * dataZoom.scrollSensitivity, grid, pos);
|
ScaleDataZoom(dataZoom, eventData.scrollDelta.y * dataZoom.scrollSensitivity, grid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,61 +375,23 @@ namespace XCharts.Runtime
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ScaleDataZoom(DataZoom dataZoom, float delta, GridCoord grid = null, Vector2? mousePos = null)
|
private void ScaleDataZoom(DataZoom dataZoom, float delta, GridCoord grid = null)
|
||||||
{
|
{
|
||||||
if (grid == null) grid = chart.GetGridOfDataZoom(dataZoom);
|
if (grid == null) grid = chart.GetGridOfDataZoom(dataZoom);
|
||||||
var gridRange = dataZoom.orient == Orient.Horizonal ? grid.context.width : grid.context.height;
|
var range = dataZoom.orient == Orient.Horizonal ? grid.context.width : grid.context.height;
|
||||||
var currentRange = dataZoom.end - dataZoom.start;
|
var deltaPercent = Mathf.Abs(delta / range * 100);
|
||||||
if (delta > 0 && currentRange <= 0) return;
|
float start, end;
|
||||||
|
if (delta > 0)
|
||||||
// Mouse position as a fraction [0,1] within the grid.
|
|
||||||
// The grid always maps the current [start,end] window onto its full pixel width,
|
|
||||||
// so this fraction directly equals the mouse's relative position inside the data window.
|
|
||||||
float fraction = 0.5f;
|
|
||||||
if (mousePos.HasValue && gridRange > 0)
|
|
||||||
{
|
{
|
||||||
float raw = dataZoom.orient == Orient.Horizonal
|
if (dataZoom.end <= dataZoom.start) return;
|
||||||
? (mousePos.Value.x - grid.context.x) / gridRange
|
start = dataZoom.start + deltaPercent;
|
||||||
: (mousePos.Value.y - grid.context.y) / gridRange;
|
end = dataZoom.end - deltaPercent;
|
||||||
|
|
||||||
bool isInverse = false;
|
|
||||||
if (dataZoom.orient == Orient.Horizonal && dataZoom.xAxisIndexs.Count > 0)
|
|
||||||
{
|
|
||||||
var xAxis = chart.GetChartComponent<XAxis>(dataZoom.xAxisIndexs[0]);
|
|
||||||
isInverse = xAxis != null && xAxis.inverse;
|
|
||||||
}
|
|
||||||
else if (dataZoom.orient == Orient.Vertical && dataZoom.yAxisIndexs.Count > 0)
|
|
||||||
{
|
|
||||||
var yAxis = chart.GetChartComponent<YAxis>(dataZoom.yAxisIndexs[0]);
|
|
||||||
isInverse = yAxis != null && yAxis.inverse;
|
|
||||||
}
|
|
||||||
fraction = Mathf.Clamp01(isInverse ? 1f - raw : raw);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// The data-space anchor (0~100) under the mouse — must stay at the same fraction after zoom.
|
|
||||||
var anchorPercent = dataZoom.start + fraction * currentRange;
|
|
||||||
|
|
||||||
// New range: proportional to current range for consistent zoom feel at any zoom level.
|
|
||||||
var deltaPercent = Mathf.Abs(delta / gridRange * currentRange);
|
|
||||||
var newRange = delta > 0 ? currentRange - deltaPercent : currentRange + deltaPercent;
|
|
||||||
|
|
||||||
// Place the new window so that anchorPercent stays under the mouse.
|
|
||||||
var start = anchorPercent - fraction * newRange;
|
|
||||||
var end = anchorPercent + (1f - fraction) * newRange;
|
|
||||||
|
|
||||||
// If the window goes out of [0,100], slide it to the boundary while keeping its size,
|
|
||||||
// so the zoom always feels consistent and the window never shrinks on one side only.
|
|
||||||
if (start < 0f)
|
|
||||||
{
|
{
|
||||||
end = Mathf.Min(100f, newRange);
|
start = dataZoom.start - deltaPercent;
|
||||||
start = 0f;
|
end = dataZoom.end + deltaPercent;
|
||||||
}
|
}
|
||||||
else if (end > 100f)
|
|
||||||
{
|
|
||||||
start = Mathf.Max(0f, 100f - newRange);
|
|
||||||
end = 100f;
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateDataZoomRange(dataZoom, start, end, grid);
|
UpdateDataZoomRange(dataZoom, start, end, grid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,11 +411,7 @@ namespace XCharts.Runtime
|
|||||||
if(grid == null) grid = chart.GetGridOfDataZoom(dataZoom);
|
if(grid == null) grid = chart.GetGridOfDataZoom(dataZoom);
|
||||||
var range = dataZoom.orient == Orient.Horizonal ? grid.context.width : grid.context.height;
|
var range = dataZoom.orient == Orient.Horizonal ? grid.context.width : grid.context.height;
|
||||||
var minRange = dataZoom.minZoomRatio * range;
|
var minRange = dataZoom.minZoomRatio * range;
|
||||||
var newRange = end - start;
|
if (end - start < minRange / range * 100)
|
||||||
var currentRange = dataZoom.end - dataZoom.start;
|
|
||||||
// Only block when shrinking the range; always allow expansion so the chart
|
|
||||||
// cannot get permanently locked after a marquee selects a narrow region.
|
|
||||||
if (newRange < minRange / range * 100 && newRange <= currentRange)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -639,26 +546,13 @@ namespace XCharts.Runtime
|
|||||||
Serie serie = chart.series[0];
|
Serie serie = chart.series[0];
|
||||||
Axis axis = chart.GetChartComponent<YAxis>(0);
|
Axis axis = chart.GetChartComponent<YAxis>(0);
|
||||||
var showData = serie.GetDataList(null);
|
var showData = serie.GetDataList(null);
|
||||||
float scaleWid = showData.Count > 1 ? dataZoom.context.width / (showData.Count - 1) : dataZoom.context.width;
|
float scaleWid = dataZoom.context.width / (showData.Count - 1);
|
||||||
Vector3 lp = Vector3.zero;
|
Vector3 lp = Vector3.zero;
|
||||||
Vector3 np = Vector3.zero;
|
Vector3 np = Vector3.zero;
|
||||||
// shadow always shows the full data range, independent of DataZoom window
|
double minValue = 0;
|
||||||
double minValue = SerieHelper.GetMinData(serie, 1, null, axis.inverse);
|
double maxValue = 0;
|
||||||
double maxValue = SerieHelper.GetMaxData(serie, 1, null, axis.inverse);
|
SeriesHelper.GetYMinMaxValue(chart, 0, axis.inverse, out minValue, out maxValue, false, false);
|
||||||
minValue = ChartHelper.GetMinDivisibleValue(minValue, 0);
|
AxisHelper.AdjustMinMaxValue(axis, ref minValue, ref maxValue, true);
|
||||||
maxValue = ChartHelper.GetMaxDivisibleValue(maxValue, 0);
|
|
||||||
double xMinValue = 0;
|
|
||||||
double xMaxValue = 0;
|
|
||||||
bool useXValueForShadow = false;
|
|
||||||
var xAxisIndex = dataZoom.xAxisIndexs.Count > 0 ? dataZoom.xAxisIndexs[0] : 0;
|
|
||||||
var xAxis = chart.GetChartComponent<XAxis>(xAxisIndex);
|
|
||||||
if (xAxis != null && (xAxis.IsValue() || xAxis.IsTime()))
|
|
||||||
{
|
|
||||||
xMinValue = SerieHelper.GetMinData(serie, 0, null, xAxis.inverse);
|
|
||||||
xMaxValue = SerieHelper.GetMaxData(serie, 0, null, xAxis.inverse);
|
|
||||||
AxisHelper.AdjustMinMaxValue(xAxis, ref xMinValue, ref xMaxValue, true);
|
|
||||||
useXValueForShadow = (xMaxValue - xMinValue) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int rate = 1;
|
int rate = 1;
|
||||||
var sampleDist = serie.sampleDist < 2 ? 2 : serie.sampleDist;
|
var sampleDist = serie.sampleDist < 2 ? 2 : serie.sampleDist;
|
||||||
@@ -674,40 +568,12 @@ namespace XCharts.Runtime
|
|||||||
var animationDuration = serie.animation.GetChangeDuration();
|
var animationDuration = serie.animation.GetChangeDuration();
|
||||||
var dataAddDuration = serie.animation.GetAdditionDuration();
|
var dataAddDuration = serie.animation.GetAdditionDuration();
|
||||||
var unscaledTime = serie.animation.unscaledTime;
|
var unscaledTime = serie.animation.unscaledTime;
|
||||||
var useCurrentData = false;
|
|
||||||
List<double> sampleSumPrefix = null;
|
|
||||||
if (serie.animation.enable)
|
|
||||||
{
|
|
||||||
useCurrentData = DataHelper.IsAnyDataChanged(ref showData, serie.minShow, maxCount);
|
|
||||||
dataChanging = useCurrentData;
|
|
||||||
}
|
|
||||||
if (!useCurrentData && rate > 1 &&
|
|
||||||
(serie.sampleType == SampleType.Sum || serie.sampleType == SampleType.Average))
|
|
||||||
{
|
|
||||||
if (_sampleSumPrefixCache == null || _sampleSumPrefixMaxCount != maxCount || _sampleSumPrefixInverse != axis.inverse)
|
|
||||||
{
|
|
||||||
_sampleSumPrefixCache = DataHelper.BuildSampleSumPrefix(ref showData, maxCount, axis.inverse);
|
|
||||||
_sampleSumPrefixMaxCount = maxCount;
|
|
||||||
_sampleSumPrefixInverse = axis.inverse;
|
|
||||||
}
|
|
||||||
sampleSumPrefix = _sampleSumPrefixCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = serie.minShow; i < maxCount; i += rate)
|
for (int i = 0; i < maxCount; i += rate)
|
||||||
{
|
{
|
||||||
double value = DataHelper.SampleValue(ref showData, serie.sampleType, rate, serie.minShow, maxCount, totalAverage, i,
|
double value = DataHelper.SampleValue(ref showData, serie.sampleType, rate, serie.minShow, maxCount, totalAverage, i,
|
||||||
dataAddDuration, animationDuration, ref dataChanging, axis, unscaledTime,
|
dataAddDuration, animationDuration, ref dataChanging, axis, unscaledTime);
|
||||||
useCurrentData, false, sampleSumPrefix);
|
float pX = dataZoom.context.x + i * scaleWid;
|
||||||
float pX;
|
|
||||||
if (useXValueForShadow && i < showData.Count && showData[i].data.Count > 0)
|
|
||||||
{
|
|
||||||
var xVal = (xAxis != null && xAxis.inverse) ? -showData[i].data[0] : showData[i].data[0];
|
|
||||||
pX = dataZoom.context.x + (float)((xVal - xMinValue) / (xMaxValue - xMinValue)) * dataZoom.context.width;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pX = dataZoom.context.x + i * scaleWid;
|
|
||||||
}
|
|
||||||
float dataHig = (float)((maxValue - minValue) == 0 ? 0 :
|
float dataHig = (float)((maxValue - minValue) == 0 ? 0 :
|
||||||
(value - minValue) / (maxValue - minValue) * dataZoom.context.height);
|
(value - minValue) / (maxValue - minValue) * dataZoom.context.height);
|
||||||
np = new Vector3(pX, chart.chartY + dataZoom.bottom + dataHig);
|
np = new Vector3(pX, chart.chartY + dataZoom.bottom + dataHig);
|
||||||
@@ -775,11 +641,11 @@ namespace XCharts.Runtime
|
|||||||
float scaleWid = dataZoom.context.height / (showData.Count - 1);
|
float scaleWid = dataZoom.context.height / (showData.Count - 1);
|
||||||
Vector3 lp = Vector3.zero;
|
Vector3 lp = Vector3.zero;
|
||||||
Vector3 np = Vector3.zero;
|
Vector3 np = Vector3.zero;
|
||||||
double minValue;
|
double minValue = 0;
|
||||||
double maxValue;
|
double maxValue = 0;
|
||||||
SeriesHelper.GetYMinMaxValue(chart, 0, axis.inverse, out minValue, out maxValue);
|
SeriesHelper.GetYMinMaxValue(chart, 0, axis.inverse, out minValue, out maxValue);
|
||||||
minValue = ChartHelper.GetMinDivisibleValue(minValue, 0);
|
AxisHelper.AdjustMinMaxValue(axis, ref minValue, ref maxValue, true);
|
||||||
maxValue = ChartHelper.GetMaxDivisibleValue(maxValue, 0);
|
|
||||||
int rate = 1;
|
int rate = 1;
|
||||||
var sampleDist = serie.sampleDist < 2 ? 2 : serie.sampleDist;
|
var sampleDist = serie.sampleDist < 2 ? 2 : serie.sampleDist;
|
||||||
var maxCount = showData.Count;
|
var maxCount = showData.Count;
|
||||||
@@ -794,30 +660,11 @@ namespace XCharts.Runtime
|
|||||||
var animationDuration = serie.animation.GetChangeDuration();
|
var animationDuration = serie.animation.GetChangeDuration();
|
||||||
var dataAddDuration = serie.animation.GetAdditionDuration();
|
var dataAddDuration = serie.animation.GetAdditionDuration();
|
||||||
var unscaledTime = serie.animation.unscaledTime;
|
var unscaledTime = serie.animation.unscaledTime;
|
||||||
var useCurrentData = false;
|
|
||||||
List<double> sampleSumPrefix = null;
|
|
||||||
if (serie.animation.enable)
|
|
||||||
{
|
|
||||||
useCurrentData = DataHelper.IsAnyDataChanged(ref showData, serie.minShow, maxCount);
|
|
||||||
dataChanging = useCurrentData;
|
|
||||||
}
|
|
||||||
if (!useCurrentData && rate > 1 &&
|
|
||||||
(serie.sampleType == SampleType.Sum || serie.sampleType == SampleType.Average))
|
|
||||||
{
|
|
||||||
if (_sampleSumPrefixCache == null || _sampleSumPrefixMaxCount != maxCount || _sampleSumPrefixInverse != axis.inverse)
|
|
||||||
{
|
|
||||||
_sampleSumPrefixCache = DataHelper.BuildSampleSumPrefix(ref showData, maxCount, axis.inverse);
|
|
||||||
_sampleSumPrefixMaxCount = maxCount;
|
|
||||||
_sampleSumPrefixInverse = axis.inverse;
|
|
||||||
}
|
|
||||||
sampleSumPrefix = _sampleSumPrefixCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = serie.minShow; i < maxCount; i += rate)
|
for (int i = 0; i < maxCount; i += rate)
|
||||||
{
|
{
|
||||||
double value = DataHelper.SampleValue(ref showData, serie.sampleType, rate, serie.minShow, maxCount, totalAverage, i,
|
double value = DataHelper.SampleValue(ref showData, serie.sampleType, rate, serie.minShow, maxCount, totalAverage, i,
|
||||||
dataAddDuration, animationDuration, ref dataChanging, axis, unscaledTime,
|
dataAddDuration, animationDuration, ref dataChanging, axis, unscaledTime);
|
||||||
useCurrentData, false, sampleSumPrefix);
|
|
||||||
float pY = dataZoom.context.y + i * scaleWid;
|
float pY = dataZoom.context.y + i * scaleWid;
|
||||||
float dataHig = (maxValue - minValue) == 0 ? 0 :
|
float dataHig = (maxValue - minValue) == 0 ? 0 :
|
||||||
(float)((value - minValue) / (maxValue - minValue) * dataZoom.context.width);
|
(float)((value - minValue) / (maxValue - minValue) * dataZoom.context.width);
|
||||||
@@ -859,7 +706,7 @@ namespace XCharts.Runtime
|
|||||||
|
|
||||||
private void DrawMarquee(VertexHelper vh, DataZoom dataZoom)
|
private void DrawMarquee(VertexHelper vh, DataZoom dataZoom)
|
||||||
{
|
{
|
||||||
if (!dataZoom.enable || !dataZoom.supportMarquee || !dataZoom.context.isMarqueeDrag)
|
if (!dataZoom.enable || !dataZoom.supportMarquee)
|
||||||
return;
|
return;
|
||||||
var areaColor = dataZoom.marqueeStyle.areaStyle.GetColor(chart.theme.dataZoom.dataAreaColor);
|
var areaColor = dataZoom.marqueeStyle.areaStyle.GetColor(chart.theme.dataZoom.dataAreaColor);
|
||||||
UGL.DrawRectangle(vh, dataZoom.context.marqueeRect, areaColor);
|
UGL.DrawRectangle(vh, dataZoom.context.marqueeRect, areaColor);
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ namespace XCharts.Runtime
|
|||||||
{
|
{
|
||||||
double serieMinValue = 0;
|
double serieMinValue = 0;
|
||||||
double serieMaxValue = 0;
|
double serieMaxValue = 0;
|
||||||
SerieHelper.GetMinMaxData(serie, 0, out serieMinValue, out serieMaxValue);
|
SerieHelper.GetMinMaxData(serie, out serieMinValue, out serieMaxValue, null, 2);
|
||||||
if (serieMinValue < min)
|
if (serieMinValue < min)
|
||||||
min = serieMinValue;
|
min = serieMinValue;
|
||||||
if (serieMaxValue > max)
|
if (serieMaxValue > max)
|
||||||
|
|||||||
@@ -68,50 +68,6 @@ namespace XCharts.Runtime
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
End
|
End
|
||||||
}
|
}
|
||||||
/// <summary>
|
|
||||||
/// The value-based condition for showing label. Controls visibility based on threshold comparison.
|
|
||||||
/// ||标签基于值的显示条件,通过与阈值比较来控制标签的显示。
|
|
||||||
/// </summary>
|
|
||||||
public enum ShowCondition
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Always show label.
|
|
||||||
/// ||总是显示标签。
|
|
||||||
/// </summary>
|
|
||||||
Always,
|
|
||||||
/// <summary>
|
|
||||||
/// Show label when value is greater than showThreshold.
|
|
||||||
/// ||大于showThreshold才显示标签。
|
|
||||||
/// </summary>
|
|
||||||
GreaterThan,
|
|
||||||
/// <summary>
|
|
||||||
/// Show label when value is less than showThreshold.
|
|
||||||
/// ||小于showThreshold才显示标签。
|
|
||||||
/// </summary>
|
|
||||||
LessThan,
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// The data-pattern-based filter for showing label. Controls visibility based on data topology.
|
|
||||||
/// ||标签基于数据形态的显示筛选,通过数据的拓扑特征(波峰/波谷)来控制标签的显示。
|
|
||||||
/// </summary>
|
|
||||||
public enum ShowFilter
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// All data points show label.
|
|
||||||
/// ||所有数据点都显示标签。
|
|
||||||
/// </summary>
|
|
||||||
All,
|
|
||||||
/// <summary>
|
|
||||||
/// Show label when value is at a peak.
|
|
||||||
/// ||波峰才显示标签。
|
|
||||||
/// </summary>
|
|
||||||
Peak,
|
|
||||||
/// <summary>
|
|
||||||
/// Show label when value is at a valley.
|
|
||||||
/// ||波谷才显示标签。
|
|
||||||
/// </summary>
|
|
||||||
Valley
|
|
||||||
}
|
|
||||||
|
|
||||||
[SerializeField] protected bool m_Show = true;
|
[SerializeField] protected bool m_Show = true;
|
||||||
[SerializeField] Position m_Position = Position.Default;
|
[SerializeField] Position m_Position = Position.Default;
|
||||||
@@ -126,10 +82,6 @@ namespace XCharts.Runtime
|
|||||||
[SerializeField] protected float m_Height = 0;
|
[SerializeField] protected float m_Height = 0;
|
||||||
[SerializeField][Since("v3.15.0")] protected float m_FixedX = 0;
|
[SerializeField][Since("v3.15.0")] protected float m_FixedX = 0;
|
||||||
[SerializeField][Since("v3.15.0")] protected float m_FixedY = 0;
|
[SerializeField][Since("v3.15.0")] protected float m_FixedY = 0;
|
||||||
[SerializeField][Since("v3.16.0")] protected ShowCondition m_ShowCondition = ShowCondition.Always;
|
|
||||||
[SerializeField][Since("v3.16.0")] protected ShowFilter m_ShowFilter = ShowFilter.All;
|
|
||||||
[SerializeField][Since("v3.16.0")] protected double m_ShowThreshold = 0;
|
|
||||||
[SerializeField][Since("v3.16.0")] protected float m_ShowMinGap = 0;
|
|
||||||
|
|
||||||
[SerializeField] protected IconStyle m_Icon = new IconStyle();
|
[SerializeField] protected IconStyle m_Icon = new IconStyle();
|
||||||
[SerializeField] protected ImageStyle m_Background = new ImageStyle();
|
[SerializeField] protected ImageStyle m_Background = new ImageStyle();
|
||||||
@@ -148,9 +100,6 @@ namespace XCharts.Runtime
|
|||||||
m_Height = 0;
|
m_Height = 0;
|
||||||
m_NumericFormatter = "";
|
m_NumericFormatter = "";
|
||||||
m_AutoOffset = false;
|
m_AutoOffset = false;
|
||||||
m_ShowCondition = ShowCondition.Always;
|
|
||||||
m_ShowFilter = ShowFilter.All;
|
|
||||||
m_ShowThreshold = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -359,47 +308,6 @@ namespace XCharts.Runtime
|
|||||||
set { if (PropertyUtil.SetStruct(ref m_FixedY, value)) SetComponentDirty(); }
|
set { if (PropertyUtil.SetStruct(ref m_FixedY, value)) SetComponentDirty(); }
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The value-based show condition of label. Default is ShowCondition.Always.
|
|
||||||
/// When set to GreaterThan or LessThan, the label is shown only when the value satisfies the threshold.
|
|
||||||
/// ||标签基于值的显示条件。默认为ShowCondition.Always,总是显示。
|
|
||||||
/// 设为GreaterThan或LessThan时,只有满足showThreshold阈值条件的数据点才显示标签。
|
|
||||||
/// </summary>
|
|
||||||
public ShowCondition showCondition
|
|
||||||
{
|
|
||||||
get { return m_ShowCondition; }
|
|
||||||
set { if (PropertyUtil.SetStruct(ref m_ShowCondition, value)) SetComponentDirty(); }
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// The data-pattern-based show filter of label. Default is ShowFilter.All.
|
|
||||||
/// When set to Peak or Valley, the label is shown only at local maximum or minimum data points.
|
|
||||||
/// ||标签基于数据形态的显示筛选。默认为ShowFilter.All,所有数据点都显示。
|
|
||||||
/// 设为Peak或Valley时,只在局部波峰或波谷的数据点显示标签。
|
|
||||||
/// </summary>
|
|
||||||
public ShowFilter showFilter
|
|
||||||
{
|
|
||||||
get { return m_ShowFilter; }
|
|
||||||
set { if (PropertyUtil.SetStruct(ref m_ShowFilter, value)) SetComponentDirty(); }
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// The threshold for showCondition. When showCondition is GreaterThan or LessThan, only values that satisfy the comparison will show label. Default is 0.
|
|
||||||
/// ||showCondition的阈值。当showCondition为GreaterThan或LessThan时生效,只有满足比较条件的值才显示标签。默认值为0。
|
|
||||||
/// </summary>
|
|
||||||
public double showThreshold
|
|
||||||
{
|
|
||||||
get { return m_ShowThreshold; }
|
|
||||||
set { if (PropertyUtil.SetStruct(ref m_ShowThreshold, value)) SetComponentDirty(); }
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// the gap between label and the previous label. When the distance to the previous label is less than this value,
|
|
||||||
/// the label with smaller y value will be hidden. Default is 0, which means this function is turned off.
|
|
||||||
/// 和上一个标签的最小间距。当和上一个标签的距离小于该值时,隐藏对应y值较小的标签。默认为0不开启该功能。
|
|
||||||
/// </summary>
|
|
||||||
public float showMinGap
|
|
||||||
{
|
|
||||||
get { return m_ShowMinGap; }
|
|
||||||
set { if (PropertyUtil.SetStruct(ref m_ShowMinGap, value)) SetComponentDirty(); }
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// the sytle of background.
|
/// the sytle of background.
|
||||||
/// ||背景图样式。
|
/// ||背景图样式。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -80,8 +80,6 @@ namespace XCharts.Runtime
|
|||||||
[SerializeField] private bool m_ItemAutoColor = true;
|
[SerializeField] private bool m_ItemAutoColor = true;
|
||||||
[SerializeField] private float m_ItemOpacity = 1;
|
[SerializeField] private float m_ItemOpacity = 1;
|
||||||
[SerializeField][Since("v3.15.0")] private float m_ItemInactiveOpacity = 1;
|
[SerializeField][Since("v3.15.0")] private float m_ItemInactiveOpacity = 1;
|
||||||
[SerializeField][Since("v3.16.0")] private float m_Width = 0;
|
|
||||||
[SerializeField][Since("v3.16.0")] private float m_Height = 0;
|
|
||||||
[SerializeField] private string m_Formatter;
|
[SerializeField] private string m_Formatter;
|
||||||
[SerializeField] private LabelStyle m_LabelStyle = new LabelStyle();
|
[SerializeField] private LabelStyle m_LabelStyle = new LabelStyle();
|
||||||
[SerializeField][Since("v3.10.0")] private TextLimit m_TextLimit = new TextLimit();
|
[SerializeField][Since("v3.10.0")] private TextLimit m_TextLimit = new TextLimit();
|
||||||
@@ -204,25 +202,6 @@ namespace XCharts.Runtime
|
|||||||
set { if (PropertyUtil.SetClass(ref m_Formatter, value)) SetComponentDirty(); }
|
set { if (PropertyUtil.SetClass(ref m_Formatter, value)) SetComponentDirty(); }
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// the width of legend component. Default is 0 for auto adapt. When set a value between 0 and 1, it means the percentage relative to chart width and height.
|
|
||||||
/// ||图例组件的宽。默认为0自适应。当设置0-1的值时,表示相对于图表宽高的比例。
|
|
||||||
/// </summary>
|
|
||||||
public float width
|
|
||||||
{
|
|
||||||
get { return m_Width; }
|
|
||||||
set { if (PropertyUtil.SetStruct(ref m_Width, value)) SetComponentDirty(); }
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// the height of legend component. Default is 0 for auto adapt. When set a value between 0 and 1, it means the percentage relative to chart width and height.
|
|
||||||
/// ||图例组件的高。默认为0自适应。当设置0-1的值时,表示相对于图表宽高的比例。
|
|
||||||
/// </summary>
|
|
||||||
/// <value></value>
|
|
||||||
public float height
|
|
||||||
{
|
|
||||||
get { return m_Height; }
|
|
||||||
set { if (PropertyUtil.SetStruct(ref m_Height, value)) SetComponentDirty(); }
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// the style of text.
|
/// the style of text.
|
||||||
/// ||文本样式。
|
/// ||文本样式。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -93,23 +93,10 @@ namespace XCharts.Runtime
|
|||||||
var startY = 0f;
|
var startY = 0f;
|
||||||
var legendMaxWidth = chartWidth - legend.location.runtimeLeft - legend.location.runtimeRight;
|
var legendMaxWidth = chartWidth - legend.location.runtimeLeft - legend.location.runtimeRight;
|
||||||
var legendMaxHeight = chartHeight - legend.location.runtimeTop - legend.location.runtimeBottom;
|
var legendMaxHeight = chartHeight - legend.location.runtimeTop - legend.location.runtimeBottom;
|
||||||
var isVertical = legend.orient == Orient.Vertical;
|
|
||||||
var fixedWidth = legend.width <= 0 ? 0
|
|
||||||
: legend.width < 1 ? chartWidth * legend.width
|
|
||||||
: legend.width;
|
|
||||||
var fixedHeight = legend.height <= 0 ? 0
|
|
||||||
: legend.height < 1 ? chartHeight * legend.height
|
|
||||||
: legend.height;
|
|
||||||
// Horizonal: width constrains layout wrapping; Vertical: height constrains layout wrapping.
|
|
||||||
// The other axis only affects the background size, not the layout.
|
|
||||||
if (!isVertical && fixedWidth > 0) legendMaxWidth = fixedWidth;
|
|
||||||
if (isVertical && fixedHeight > 0) legendMaxHeight = fixedHeight;
|
|
||||||
UpdateLegendWidthAndHeight(legend, legendMaxWidth, legendMaxHeight);
|
UpdateLegendWidthAndHeight(legend, legendMaxWidth, legendMaxHeight);
|
||||||
// Override context size for fixed dimensions (controls background rect size).
|
|
||||||
if (fixedWidth > 0) legend.context.width = fixedWidth;
|
|
||||||
if (fixedHeight > 0) legend.context.height = fixedHeight;
|
|
||||||
var legendRuntimeWidth = legend.context.width;
|
var legendRuntimeWidth = legend.context.width;
|
||||||
var legendRuntimeHeight = legend.context.height;
|
var legendRuntimeHeight = legend.context.height;
|
||||||
|
var isVertical = legend.orient == Orient.Vertical;
|
||||||
switch (legend.location.align)
|
switch (legend.location.align)
|
||||||
{
|
{
|
||||||
case Location.Align.TopCenter:
|
case Location.Align.TopCenter:
|
||||||
@@ -211,14 +198,11 @@ namespace XCharts.Runtime
|
|||||||
legend.context.eachHeight = 0;
|
legend.context.eachHeight = 0;
|
||||||
if (legend.orient == Orient.Horizonal)
|
if (legend.orient == Orient.Horizonal)
|
||||||
{
|
{
|
||||||
var maxRowWidth = 0f;
|
|
||||||
foreach (var kv in legend.context.buttonList)
|
foreach (var kv in legend.context.buttonList)
|
||||||
{
|
{
|
||||||
if (width + kv.Value.width > maxWidth)
|
if (width + kv.Value.width > maxWidth)
|
||||||
{
|
{
|
||||||
realWidth = width - legend.itemGap;
|
realWidth = width - legend.itemGap;
|
||||||
if (realWidth > maxRowWidth)
|
|
||||||
maxRowWidth = realWidth;
|
|
||||||
realHeight += height + legend.itemGap;
|
realHeight += height + legend.itemGap;
|
||||||
if (legend.context.eachHeight < height + legend.itemGap)
|
if (legend.context.eachHeight < height + legend.itemGap)
|
||||||
{
|
{
|
||||||
@@ -232,10 +216,8 @@ namespace XCharts.Runtime
|
|||||||
height = kv.Value.height;
|
height = kv.Value.height;
|
||||||
}
|
}
|
||||||
width -= legend.itemGap;
|
width -= legend.itemGap;
|
||||||
if (width > maxRowWidth)
|
|
||||||
maxRowWidth = width;
|
|
||||||
legend.context.height = realHeight + height;
|
legend.context.height = realHeight + height;
|
||||||
legend.context.width = maxRowWidth;
|
legend.context.width = realWidth > 0 ? realWidth : width;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ namespace XCharts.Runtime
|
|||||||
[SerializeField]
|
[SerializeField]
|
||||||
private List<LabelStyle> m_ContentLabelStyles = new List<LabelStyle>()
|
private List<LabelStyle> m_ContentLabelStyles = new List<LabelStyle>()
|
||||||
{
|
{
|
||||||
new LabelStyle() { textPadding = new TextPadding(0, 5, 0, 0), textStyle = new TextStyle() { alignment = TextAnchor.MiddleLeft } },
|
new LabelStyle() { textPadding = new TextPadding(0, 5, 0, 0), textStyle = new TextStyle() { alignment = TextAnchor.MiddleCenter } },
|
||||||
new LabelStyle() { textPadding = new TextPadding(0, 20, 0, 0), textStyle = new TextStyle() { alignment = TextAnchor.MiddleLeft } },
|
new LabelStyle() { textPadding = new TextPadding(0, 20, 0, 0), textStyle = new TextStyle() { alignment = TextAnchor.MiddleLeft } },
|
||||||
new LabelStyle() { textPadding = new TextPadding(0, 0, 0, 0), textStyle = new TextStyle() { alignment = TextAnchor.MiddleRight } }
|
new LabelStyle() { textPadding = new TextPadding(0, 0, 0, 0), textStyle = new TextStyle() { alignment = TextAnchor.MiddleRight } }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ namespace XCharts.Runtime
|
|||||||
internal sealed class TooltipHandler : MainComponentHandler<Tooltip>
|
internal sealed class TooltipHandler : MainComponentHandler<Tooltip>
|
||||||
{
|
{
|
||||||
private Dictionary<string, ChartLabel> m_IndicatorLabels = new Dictionary<string, ChartLabel>();
|
private Dictionary<string, ChartLabel> m_IndicatorLabels = new Dictionary<string, ChartLabel>();
|
||||||
private Dictionary<Serie, Dictionary<int, List<SerieData>>> m_SortedAxisDataCache =
|
|
||||||
new Dictionary<Serie, Dictionary<int, List<SerieData>>>();
|
|
||||||
private GameObject m_LabelRoot;
|
private GameObject m_LabelRoot;
|
||||||
private ISerieContainer m_PointerContainer;
|
private ISerieContainer m_PointerContainer;
|
||||||
|
|
||||||
@@ -427,10 +425,8 @@ namespace XCharts.Runtime
|
|||||||
|
|
||||||
private void GetSerieDataByXYAxis(Serie serie, Axis xAxis, Axis yAxis)
|
private void GetSerieDataByXYAxis(Serie serie, Axis xAxis, Axis yAxis)
|
||||||
{
|
{
|
||||||
var xPointerInternal = xAxis.inverse ? -xAxis.context.pointerValue : xAxis.context.pointerValue;
|
var xAxisIndex = AxisHelper.GetAxisValueSplitIndex(xAxis, xAxis.context.pointerValue, false);
|
||||||
var yPointerInternal = yAxis.inverse ? -yAxis.context.pointerValue : yAxis.context.pointerValue;
|
var yAxisIndex = AxisHelper.GetAxisValueSplitIndex(yAxis, yAxis.context.pointerValue, false);
|
||||||
var xAxisIndex = AxisHelper.GetAxisValueSplitIndex(xAxis, xPointerInternal, false);
|
|
||||||
var yAxisIndex = AxisHelper.GetAxisValueSplitIndex(yAxis, yPointerInternal, false);
|
|
||||||
serie.context.pointerItemDataIndex = -1;
|
serie.context.pointerItemDataIndex = -1;
|
||||||
if (serie is Heatmap)
|
if (serie is Heatmap)
|
||||||
{
|
{
|
||||||
@@ -443,10 +439,8 @@ namespace XCharts.Runtime
|
|||||||
}
|
}
|
||||||
foreach (var serieData in serie.data)
|
foreach (var serieData in serie.data)
|
||||||
{
|
{
|
||||||
var xData = xAxis.inverse ? -serieData.GetData(0) : serieData.GetData(0);
|
var x = AxisHelper.GetAxisValueSplitIndex(xAxis, serieData.GetData(0), true);
|
||||||
var yData = yAxis.inverse ? -serieData.GetData(1) : serieData.GetData(1);
|
var y = AxisHelper.GetAxisValueSplitIndex(yAxis, serieData.GetData(1), true);
|
||||||
var x = AxisHelper.GetAxisValueSplitIndex(xAxis, xData, true);
|
|
||||||
var y = AxisHelper.GetAxisValueSplitIndex(yAxis, yData, true);
|
|
||||||
if (xAxisIndex == x && y == yAxisIndex)
|
if (xAxisIndex == x && y == yAxisIndex)
|
||||||
{
|
{
|
||||||
serie.context.pointerItemDataIndex = serieData.index;
|
serie.context.pointerItemDataIndex = serieData.index;
|
||||||
@@ -456,118 +450,30 @@ namespace XCharts.Runtime
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void GetSerieDataIndexByAxis(Serie serie, Axis axis, GridCoord grid, int dimension = 0)
|
private void GetSerieDataIndexByAxis(Serie serie, Axis axis, GridCoord grid, int dimension = 0)
|
||||||
{
|
|
||||||
var axisValue = axis.context.pointerValue;
|
|
||||||
serie.context.pointerAxisDataIndexs.Clear();
|
|
||||||
|
|
||||||
if (axis.IsTime())
|
|
||||||
{
|
|
||||||
FindSerieDataIndexByAxisLinear(serie, axis, axisValue, dimension);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var sortedData = GetSortedAxisData(serie, dimension);
|
|
||||||
var nearestIndex = GetNearestSerieDataIndex(sortedData, axisValue, dimension, axis.context.tickValue);
|
|
||||||
if (nearestIndex >= 0)
|
|
||||||
serie.context.pointerAxisDataIndexs.Add(nearestIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (serie.context.pointerAxisDataIndexs.Count > 0)
|
|
||||||
{
|
|
||||||
var index = serie.context.pointerAxisDataIndexs[0];
|
|
||||||
serie.context.pointerItemDataIndex = index;
|
|
||||||
var dataValue = serie.GetSerieData(index).GetData(dimension);
|
|
||||||
axis.context.axisTooltipValue = axis.inverse ? -dataValue : dataValue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
serie.context.pointerItemDataIndex = -1;
|
|
||||||
axis.context.axisTooltipValue = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<SerieData> GetSortedAxisData(Serie serie, int dimension)
|
|
||||||
{
|
|
||||||
Dictionary<int, List<SerieData>> dimensionCache;
|
|
||||||
if (!m_SortedAxisDataCache.TryGetValue(serie, out dimensionCache))
|
|
||||||
{
|
|
||||||
dimensionCache = new Dictionary<int, List<SerieData>>();
|
|
||||||
m_SortedAxisDataCache[serie] = dimensionCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<SerieData> sortedData;
|
|
||||||
if (!dimensionCache.TryGetValue(dimension, out sortedData))
|
|
||||||
{
|
|
||||||
sortedData = new List<SerieData>();
|
|
||||||
dimensionCache[dimension] = sortedData;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (serie.dataDirty || sortedData.Count != serie.dataCount)
|
|
||||||
{
|
|
||||||
sortedData.Clear();
|
|
||||||
for (int i = 0; i < serie.dataCount; i++)
|
|
||||||
{
|
|
||||||
sortedData.Add(serie.data[i]);
|
|
||||||
}
|
|
||||||
sortedData.Sort(delegate (SerieData a, SerieData b)
|
|
||||||
{
|
|
||||||
return a.GetData(dimension).CompareTo(b.GetData(dimension));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return sortedData;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int GetNearestSerieDataIndex(List<SerieData> sortedData, double axisValue, int dimension, double tickValue)
|
|
||||||
{
|
|
||||||
var dataCount = sortedData.Count;
|
|
||||||
if (dataCount <= 0) return -1;
|
|
||||||
|
|
||||||
if (dataCount == 1)
|
|
||||||
{
|
|
||||||
var currValue = sortedData[0].GetData(dimension);
|
|
||||||
var diff = tickValue * 0.5f;
|
|
||||||
return axisValue >= currValue - diff && axisValue <= currValue + diff
|
|
||||||
? sortedData[0].index
|
|
||||||
: -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var firstValue = sortedData[0].GetData(dimension);
|
|
||||||
var secondValue = sortedData[1].GetData(dimension);
|
|
||||||
if (axisValue <= firstValue + (secondValue - firstValue) / 2)
|
|
||||||
return sortedData[0].index;
|
|
||||||
|
|
||||||
var lastValue = sortedData[dataCount - 1].GetData(dimension);
|
|
||||||
var beforeLastValue = sortedData[dataCount - 2].GetData(dimension);
|
|
||||||
if (axisValue > beforeLastValue + (lastValue - beforeLastValue) / 2)
|
|
||||||
return sortedData[dataCount - 1].index;
|
|
||||||
|
|
||||||
var low = 1;
|
|
||||||
var high = dataCount - 2;
|
|
||||||
while (low <= high)
|
|
||||||
{
|
|
||||||
var mid = (low + high) / 2;
|
|
||||||
var prevValue = sortedData[mid - 1].GetData(dimension);
|
|
||||||
var currValue = sortedData[mid].GetData(dimension);
|
|
||||||
var nextValue = sortedData[mid + 1].GetData(dimension);
|
|
||||||
var leftBound = currValue - (currValue - prevValue) / 2;
|
|
||||||
var rightBound = currValue + (nextValue - currValue) / 2;
|
|
||||||
if (axisValue > leftBound && axisValue <= rightBound)
|
|
||||||
return sortedData[mid].index;
|
|
||||||
if (axisValue <= leftBound)
|
|
||||||
high = mid - 1;
|
|
||||||
else
|
|
||||||
low = mid + 1;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FindSerieDataIndexByAxisLinear(Serie serie, Axis axis, double axisValue, int dimension)
|
|
||||||
{
|
{
|
||||||
var currValue = 0d;
|
var currValue = 0d;
|
||||||
var lastValue = 0d;
|
var lastValue = 0d;
|
||||||
var nextValue = 0d;
|
var nextValue = 0d;
|
||||||
|
var axisValue = axis.context.pointerValue;
|
||||||
|
var isTimeAxis = axis.IsTime();
|
||||||
var dataCount = serie.dataCount;
|
var dataCount = serie.dataCount;
|
||||||
|
var themeSymbolSize = chart.theme.serie.scatterSymbolSize;
|
||||||
var data = serie.data;
|
var data = serie.data;
|
||||||
|
if (!isTimeAxis)// || serie.useSortData)
|
||||||
|
{
|
||||||
|
serie.context.sortedData.Clear();
|
||||||
|
for (int i = 0; i < dataCount; i++)
|
||||||
|
{
|
||||||
|
var serieData = serie.data[i];
|
||||||
|
serie.context.sortedData.Add(serieData);
|
||||||
|
}
|
||||||
|
serie.context.sortedData.Sort(delegate (SerieData a, SerieData b)
|
||||||
|
{
|
||||||
|
return a.GetData(dimension).CompareTo(b.GetData(dimension));
|
||||||
|
});
|
||||||
|
data = serie.context.sortedData;
|
||||||
|
}
|
||||||
|
serie.context.pointerAxisDataIndexs.Clear();
|
||||||
for (int i = 0; i < dataCount; i++)
|
for (int i = 0; i < dataCount; i++)
|
||||||
{
|
{
|
||||||
var serieData = data[i];
|
var serieData = data[i];
|
||||||
@@ -612,18 +518,28 @@ namespace XCharts.Runtime
|
|||||||
}
|
}
|
||||||
lastValue = currValue;
|
lastValue = currValue;
|
||||||
}
|
}
|
||||||
|
if (serie.context.pointerAxisDataIndexs.Count > 0)
|
||||||
|
{
|
||||||
|
var index = serie.context.pointerAxisDataIndexs[0];
|
||||||
|
serie.context.pointerItemDataIndex = index;
|
||||||
|
axis.context.axisTooltipValue = serie.GetSerieData(index).GetData(dimension);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
serie.context.pointerItemDataIndex = -1;
|
||||||
|
axis.context.axisTooltipValue = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GetSerieDataIndexByItem(Serie serie, Axis axis, GridCoord grid, int dimension = 0)
|
private void GetSerieDataIndexByItem(Serie serie, Axis axis, GridCoord grid, int dimension = 0)
|
||||||
{
|
{
|
||||||
if (serie.context.pointerItemDataIndex >= 0)
|
if (serie.context.pointerItemDataIndex >= 0)
|
||||||
{
|
{
|
||||||
var dataValue = serie.GetSerieData(serie.context.pointerItemDataIndex).GetData(dimension);
|
axis.context.axisTooltipValue = serie.GetSerieData(serie.context.pointerItemDataIndex).GetData(dimension);
|
||||||
axis.context.axisTooltipValue = axis.inverse ? -dataValue : dataValue;
|
|
||||||
}
|
}
|
||||||
else if (component.type == Tooltip.Type.Cross)
|
else if (component.type == Tooltip.Type.Cross)
|
||||||
{
|
{
|
||||||
axis.context.axisTooltipValue = axis.inverse ? -axis.context.pointerValue : axis.context.pointerValue;
|
axis.context.axisTooltipValue = axis.context.pointerValue;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -260,7 +260,6 @@ namespace XCharts.Runtime
|
|||||||
view.layout.childControlWidth = false;
|
view.layout.childControlWidth = false;
|
||||||
view.layout.childForceExpandHeight = false;
|
view.layout.childForceExpandHeight = false;
|
||||||
view.layout.childForceExpandWidth = false;
|
view.layout.childForceExpandWidth = false;
|
||||||
view.layout.childAlignment = tooltip.titleLabelStyle.textStyle.alignment;
|
|
||||||
view.layout.padding = new RectOffset(tooltip.paddingLeftRight,
|
view.layout.padding = new RectOffset(tooltip.paddingLeftRight,
|
||||||
tooltip.paddingLeftRight,
|
tooltip.paddingLeftRight,
|
||||||
tooltip.paddingTopBottom,
|
tooltip.paddingTopBottom,
|
||||||
|
|||||||
@@ -25,13 +25,6 @@ namespace XCharts.Runtime
|
|||||||
return !string.IsNullOrEmpty(content) && content.IndexOf('{') >= 0;
|
return !string.IsNullOrEmpty(content) && content.IndexOf('{') >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool NeedTotalContent(string content)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(content)) return false;
|
|
||||||
return content.IndexOf("{d", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
|
|
||||||
content.IndexOf("{f", System.StringComparison.OrdinalIgnoreCase) >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 替换字符串中的通配符,支持的通配符有{.}、{a}、{b}、{c}、{d}、{e}、{f}、{g}、{h}、{y}。
|
/// 替换字符串中的通配符,支持的通配符有{.}、{a}、{b}、{c}、{d}、{e}、{f}、{g}、{h}、{y}。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -778,27 +778,5 @@ namespace XCharts.Runtime
|
|||||||
foreach (var component in m_Components) component.ResetStatus();
|
foreach (var component in m_Components) component.ResetStatus();
|
||||||
foreach (var handler in m_SerieHandlers) handler.ForceUpdateSerieContext();
|
foreach (var handler in m_SerieHandlers) handler.ForceUpdateSerieContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Export chart configuration and data to JSON string.
|
|
||||||
/// ||导出图表配置和数据为JSON字符串。
|
|
||||||
/// </summary>
|
|
||||||
[Since("v3.16.0")]
|
|
||||||
public string ExportToJson(bool prettyPrint = true)
|
|
||||||
{
|
|
||||||
return XCharts.Runtime.ChartJsonSerializer.Serialize(this, prettyPrint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Import JSON and update current chart configuration.
|
|
||||||
/// ||导入JSON并更新当前图表配置。
|
|
||||||
/// </summary>
|
|
||||||
[Since("v3.16.0")]
|
|
||||||
public void ImportFromJson(string json)
|
|
||||||
{
|
|
||||||
XCharts.Runtime.ChartJsonDeserializer.Deserialize(json, this);
|
|
||||||
RefreshAllComponent();
|
|
||||||
RefreshChart();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,15 +56,15 @@ namespace XCharts.Runtime
|
|||||||
}
|
}
|
||||||
if (isX)
|
if (isX)
|
||||||
{
|
{
|
||||||
SeriesHelper.GetXMinMaxValue(this, axisIndex, axis.inverse, out tempMinValue, out tempMaxValue, false, needAnimationData);
|
SeriesHelper.GetXMinMaxValue(this, axisIndex, axis.inverse, out tempMinValue, out tempMaxValue, false, false, needAnimationData);
|
||||||
}
|
}
|
||||||
else if (isY)
|
else if (isY)
|
||||||
{
|
{
|
||||||
SeriesHelper.GetYMinMaxValue(this, axisIndex, axis.inverse, out tempMinValue, out tempMaxValue, false, needAnimationData);
|
SeriesHelper.GetYMinMaxValue(this, axisIndex, axis.inverse, out tempMinValue, out tempMaxValue, false, false, needAnimationData);
|
||||||
}
|
}
|
||||||
else if(isZ)
|
else if(isZ)
|
||||||
{
|
{
|
||||||
SeriesHelper.GetZMinMaxValue(this, axisIndex, axis.inverse, out tempMinValue, out tempMaxValue, false, needAnimationData);
|
SeriesHelper.GetZMinMaxValue(this, axisIndex, axis.inverse, out tempMinValue, out tempMaxValue, false, false, needAnimationData);
|
||||||
}
|
}
|
||||||
AxisHelper.AdjustMinMaxValue(axis, ref tempMinValue, ref tempMaxValue, true);
|
AxisHelper.AdjustMinMaxValue(axis, ref tempMinValue, ref tempMaxValue, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,25 +154,5 @@ namespace XCharts.Runtime
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnThemeChanged() { }
|
protected virtual void OnThemeChanged() { }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Export UI component configuration to JSON string.
|
|
||||||
/// </summary>
|
|
||||||
[Since("v3.16.0")]
|
|
||||||
public string ExportToJson(bool prettyPrint = true)
|
|
||||||
{
|
|
||||||
return UIComponentJsonSerializer.Serialize(this, prettyPrint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Import JSON and update current UI component configuration.
|
|
||||||
/// </summary>
|
|
||||||
[Since("v3.16.0")]
|
|
||||||
public void ImportFromJson(string json)
|
|
||||||
{
|
|
||||||
UIComponentJsonDeserializer.Deserialize(json, this);
|
|
||||||
RefreshAllComponent();
|
|
||||||
RefreshGraph();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1290,7 +1290,8 @@ namespace XCharts.Runtime
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
var clampedExportScale = Mathf.Max(1f, exportScale);
|
var clampedExportScale = Mathf.Max(1f, exportScale);
|
||||||
var outputScaleFactor = clampedExportScale;
|
var scaleFactor = canvas.scaleFactor <= 0 ? 1f : canvas.scaleFactor;
|
||||||
|
var outputScaleFactor = scaleFactor * clampedExportScale;
|
||||||
var width = Mathf.Max(1, Mathf.CeilToInt(rectTransform.rect.width * outputScaleFactor));
|
var width = Mathf.Max(1, Mathf.CeilToInt(rectTransform.rect.width * outputScaleFactor));
|
||||||
var height = Mathf.Max(1, Mathf.CeilToInt(rectTransform.rect.height * outputScaleFactor));
|
var height = Mathf.Max(1, Mathf.CeilToInt(rectTransform.rect.height * outputScaleFactor));
|
||||||
var chart = rectTransform.GetComponent<BaseChart>();
|
var chart = rectTransform.GetComponent<BaseChart>();
|
||||||
@@ -1403,8 +1404,8 @@ namespace XCharts.Runtime
|
|||||||
// so the saved image has original width/height but higher quality.
|
// so the saved image has original width/height but higher quality.
|
||||||
if (clampedExportScale > 1f)
|
if (clampedExportScale > 1f)
|
||||||
{
|
{
|
||||||
var targetWidth = Mathf.Max(1, Mathf.CeilToInt(rectTransform.rect.width));
|
var targetWidth = Mathf.Max(1, Mathf.CeilToInt(rectTransform.rect.width * scaleFactor));
|
||||||
var targetHeight = Mathf.Max(1, Mathf.CeilToInt(rectTransform.rect.height));
|
var targetHeight = Mathf.Max(1, Mathf.CeilToInt(rectTransform.rect.height * scaleFactor));
|
||||||
|
|
||||||
var smallRT = RenderTexture.GetTemporary(targetWidth, targetHeight, 0, rt.format);
|
var smallRT = RenderTexture.GetTemporary(targetWidth, targetHeight, 0, rt.format);
|
||||||
Graphics.Blit(rt, smallRT);
|
Graphics.Blit(rt, smallRT);
|
||||||
@@ -1415,7 +1416,7 @@ namespace XCharts.Runtime
|
|||||||
tex.Apply();
|
tex.Apply();
|
||||||
RenderTexture.ReleaseTemporary(smallRT);
|
RenderTexture.ReleaseTemporary(smallRT);
|
||||||
|
|
||||||
var cornerRadiiFinal = GetChartCornerRadius(chart, rectTransform.rect.width, rectTransform.rect.height, 1f);
|
var cornerRadiiFinal = GetChartCornerRadius(chart, rectTransform.rect.width, rectTransform.rect.height, scaleFactor);
|
||||||
ApplyRoundedCornerClip(tex, cornerRadiiFinal);
|
ApplyRoundedCornerClip(tex, cornerRadiiFinal);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -5,36 +5,6 @@ namespace XCharts.Runtime
|
|||||||
{
|
{
|
||||||
public static class DataHelper
|
public static class DataHelper
|
||||||
{
|
{
|
||||||
private static List<double> s_SampleSumPrefix = new List<double>();
|
|
||||||
|
|
||||||
public static bool IsAnyDataChanged(ref List<SerieData> showData, int minCount, int maxCount)
|
|
||||||
{
|
|
||||||
for (int i = minCount; i < maxCount; i++)
|
|
||||||
{
|
|
||||||
if (showData[i].IsDataChanged())
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<double> BuildSampleSumPrefix(ref List<SerieData> showData, int maxCount, bool inverse)
|
|
||||||
{
|
|
||||||
if (maxCount < 0) maxCount = 0;
|
|
||||||
var targetCount = maxCount + 1;
|
|
||||||
if (s_SampleSumPrefix.Count != targetCount)
|
|
||||||
{
|
|
||||||
s_SampleSumPrefix.Clear();
|
|
||||||
for (int i = 0; i < targetCount; i++)
|
|
||||||
s_SampleSumPrefix.Add(0);
|
|
||||||
}
|
|
||||||
s_SampleSumPrefix[0] = 0;
|
|
||||||
for (int i = 0; i < maxCount; i++)
|
|
||||||
{
|
|
||||||
s_SampleSumPrefix[i + 1] = s_SampleSumPrefix[i] + showData[i].GetData(1, inverse);
|
|
||||||
}
|
|
||||||
return s_SampleSumPrefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double DataAverage(ref List<SerieData> showData, SampleType sampleType,
|
public static double DataAverage(ref List<SerieData> showData, SampleType sampleType,
|
||||||
int minCount, int maxCount, int rate)
|
int minCount, int maxCount, int rate)
|
||||||
{
|
{
|
||||||
@@ -53,84 +23,14 @@ namespace XCharts.Runtime
|
|||||||
|
|
||||||
public static double SampleValue(ref List<SerieData> showData, SampleType sampleType, int rate,
|
public static double SampleValue(ref List<SerieData> showData, SampleType sampleType, int rate,
|
||||||
int minCount, int maxCount, double totalAverage, int index, float dataAddDuration, float dataChangeDuration,
|
int minCount, int maxCount, double totalAverage, int index, float dataAddDuration, float dataChangeDuration,
|
||||||
ref bool dataChanging, Axis axis, bool unscaledTime, bool useCurrentData = true,
|
ref bool dataChanging, Axis axis, bool unscaledTime)
|
||||||
bool checkDataChanging = true, List<double> sampleSumPrefix = null)
|
|
||||||
{
|
{
|
||||||
var inverse = axis.inverse;
|
var inverse = axis.inverse;
|
||||||
var minValue = 0;
|
var minValue = 0;
|
||||||
var maxValue = 0;
|
var maxValue = 0;
|
||||||
if (!useCurrentData)
|
|
||||||
{
|
|
||||||
if (rate <= 1 || index == minCount)
|
|
||||||
return showData[index].GetData(1, inverse);
|
|
||||||
|
|
||||||
switch (sampleType)
|
|
||||||
{
|
|
||||||
case SampleType.Sum:
|
|
||||||
case SampleType.Average:
|
|
||||||
if (sampleSumPrefix != null)
|
|
||||||
{
|
|
||||||
var totalByPrefix = sampleSumPrefix[index + 1] - sampleSumPrefix[index - rate + 1];
|
|
||||||
if (sampleType == SampleType.Average)
|
|
||||||
return totalByPrefix / rate;
|
|
||||||
else
|
|
||||||
return totalByPrefix;
|
|
||||||
}
|
|
||||||
double total = 0;
|
|
||||||
for (int i = index; i > index - rate; i--)
|
|
||||||
{
|
|
||||||
total += showData[i].GetData(1, inverse);
|
|
||||||
}
|
|
||||||
if (sampleType == SampleType.Average)
|
|
||||||
return total / rate;
|
|
||||||
else
|
|
||||||
return total;
|
|
||||||
|
|
||||||
case SampleType.Max:
|
|
||||||
double max = double.MinValue;
|
|
||||||
for (int i = index; i > index - rate; i--)
|
|
||||||
{
|
|
||||||
var value = showData[i].GetData(1, inverse);
|
|
||||||
if (value > max)
|
|
||||||
max = value;
|
|
||||||
}
|
|
||||||
return max;
|
|
||||||
|
|
||||||
case SampleType.Min:
|
|
||||||
double min = double.MaxValue;
|
|
||||||
for (int i = index; i > index - rate; i--)
|
|
||||||
{
|
|
||||||
var value = showData[i].GetData(1, inverse);
|
|
||||||
if (value < min)
|
|
||||||
min = value;
|
|
||||||
}
|
|
||||||
return min;
|
|
||||||
|
|
||||||
case SampleType.Peak:
|
|
||||||
max = double.MinValue;
|
|
||||||
min = double.MaxValue;
|
|
||||||
total = 0;
|
|
||||||
for (int i = index; i > index - rate; i--)
|
|
||||||
{
|
|
||||||
var value = showData[i].GetData(1, inverse);
|
|
||||||
total += value;
|
|
||||||
if (value < min)
|
|
||||||
min = value;
|
|
||||||
if (value > max)
|
|
||||||
max = value;
|
|
||||||
}
|
|
||||||
var average = total / rate;
|
|
||||||
if (average >= totalAverage)
|
|
||||||
return max;
|
|
||||||
else
|
|
||||||
return min;
|
|
||||||
}
|
|
||||||
return showData[index].GetData(1, inverse);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rate <= 1 || index == minCount)
|
if (rate <= 1 || index == minCount)
|
||||||
{
|
{
|
||||||
if (checkDataChanging && showData[index].IsDataChanged())
|
if (showData[index].IsDataChanged())
|
||||||
dataChanging = true;
|
dataChanging = true;
|
||||||
|
|
||||||
return showData[index].GetCurrData(1, dataAddDuration, dataChangeDuration, inverse, minValue, maxValue, unscaledTime);
|
return showData[index].GetCurrData(1, dataAddDuration, dataChangeDuration, inverse, minValue, maxValue, unscaledTime);
|
||||||
@@ -145,7 +45,7 @@ namespace XCharts.Runtime
|
|||||||
{
|
{
|
||||||
count++;
|
count++;
|
||||||
total += showData[i].GetCurrData(1, dataAddDuration, dataChangeDuration, inverse, minValue, maxValue, unscaledTime);
|
total += showData[i].GetCurrData(1, dataAddDuration, dataChangeDuration, inverse, minValue, maxValue, unscaledTime);
|
||||||
if (checkDataChanging && showData[i].IsDataChanged())
|
if (showData[i].IsDataChanged())
|
||||||
dataChanging = true;
|
dataChanging = true;
|
||||||
}
|
}
|
||||||
if (sampleType == SampleType.Average)
|
if (sampleType == SampleType.Average)
|
||||||
@@ -161,7 +61,7 @@ namespace XCharts.Runtime
|
|||||||
if (value > max)
|
if (value > max)
|
||||||
max = value;
|
max = value;
|
||||||
|
|
||||||
if (checkDataChanging && showData[i].IsDataChanged())
|
if (showData[i].IsDataChanged())
|
||||||
dataChanging = true;
|
dataChanging = true;
|
||||||
}
|
}
|
||||||
return max;
|
return max;
|
||||||
@@ -174,7 +74,7 @@ namespace XCharts.Runtime
|
|||||||
if (value < min)
|
if (value < min)
|
||||||
min = value;
|
min = value;
|
||||||
|
|
||||||
if (checkDataChanging && showData[i].IsDataChanged())
|
if (showData[i].IsDataChanged())
|
||||||
dataChanging = true;
|
dataChanging = true;
|
||||||
}
|
}
|
||||||
return min;
|
return min;
|
||||||
@@ -192,7 +92,7 @@ namespace XCharts.Runtime
|
|||||||
if (value > max)
|
if (value > max)
|
||||||
max = value;
|
max = value;
|
||||||
|
|
||||||
if (checkDataChanging && showData[i].IsDataChanged())
|
if (showData[i].IsDataChanged())
|
||||||
dataChanging = true;
|
dataChanging = true;
|
||||||
}
|
}
|
||||||
var average = total / rate;
|
var average = total / rate;
|
||||||
@@ -201,7 +101,7 @@ namespace XCharts.Runtime
|
|||||||
else
|
else
|
||||||
return min;
|
return min;
|
||||||
}
|
}
|
||||||
if (checkDataChanging && showData[index].IsDataChanged())
|
if (showData[index].IsDataChanged())
|
||||||
dataChanging = true;
|
dataChanging = true;
|
||||||
|
|
||||||
return showData[index].GetCurrData(1, dataAddDuration, dataChangeDuration, inverse, minValue, maxValue, unscaledTime);
|
return showData[index].GetCurrData(1, dataAddDuration, dataChangeDuration, inverse, minValue, maxValue, unscaledTime);
|
||||||
|
|||||||
@@ -217,8 +217,7 @@ namespace XCharts.Runtime
|
|||||||
var close = serieData.GetCurrData(startDataIndex + 1, dataAddDuration, dataChangeDuration, yAxis.inverse, yMinValue, yMaxValue, unscaledTime);
|
var close = serieData.GetCurrData(startDataIndex + 1, dataAddDuration, dataChangeDuration, yAxis.inverse, yMinValue, yMaxValue, unscaledTime);
|
||||||
var lowest = serieData.GetCurrData(startDataIndex + 2, dataAddDuration, dataChangeDuration, yAxis.inverse, yMinValue, yMaxValue, unscaledTime);
|
var lowest = serieData.GetCurrData(startDataIndex + 2, dataAddDuration, dataChangeDuration, yAxis.inverse, yMinValue, yMaxValue, unscaledTime);
|
||||||
var heighest = serieData.GetCurrData(startDataIndex + 3, dataAddDuration, dataChangeDuration, yAxis.inverse, yMinValue, yMaxValue, unscaledTime);
|
var heighest = serieData.GetCurrData(startDataIndex + 3, dataAddDuration, dataChangeDuration, yAxis.inverse, yMinValue, yMaxValue, unscaledTime);
|
||||||
var isBodyRise = yAxis.inverse ? close <= open : close >= open;
|
var isRise = yAxis.inverse ? close < open : close > open;
|
||||||
var isColorRise = IsColorRise(showData, i, startDataIndex, open, close);
|
|
||||||
var borderWidth = open == 0 ? 0f :
|
var borderWidth = open == 0 ? 0f :
|
||||||
(itemStyle.borderWidth == 0 ? theme.serie.candlestickBorderWidth :
|
(itemStyle.borderWidth == 0 ? theme.serie.candlestickBorderWidth :
|
||||||
itemStyle.borderWidth);
|
itemStyle.borderWidth);
|
||||||
@@ -240,7 +239,7 @@ namespace XCharts.Runtime
|
|||||||
Vector3 plb, plt, prt, prb, top;
|
Vector3 plb, plt, prt, prb, top;
|
||||||
|
|
||||||
var offset = 2 * borderWidth;
|
var offset = 2 * borderWidth;
|
||||||
if (isBodyRise)
|
if (isRise)
|
||||||
{
|
{
|
||||||
plb = new Vector3(pX + gap + offset, pY + offset);
|
plb = new Vector3(pX + gap + offset, pY + offset);
|
||||||
plt = new Vector3(pX + gap + offset, pY + currHig - offset);
|
plt = new Vector3(pX + gap + offset, pY + currHig - offset);
|
||||||
@@ -266,10 +265,10 @@ namespace XCharts.Runtime
|
|||||||
}
|
}
|
||||||
serie.context.dataPoints.Add(top);
|
serie.context.dataPoints.Add(top);
|
||||||
serie.context.dataIndexs.Add(serieData.index);
|
serie.context.dataIndexs.Add(serieData.index);
|
||||||
var areaColor = isColorRise ?
|
var areaColor = isRise ?
|
||||||
itemStyle.GetColor(theme.serie.candlestickColor) :
|
itemStyle.GetColor(theme.serie.candlestickColor) :
|
||||||
itemStyle.GetColor0(theme.serie.candlestickColor0);
|
itemStyle.GetColor0(theme.serie.candlestickColor0);
|
||||||
var borderColor = isColorRise ?
|
var borderColor = isRise ?
|
||||||
itemStyle.GetBorderColor(theme.serie.candlestickBorderColor) :
|
itemStyle.GetBorderColor(theme.serie.candlestickBorderColor) :
|
||||||
itemStyle.GetBorderColor0(theme.serie.candlestickBorderColor0);
|
itemStyle.GetBorderColor0(theme.serie.candlestickBorderColor0);
|
||||||
var itemWidth = Mathf.Abs(prt.x - plb.x);
|
var itemWidth = Mathf.Abs(prt.x - plb.x);
|
||||||
@@ -313,7 +312,7 @@ namespace XCharts.Runtime
|
|||||||
{
|
{
|
||||||
UGL.DrawLine(vh, openCenterPos, closeCenterPos, Mathf.Max(borderWidth, barWidth / 2), borderColor);
|
UGL.DrawLine(vh, openCenterPos, closeCenterPos, Mathf.Max(borderWidth, barWidth / 2), borderColor);
|
||||||
}
|
}
|
||||||
if (isBodyRise)
|
if (isRise)
|
||||||
{
|
{
|
||||||
UGL.DrawLine(vh, openCenterPos, lowPos, borderWidth, borderColor);
|
UGL.DrawLine(vh, openCenterPos, lowPos, borderWidth, borderColor);
|
||||||
UGL.DrawLine(vh, closeCenterPos, heighPos, borderWidth, borderColor);
|
UGL.DrawLine(vh, closeCenterPos, heighPos, borderWidth, borderColor);
|
||||||
@@ -334,17 +333,5 @@ namespace XCharts.Runtime
|
|||||||
chart.RefreshPainter(serie);
|
chart.RefreshPainter(serie);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsColorRise(List<SerieData> showData, int dataIndex, int startDataIndex, double open, double close)
|
|
||||||
{
|
|
||||||
if (dataIndex > 0)
|
|
||||||
{
|
|
||||||
var prevSerieData = showData[dataIndex - 1];
|
|
||||||
var prevStartDataIndex = prevSerieData.data.Count > 4 ? 1 : 0;
|
|
||||||
var prevClose = prevSerieData.GetData(prevStartDataIndex + 1);
|
|
||||||
return close >= prevClose;
|
|
||||||
}
|
|
||||||
return close >= open;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,8 +151,7 @@ namespace XCharts.Runtime
|
|||||||
var close = serieData.GetCurrData(startDataIndex + 1, dataAddDuration, dataChangeDuration, yAxis.inverse, yMinValue, yMaxValue, unscaledTime);
|
var close = serieData.GetCurrData(startDataIndex + 1, dataAddDuration, dataChangeDuration, yAxis.inverse, yMinValue, yMaxValue, unscaledTime);
|
||||||
var lowest = serieData.GetCurrData(startDataIndex + 2, dataAddDuration, dataChangeDuration, yAxis.inverse, yMinValue, yMaxValue, unscaledTime);
|
var lowest = serieData.GetCurrData(startDataIndex + 2, dataAddDuration, dataChangeDuration, yAxis.inverse, yMinValue, yMaxValue, unscaledTime);
|
||||||
var heighest = serieData.GetCurrData(startDataIndex + 3, dataAddDuration, dataChangeDuration, yAxis.inverse, yMinValue, yMaxValue, unscaledTime);
|
var heighest = serieData.GetCurrData(startDataIndex + 3, dataAddDuration, dataChangeDuration, yAxis.inverse, yMinValue, yMaxValue, unscaledTime);
|
||||||
var isBodyRise = yAxis.inverse ? close <= open : close >= open;
|
var isRise = yAxis.inverse ? close<open : close> open;
|
||||||
var isColorRise = IsColorRise(showData, i, startDataIndex, open, close);
|
|
||||||
var borderWidth = open == 0 ? 0f :
|
var borderWidth = open == 0 ? 0f :
|
||||||
(itemStyle.borderWidth == 0 ? theme.serie.candlestickBorderWidth :
|
(itemStyle.borderWidth == 0 ? theme.serie.candlestickBorderWidth :
|
||||||
itemStyle.borderWidth);
|
itemStyle.borderWidth);
|
||||||
@@ -174,7 +173,7 @@ namespace XCharts.Runtime
|
|||||||
Vector3 plb, plt, prt, prb, top;
|
Vector3 plb, plt, prt, prb, top;
|
||||||
|
|
||||||
var offset = 2 * borderWidth;
|
var offset = 2 * borderWidth;
|
||||||
if (isBodyRise)
|
if (isRise)
|
||||||
{
|
{
|
||||||
plb = new Vector3(pX + gap + offset, pY + offset);
|
plb = new Vector3(pX + gap + offset, pY + offset);
|
||||||
plt = new Vector3(pX + gap + offset, pY + currHig - offset);
|
plt = new Vector3(pX + gap + offset, pY + currHig - offset);
|
||||||
@@ -200,10 +199,10 @@ namespace XCharts.Runtime
|
|||||||
// }
|
// }
|
||||||
serie.context.dataPoints.Add(top);
|
serie.context.dataPoints.Add(top);
|
||||||
serie.context.dataIndexs.Add(serieData.index);
|
serie.context.dataIndexs.Add(serieData.index);
|
||||||
var areaColor = isColorRise ?
|
var areaColor = isRise ?
|
||||||
itemStyle.GetColor(theme.serie.candlestickColor) :
|
itemStyle.GetColor(theme.serie.candlestickColor) :
|
||||||
itemStyle.GetColor0(theme.serie.candlestickColor0);
|
itemStyle.GetColor0(theme.serie.candlestickColor0);
|
||||||
var borderColor = isColorRise ?
|
var borderColor = isRise ?
|
||||||
itemStyle.GetBorderColor(theme.serie.candlestickBorderColor) :
|
itemStyle.GetBorderColor(theme.serie.candlestickBorderColor) :
|
||||||
itemStyle.GetBorderColor0(theme.serie.candlestickBorderColor0);
|
itemStyle.GetBorderColor0(theme.serie.candlestickBorderColor0);
|
||||||
var itemWidth = Mathf.Abs(prt.x - plb.x);
|
var itemWidth = Mathf.Abs(prt.x - plb.x);
|
||||||
@@ -236,7 +235,7 @@ namespace XCharts.Runtime
|
|||||||
UGL.DrawBorder(vh, center, itemWidth, itemHeight, 2 * borderWidth, borderColor, 0,
|
UGL.DrawBorder(vh, center, itemWidth, itemHeight, 2 * borderWidth, borderColor, 0,
|
||||||
itemStyle.cornerRadius, isYAxis, 0.5f);
|
itemStyle.cornerRadius, isYAxis, 0.5f);
|
||||||
}
|
}
|
||||||
if (isBodyRise)
|
if (isRise)
|
||||||
{
|
{
|
||||||
UGL.DrawLine(vh, openCenterPos, lowPos, borderWidth, borderColor);
|
UGL.DrawLine(vh, openCenterPos, lowPos, borderWidth, borderColor);
|
||||||
UGL.DrawLine(vh, closeCenterPos, heighPos, borderWidth, borderColor);
|
UGL.DrawLine(vh, closeCenterPos, heighPos, borderWidth, borderColor);
|
||||||
@@ -262,17 +261,5 @@ namespace XCharts.Runtime
|
|||||||
chart.RefreshPainter(serie);
|
chart.RefreshPainter(serie);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsColorRise(List<SerieData> showData, int dataIndex, int startDataIndex, double open, double close)
|
|
||||||
{
|
|
||||||
if (dataIndex > 0)
|
|
||||||
{
|
|
||||||
var prevSerieData = showData[dataIndex - 1];
|
|
||||||
var prevStartDataIndex = prevSerieData.data.Count > 4 ? 1 : 0;
|
|
||||||
var prevClose = prevSerieData.GetData(prevStartDataIndex + 1);
|
|
||||||
return close >= prevClose;
|
|
||||||
}
|
|
||||||
return close >= open;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,7 +56,6 @@ namespace XCharts.Runtime
|
|||||||
m_LastCheckContextFlag = needCheck;
|
m_LastCheckContextFlag = needCheck;
|
||||||
var lineWidth = serie.lineStyle.GetWidth(chart.theme.serie.lineWidth);
|
var lineWidth = serie.lineStyle.GetWidth(chart.theme.serie.lineWidth);
|
||||||
var themeSymbolSize = chart.theme.serie.lineSymbolSize;
|
var themeSymbolSize = chart.theme.serie.lineSymbolSize;
|
||||||
var symbolVisible = serie.symbol != null && serie.symbol.show && serie.symbol.type != SymbolType.None;
|
|
||||||
var needInteract = false;
|
var needInteract = false;
|
||||||
serie.ResetDataIndex();
|
serie.ResetDataIndex();
|
||||||
if (m_LegendEnter)
|
if (m_LegendEnter)
|
||||||
@@ -66,12 +65,9 @@ namespace XCharts.Runtime
|
|||||||
for (int i = 0; i < serie.dataCount; i++)
|
for (int i = 0; i < serie.dataCount; i++)
|
||||||
{
|
{
|
||||||
var serieData = serie.data[i];
|
var serieData = serie.data[i];
|
||||||
|
var size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize, SerieState.Emphasis);
|
||||||
serieData.context.highlight = true;
|
serieData.context.highlight = true;
|
||||||
if (symbolVisible)
|
serieData.interact.SetValue(ref needInteract, size);
|
||||||
{
|
|
||||||
var size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize, SerieState.Emphasis);
|
|
||||||
serieData.interact.SetValue(ref needInteract, size);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (serie.context.isTriggerByAxis)
|
else if (serie.context.isTriggerByAxis)
|
||||||
@@ -83,12 +79,9 @@ namespace XCharts.Runtime
|
|||||||
var serieData = serie.data[i];
|
var serieData = serie.data[i];
|
||||||
var highlight = i == serie.context.pointerItemDataIndex;
|
var highlight = i == serie.context.pointerItemDataIndex;
|
||||||
serieData.context.highlight = highlight;
|
serieData.context.highlight = highlight;
|
||||||
if (symbolVisible)
|
var state = SerieHelper.GetSerieState(serie, serieData, true);
|
||||||
{
|
var size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize, state);
|
||||||
var state = SerieHelper.GetSerieState(serie, serieData, true);
|
serieData.interact.SetValue(ref needInteract, size);
|
||||||
var size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize, state);
|
|
||||||
serieData.interact.SetValue(ref needInteract, size);
|
|
||||||
}
|
|
||||||
if (highlight)
|
if (highlight)
|
||||||
{
|
{
|
||||||
serie.context.pointerEnter = true;
|
serie.context.pointerEnter = true;
|
||||||
@@ -105,23 +98,13 @@ namespace XCharts.Runtime
|
|||||||
for (int i = 0; i < serie.dataCount; i++)
|
for (int i = 0; i < serie.dataCount; i++)
|
||||||
{
|
{
|
||||||
var serieData = serie.data[i];
|
var serieData = serie.data[i];
|
||||||
var pointerOffset = (Vector2)chart.pointerPos - (Vector2)serieData.context.position;
|
var dist = Vector3.Distance(chart.pointerPos, serieData.context.position);
|
||||||
bool highlight;
|
var size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize);
|
||||||
if (symbolVisible)
|
var highlight = dist <= size * 2.5f;
|
||||||
{
|
|
||||||
var size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize);
|
|
||||||
var radius = size * 2.5f;
|
|
||||||
highlight = pointerOffset.sqrMagnitude <= radius * radius;
|
|
||||||
var state = SerieHelper.GetSerieState(serie, serieData, true);
|
|
||||||
size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize, state);
|
|
||||||
serieData.interact.SetValue(ref needInteract, size);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var radius = themeSymbolSize * 2.5f;
|
|
||||||
highlight = pointerOffset.sqrMagnitude <= radius * radius;
|
|
||||||
}
|
|
||||||
serieData.context.highlight = highlight;
|
serieData.context.highlight = highlight;
|
||||||
|
var state = SerieHelper.GetSerieState(serie, serieData, true);
|
||||||
|
size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize, state);
|
||||||
|
serieData.interact.SetValue(ref needInteract, size);
|
||||||
if (highlight)
|
if (highlight)
|
||||||
{
|
{
|
||||||
serie.context.pointerEnter = true;
|
serie.context.pointerEnter = true;
|
||||||
@@ -309,20 +292,6 @@ namespace XCharts.Runtime
|
|||||||
var dataChanging = false;
|
var dataChanging = false;
|
||||||
var dataChangeDuration = serie.animation.GetChangeDuration();
|
var dataChangeDuration = serie.animation.GetChangeDuration();
|
||||||
var unscaledTime = serie.animation.unscaledTime;
|
var unscaledTime = serie.animation.unscaledTime;
|
||||||
var dataAddDuration = 0f;
|
|
||||||
var useCurrentData = false;
|
|
||||||
List<double> sampleSumPrefix = null;
|
|
||||||
if (serie.animation.enable)
|
|
||||||
{
|
|
||||||
dataAddDuration = serie.animation.GetAdditionDuration();
|
|
||||||
useCurrentData = DataHelper.IsAnyDataChanged(ref showData, serie.minShow, showData.Count);
|
|
||||||
dataChanging = useCurrentData;
|
|
||||||
}
|
|
||||||
if (!useCurrentData && rate > 1 &&
|
|
||||||
(serie.sampleType == SampleType.Sum || serie.sampleType == SampleType.Average))
|
|
||||||
{
|
|
||||||
sampleSumPrefix = DataHelper.BuildSampleSumPrefix(ref showData, showData.Count, relativedAxis.inverse);
|
|
||||||
}
|
|
||||||
|
|
||||||
var interacting = false;
|
var interacting = false;
|
||||||
var lineWidth = LineHelper.GetLineWidth(ref interacting, serie, chart.theme.serie.lineWidth);
|
var lineWidth = LineHelper.GetLineWidth(ref interacting, serie, chart.theme.serie.lineWidth);
|
||||||
@@ -359,8 +328,7 @@ namespace XCharts.Runtime
|
|||||||
var np = Vector3.zero;
|
var np = Vector3.zero;
|
||||||
var xValue = axis.IsCategory() ? realIndex : serieData.GetData(0, axis.inverse);
|
var xValue = axis.IsCategory() ? realIndex : serieData.GetData(0, axis.inverse);
|
||||||
var relativedValue = DataHelper.SampleValue(ref showData, serie.sampleType, rate, serie.minShow,
|
var relativedValue = DataHelper.SampleValue(ref showData, serie.sampleType, rate, serie.minShow,
|
||||||
maxCount, totalAverage, i, dataAddDuration, dataChangeDuration, ref dataChanging, relativedAxis,
|
maxCount, totalAverage, i, 0, dataChangeDuration, ref dataChanging, relativedAxis, unscaledTime);
|
||||||
unscaledTime, useCurrentData, false, sampleSumPrefix);
|
|
||||||
|
|
||||||
serieData.context.stackHeight = GetDataPoint(isY, axis, relativedAxis, m_SerieGrid, xValue, relativedValue,
|
serieData.context.stackHeight = GetDataPoint(isY, axis, relativedAxis, m_SerieGrid, xValue, relativedValue,
|
||||||
i, scaleWid, scaleRelativedWid, isStack, ref np);
|
i, scaleWid, scaleRelativedWid, isStack, ref np);
|
||||||
|
|||||||
@@ -97,25 +97,6 @@ namespace XCharts.Runtime
|
|||||||
new Vector3(zero, points[count - 1].position.y) :
|
new Vector3(zero, points[count - 1].position.y) :
|
||||||
new Vector3(points[count - 1].position.x, zero);
|
new Vector3(points[count - 1].position.x, zero);
|
||||||
|
|
||||||
// ===== 优化:缓存动画检查结果 =====
|
|
||||||
bool needAnimationCheck = serie.animation.IsSerieAnimation() && !serie.animation.IsFinish();
|
|
||||||
float animationCurrDetail = serie.animation.GetCurrDetail();
|
|
||||||
|
|
||||||
// ===== 优化:预计算颜色 =====
|
|
||||||
Color32[] gradientColors1 = null, gradientColors2 = null;
|
|
||||||
if (isVisualMapGradient)
|
|
||||||
{
|
|
||||||
gradientColors1 = new Color32[count];
|
|
||||||
gradientColors2 = new Color32[count];
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
var tp = points[i].position;
|
|
||||||
var zp = isY ? new Vector3(zero, tp.y) : new Vector3(tp.x, zero);
|
|
||||||
gradientColors1[i] = VisualMapHelper.GetLineGradientColor(visualMap, zp, grid, axis, relativedAxis, areaColor);
|
|
||||||
gradientColors2[i] = VisualMapHelper.GetLineGradientColor(visualMap, tp, grid, axis, relativedAxis, areaToColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var lastDataIsIgnore = false;
|
var lastDataIsIgnore = false;
|
||||||
for (int i = 0; i < points.Count; i++)
|
for (int i = 0; i < points.Count; i++)
|
||||||
{
|
{
|
||||||
@@ -130,26 +111,23 @@ namespace XCharts.Runtime
|
|||||||
var toColor = areaToColor;
|
var toColor = areaToColor;
|
||||||
var lerp = areaLerp;
|
var lerp = areaLerp;
|
||||||
|
|
||||||
// ===== 优化:使用缓存的动画状态 =====
|
if (serie.animation.CheckDetailBreak(tp, isY))
|
||||||
if (needAnimationCheck)
|
|
||||||
{
|
{
|
||||||
if (isY && tp.y > animationCurrDetail || !isY && tp.x > animationCurrDetail)
|
isBreak = true;
|
||||||
{
|
|
||||||
isBreak = true;
|
|
||||||
var ip = Vector3.zero;
|
|
||||||
var axisStartPos = isY ? new Vector3(-10000, animationCurrDetail) : new Vector3(animationCurrDetail, -10000);
|
|
||||||
var axisEndPos = isY ? new Vector3(10000, animationCurrDetail) : new Vector3(animationCurrDetail, 10000);
|
|
||||||
|
|
||||||
if (UGLHelper.GetIntersection(lp, tp, axisStartPos, axisEndPos, ref ip))
|
var progress = serie.animation.GetCurrDetail();
|
||||||
tp = ip;
|
var ip = Vector3.zero;
|
||||||
}
|
var axisStartPos = isY ? new Vector3(-10000, progress) : new Vector3(progress, -10000);
|
||||||
|
var axisEndPos = isY ? new Vector3(10000, progress) : new Vector3(progress, 10000);
|
||||||
|
|
||||||
|
if (UGLHelper.GetIntersection(lp, tp, axisStartPos, axisEndPos, ref ip))
|
||||||
|
tp = ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
var zp = isY ? new Vector3(zero, tp.y) : new Vector3(tp.x, zero);
|
var zp = isY ? new Vector3(zero, tp.y) : new Vector3(tp.x, zero);
|
||||||
if (isVisualMapGradient)
|
if (isVisualMapGradient)
|
||||||
{
|
{
|
||||||
color = gradientColors1[i];
|
color = VisualMapHelper.GetLineGradientColor(visualMap, zp, grid, axis, relativedAxis, areaColor);
|
||||||
toColor = gradientColors2[i];
|
toColor = VisualMapHelper.GetLineGradientColor(visualMap, tp, grid, axis, relativedAxis, areaToColor);
|
||||||
lerp = true;
|
lerp = true;
|
||||||
}
|
}
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
@@ -293,13 +271,6 @@ namespace XCharts.Runtime
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 【优化版本】关键性能优化:
|
|
||||||
/// 1. 颜色预计算 (50-70% 性能提升)
|
|
||||||
/// 2. 缓存动画检查结果 (30-40% 性能提升)
|
|
||||||
/// 3. 线段样式预处理 (10-20% 性能提升)
|
|
||||||
/// 总体预期提升:50-70%(当启用渐变时)
|
|
||||||
/// </summary>
|
|
||||||
internal static void DrawSerieLine(VertexHelper vh, ThemeStyle theme, Serie serie, VisualMap visualMap,
|
internal static void DrawSerieLine(VertexHelper vh, ThemeStyle theme, Serie serie, VisualMap visualMap,
|
||||||
GridCoord grid, Axis axis, Axis relativedAxis, float lineWidth)
|
GridCoord grid, Axis axis, Axis relativedAxis, float lineWidth)
|
||||||
{
|
{
|
||||||
@@ -333,40 +304,6 @@ namespace XCharts.Runtime
|
|||||||
var dashLength = serie.lineStyle.dashLength;
|
var dashLength = serie.lineStyle.dashLength;
|
||||||
var gapLength = serie.lineStyle.gapLength;
|
var gapLength = serie.lineStyle.gapLength;
|
||||||
var dotLength = serie.lineStyle.dotLength;
|
var dotLength = serie.lineStyle.dotLength;
|
||||||
|
|
||||||
// ===== 优化 1: 预计算颜色数组 (如果启用 VisualMap 渐变) =====
|
|
||||||
Color32[] pointColors1 = null, pointColors2 = null;
|
|
||||||
if (isVisualMapGradient)
|
|
||||||
{
|
|
||||||
pointColors1 = new Color32[dataCount];
|
|
||||||
pointColors2 = new Color32[dataCount];
|
|
||||||
for (int i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
pointColors1[i] = VisualMapHelper.GetLineGradientColor(visualMap, datas[i].position, grid, axis, relativedAxis, lineColor);
|
|
||||||
pointColors2[i] = pointColors1[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 如果启用线段样式渐变,也预计算
|
|
||||||
Color32[] styleColors1 = null, styleColors2 = null;
|
|
||||||
if (isLineStyleGradient && !isVisualMapGradient)
|
|
||||||
{
|
|
||||||
styleColors1 = new Color32[dataCount];
|
|
||||||
styleColors2 = new Color32[dataCount];
|
|
||||||
for (int i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
styleColors1[i] = VisualMapHelper.GetLineStyleGradientColor(serie.lineStyle, datas[i].position, grid, axis, lineColor);
|
|
||||||
styleColors2[i] = styleColors1[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===== 优化 2: 缓存动画检查结果 =====
|
|
||||||
bool needAnimationCheck = serie.animation.IsSerieAnimation() && !serie.animation.IsFinish();
|
|
||||||
float animationCurrDetail = serie.animation.GetCurrDetail();
|
|
||||||
|
|
||||||
// ===== 优化 3: 线段样式预处理 (避免循环内 switch) =====
|
|
||||||
System.Func<int, bool> isSegmentIgnored = BuildSegmentIgnoreFunc(serie.lineStyle.type,
|
|
||||||
dashLength, gapLength, dotLength);
|
|
||||||
|
|
||||||
for (int i = 1; i < dataCount; i++)
|
for (int i = 1; i < dataCount; i++)
|
||||||
{
|
{
|
||||||
var cdata = datas[i];
|
var cdata = datas[i];
|
||||||
@@ -375,20 +312,15 @@ namespace XCharts.Runtime
|
|||||||
var lp = datas[i - 1].position;
|
var lp = datas[i - 1].position;
|
||||||
|
|
||||||
var np = i == dataCount - 1 ? cp : datas[i + 1].position;
|
var np = i == dataCount - 1 ? cp : datas[i + 1].position;
|
||||||
|
if (serie.animation.CheckDetailBreak(cp, isY))
|
||||||
// ===== 优化:使用缓存的动画状态 =====
|
|
||||||
if (needAnimationCheck)
|
|
||||||
{
|
{
|
||||||
if (isY && cp.y > animationCurrDetail || !isY && cp.x > animationCurrDetail)
|
isBreak = true;
|
||||||
{
|
var ip = Vector3.zero;
|
||||||
isBreak = true;
|
var progress = serie.animation.GetCurrDetail();
|
||||||
var ip = Vector3.zero;
|
var rate = 0f;
|
||||||
var rate = 0f;
|
if (AnimationStyleHelper.GetAnimationPosition(serie.animation, isY, lp, cp, progress, ref ip, ref rate))
|
||||||
if (AnimationStyleHelper.GetAnimationPosition(serie.animation, isY, lp, cp, animationCurrDetail, ref ip, ref rate))
|
cp = np = ip;
|
||||||
cp = np = ip;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
serie.context.lineEndPostion = cp;
|
serie.context.lineEndPostion = cp;
|
||||||
serie.context.lineEndValueY = AxisHelper.GetAxisPositionValue(grid, relativedAxis, cp);
|
serie.context.lineEndValueY = AxisHelper.GetAxisPositionValue(grid, relativedAxis, cp);
|
||||||
var handled = false;
|
var handled = false;
|
||||||
@@ -406,11 +338,39 @@ namespace XCharts.Runtime
|
|||||||
handled = true;
|
handled = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
{
|
||||||
// ===== 优化:使用预处理的线段样式函数 =====
|
segmentCount++;
|
||||||
segmentCount++;
|
var index = 0f;
|
||||||
if (isSegmentIgnored(segmentCount))
|
switch (serie.lineStyle.type)
|
||||||
isIgnore = true;
|
{
|
||||||
|
case LineStyle.Type.Dashed:
|
||||||
|
index = segmentCount % (dashLength + gapLength);
|
||||||
|
if (index >= dashLength)
|
||||||
|
isIgnore = true;
|
||||||
|
break;
|
||||||
|
case LineStyle.Type.Dotted:
|
||||||
|
index = segmentCount % (dotLength + gapLength);
|
||||||
|
if (index >= dotLength)
|
||||||
|
isIgnore = true;
|
||||||
|
break;
|
||||||
|
case LineStyle.Type.DashDot:
|
||||||
|
index = segmentCount % (dashLength + dotLength + 2 * gapLength);
|
||||||
|
if (index >= dashLength && index < dashLength + gapLength)
|
||||||
|
isIgnore = true;
|
||||||
|
else if (index >= dashLength + gapLength + dotLength)
|
||||||
|
isIgnore = true;
|
||||||
|
break;
|
||||||
|
case LineStyle.Type.DashDotDot:
|
||||||
|
index = segmentCount % (dashLength + 2 * dotLength + 3 * gapLength);
|
||||||
|
if (index >= dashLength && index < dashLength + gapLength)
|
||||||
|
isIgnore = true;
|
||||||
|
else if (index >= dashLength + gapLength + dotLength && index < dashLength + dotLength + 2 * gapLength)
|
||||||
|
isIgnore = true;
|
||||||
|
else if (index >= dashLength + 2 * gapLength + 2 * dotLength)
|
||||||
|
isIgnore = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (handled)
|
if (handled)
|
||||||
{
|
{
|
||||||
@@ -431,35 +391,12 @@ namespace XCharts.Runtime
|
|||||||
if (i == 1)
|
if (i == 1)
|
||||||
{
|
{
|
||||||
if (isClip) lastDataIsIgnore = true;
|
if (isClip) lastDataIsIgnore = true;
|
||||||
if (isVisualMapGradient)
|
AddLineVertToVertexHelper(vh, ltp, lbp, lineColor, isVisualMapGradient, isLineStyleGradient,
|
||||||
{
|
visualMap, serie.lineStyle, grid, axis, relativedAxis, false, lastDataIsIgnore, isIgnore);
|
||||||
AddLineVertToVertexHelperFast(vh, ltp, lbp, pointColors1[0], pointColors1[0], false, lastDataIsIgnore, isIgnore);
|
|
||||||
}
|
|
||||||
else if (isLineStyleGradient)
|
|
||||||
{
|
|
||||||
AddLineVertToVertexHelperFast(vh, ltp, lbp, styleColors1[0], styleColors1[0], false, lastDataIsIgnore, isIgnore);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AddLineVertToVertexHelper(vh, ltp, lbp, lineColor, false, false,
|
|
||||||
visualMap, serie.lineStyle, grid, axis, relativedAxis, false, lastDataIsIgnore, isIgnore);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dataCount == 2 || isBreak)
|
if (dataCount == 2 || isBreak)
|
||||||
{
|
{
|
||||||
if (isVisualMapGradient)
|
AddLineVertToVertexHelper(vh, clp, crp, lineColor, isVisualMapGradient, isLineStyleGradient,
|
||||||
{
|
visualMap, serie.lineStyle, grid, axis, relativedAxis, true, lastDataIsIgnore, isIgnore);
|
||||||
AddLineVertToVertexHelperFast(vh, clp, crp, pointColors1[i], pointColors1[i], true, lastDataIsIgnore, isIgnore);
|
|
||||||
}
|
|
||||||
else if (isLineStyleGradient)
|
|
||||||
{
|
|
||||||
AddLineVertToVertexHelperFast(vh, clp, crp, styleColors1[i], styleColors1[i], true, lastDataIsIgnore, isIgnore);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AddLineVertToVertexHelper(vh, clp, crp, lineColor, false, false,
|
|
||||||
visualMap, serie.lineStyle, grid, axis, relativedAxis, true, lastDataIsIgnore, isIgnore);
|
|
||||||
}
|
|
||||||
serie.context.lineEndPostion = cp;
|
serie.context.lineEndPostion = cp;
|
||||||
serie.context.lineEndValueY = AxisHelper.GetAxisPositionValue(grid, relativedAxis, cp);
|
serie.context.lineEndValueY = AxisHelper.GetAxisPositionValue(grid, relativedAxis, cp);
|
||||||
break;
|
break;
|
||||||
@@ -469,70 +406,31 @@ namespace XCharts.Runtime
|
|||||||
if (bitp == bibp)
|
if (bitp == bibp)
|
||||||
{
|
{
|
||||||
if (bitp)
|
if (bitp)
|
||||||
{
|
AddLineVertToVertexHelper(vh, itp, ibp, lineColor, isVisualMapGradient, isLineStyleGradient,
|
||||||
if (isVisualMapGradient)
|
visualMap, serie.lineStyle, grid, axis, relativedAxis, true, lastDataIsIgnore, isIgnore);
|
||||||
AddLineVertToVertexHelperFast(vh, itp, ibp, pointColors1[i], pointColors1[i], true, lastDataIsIgnore, isIgnore);
|
|
||||||
else if (isLineStyleGradient)
|
|
||||||
AddLineVertToVertexHelperFast(vh, itp, ibp, styleColors1[i], styleColors1[i], true, lastDataIsIgnore, isIgnore);
|
|
||||||
else
|
|
||||||
AddLineVertToVertexHelper(vh, itp, ibp, lineColor, false, false, visualMap, serie.lineStyle, grid, axis, relativedAxis, true, lastDataIsIgnore, isIgnore);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (isVisualMapGradient)
|
AddLineVertToVertexHelper(vh, ltp, clp, lineColor, isVisualMapGradient, isLineStyleGradient,
|
||||||
{
|
visualMap, serie.lineStyle, grid, axis, relativedAxis, true, lastDataIsIgnore, isIgnore);
|
||||||
AddLineVertToVertexHelperFast(vh, ltp, clp, pointColors1[i-1], pointColors1[i], true, lastDataIsIgnore, isIgnore);
|
AddLineVertToVertexHelper(vh, ltp, crp, lineColor, isVisualMapGradient, isLineStyleGradient,
|
||||||
AddLineVertToVertexHelperFast(vh, ltp, crp, pointColors1[i-1], pointColors1[i], true, lastDataIsIgnore, isIgnore);
|
visualMap, serie.lineStyle, grid, axis, relativedAxis, true, lastDataIsIgnore, isIgnore);
|
||||||
}
|
|
||||||
else if (isLineStyleGradient)
|
|
||||||
{
|
|
||||||
AddLineVertToVertexHelperFast(vh, ltp, clp, styleColors1[i-1], styleColors1[i], true, lastDataIsIgnore, isIgnore);
|
|
||||||
AddLineVertToVertexHelperFast(vh, ltp, crp, styleColors1[i-1], styleColors1[i], true, lastDataIsIgnore, isIgnore);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AddLineVertToVertexHelper(vh, ltp, clp, lineColor, false, false, visualMap, serie.lineStyle, grid, axis, relativedAxis, true, lastDataIsIgnore, isIgnore);
|
|
||||||
AddLineVertToVertexHelper(vh, ltp, crp, lineColor, false, false, visualMap, serie.lineStyle, grid, axis, relativedAxis, true, lastDataIsIgnore, isIgnore);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (bitp)
|
if (bitp)
|
||||||
{
|
{
|
||||||
if (isVisualMapGradient)
|
AddLineVertToVertexHelper(vh, itp, clp, lineColor, isVisualMapGradient, isLineStyleGradient,
|
||||||
{
|
visualMap, serie.lineStyle, grid, axis, relativedAxis, true, lastDataIsIgnore, isIgnore);
|
||||||
AddLineVertToVertexHelperFast(vh, itp, clp, pointColors1[i], pointColors1[i], true, lastDataIsIgnore, isIgnore);
|
AddLineVertToVertexHelper(vh, itp, crp, lineColor, isVisualMapGradient, isLineStyleGradient,
|
||||||
AddLineVertToVertexHelperFast(vh, itp, crp, pointColors1[i], pointColors1[i], true, lastDataIsIgnore, isIgnore);
|
visualMap, serie.lineStyle, grid, axis, relativedAxis, true, lastDataIsIgnore, isIgnore);
|
||||||
}
|
|
||||||
else if (isLineStyleGradient)
|
|
||||||
{
|
|
||||||
AddLineVertToVertexHelperFast(vh, itp, clp, styleColors1[i], styleColors1[i], true, lastDataIsIgnore, isIgnore);
|
|
||||||
AddLineVertToVertexHelperFast(vh, itp, crp, styleColors1[i], styleColors1[i], true, lastDataIsIgnore, isIgnore);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AddLineVertToVertexHelper(vh, itp, clp, lineColor, false, false, visualMap, serie.lineStyle, grid, axis, relativedAxis, true, lastDataIsIgnore, isIgnore);
|
|
||||||
AddLineVertToVertexHelper(vh, itp, crp, lineColor, false, false, visualMap, serie.lineStyle, grid, axis, relativedAxis, true, lastDataIsIgnore, isIgnore);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (bibp)
|
else if (bibp)
|
||||||
{
|
{
|
||||||
if (isVisualMapGradient)
|
AddLineVertToVertexHelper(vh, clp, ibp, lineColor, isVisualMapGradient, isLineStyleGradient,
|
||||||
{
|
visualMap, serie.lineStyle, grid, axis, relativedAxis, true, lastDataIsIgnore, isIgnore);
|
||||||
AddLineVertToVertexHelperFast(vh, clp, ibp, pointColors1[i], pointColors1[i], true, lastDataIsIgnore, isIgnore);
|
AddLineVertToVertexHelper(vh, crp, ibp, lineColor, isVisualMapGradient, isLineStyleGradient,
|
||||||
AddLineVertToVertexHelperFast(vh, crp, ibp, pointColors1[i], pointColors1[i], true, lastDataIsIgnore, isIgnore);
|
visualMap, serie.lineStyle, grid, axis, relativedAxis, true, lastDataIsIgnore, isIgnore);
|
||||||
}
|
|
||||||
else if (isLineStyleGradient)
|
|
||||||
{
|
|
||||||
AddLineVertToVertexHelperFast(vh, clp, ibp, styleColors1[i], styleColors1[i], true, lastDataIsIgnore, isIgnore);
|
|
||||||
AddLineVertToVertexHelperFast(vh, crp, ibp, styleColors1[i], styleColors1[i], true, lastDataIsIgnore, isIgnore);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AddLineVertToVertexHelper(vh, clp, ibp, lineColor, false, false, visualMap, serie.lineStyle, grid, axis, relativedAxis, true, lastDataIsIgnore, isIgnore);
|
|
||||||
AddLineVertToVertexHelper(vh, crp, ibp, lineColor, false, false, visualMap, serie.lineStyle, grid, axis, relativedAxis, true, lastDataIsIgnore, isIgnore);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastDataIsIgnore = isIgnore;
|
lastDataIsIgnore = isIgnore;
|
||||||
@@ -541,47 +439,6 @@ namespace XCharts.Runtime
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 【优化】预处理线段样式,避免循环内重复的 switch 判断
|
|
||||||
/// 返回一个委托,用于快速判断某个段是否应该被忽略
|
|
||||||
/// </summary>
|
|
||||||
private static System.Func<int, bool> BuildSegmentIgnoreFunc(LineStyle.Type lineType,
|
|
||||||
float dashLength, float gapLength, float dotLength)
|
|
||||||
{
|
|
||||||
switch (lineType)
|
|
||||||
{
|
|
||||||
case LineStyle.Type.Dashed:
|
|
||||||
return (segmentCount) =>
|
|
||||||
{
|
|
||||||
var index = segmentCount % (dashLength + gapLength);
|
|
||||||
return index >= dashLength;
|
|
||||||
};
|
|
||||||
case LineStyle.Type.Dotted:
|
|
||||||
return (segmentCount) =>
|
|
||||||
{
|
|
||||||
var index = segmentCount % (dotLength + gapLength);
|
|
||||||
return index >= dotLength;
|
|
||||||
};
|
|
||||||
case LineStyle.Type.DashDot:
|
|
||||||
return (segmentCount) =>
|
|
||||||
{
|
|
||||||
var index = segmentCount % (dashLength + dotLength + 2 * gapLength);
|
|
||||||
return (index >= dashLength && index < dashLength + gapLength) ||
|
|
||||||
(index >= dashLength + gapLength + dotLength);
|
|
||||||
};
|
|
||||||
case LineStyle.Type.DashDotDot:
|
|
||||||
return (segmentCount) =>
|
|
||||||
{
|
|
||||||
var index = segmentCount % (dashLength + 2 * dotLength + 3 * gapLength);
|
|
||||||
return (index >= dashLength && index < dashLength + gapLength) ||
|
|
||||||
(index >= dashLength + gapLength + dotLength && index < dashLength + dotLength + 2 * gapLength) ||
|
|
||||||
(index >= dashLength + 2 * gapLength + 2 * dotLength);
|
|
||||||
};
|
|
||||||
default:
|
|
||||||
return (_) => false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static float GetLineWidth(ref bool interacting, Serie serie, float defaultWidth)
|
public static float GetLineWidth(ref bool interacting, Serie serie, float defaultWidth)
|
||||||
{
|
{
|
||||||
var lineWidth = 0f;
|
var lineWidth = 0f;
|
||||||
@@ -593,27 +450,6 @@ namespace XCharts.Runtime
|
|||||||
return lineWidth;
|
return lineWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 快速路径版本 - 用于颜色已预计算的情况,避免条件判断和重复计算
|
|
||||||
/// </summary>
|
|
||||||
private static void AddLineVertToVertexHelperFast(VertexHelper vh, Vector3 tp, Vector3 bp,
|
|
||||||
Color32 color1, Color32 color2, bool needTriangle, bool lastIgnore, bool ignore)
|
|
||||||
{
|
|
||||||
if (lastIgnore && needTriangle)
|
|
||||||
UGL.AddVertToVertexHelper(vh, tp, bp, ColorUtil.clearColor32, true);
|
|
||||||
|
|
||||||
UGL.AddVertToVertexHelper(vh, tp, bp, color1, color2, needTriangle);
|
|
||||||
|
|
||||||
if (lastIgnore && !needTriangle)
|
|
||||||
{
|
|
||||||
UGL.AddVertToVertexHelper(vh, tp, bp, ColorUtil.clearColor32, false);
|
|
||||||
}
|
|
||||||
if (ignore && needTriangle)
|
|
||||||
{
|
|
||||||
UGL.AddVertToVertexHelper(vh, tp, bp, ColorUtil.clearColor32, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AddLineVertToVertexHelper(VertexHelper vh, Vector3 tp, Vector3 bp,
|
private static void AddLineVertToVertexHelper(VertexHelper vh, Vector3 tp, Vector3 bp,
|
||||||
Color32 lineColor, bool visualMapGradient, bool lineStyleGradient, VisualMap visualMap,
|
Color32 lineColor, bool visualMapGradient, bool lineStyleGradient, VisualMap visualMap,
|
||||||
LineStyle lineStyle, GridCoord grid, Axis axis, Axis relativedAxis, bool needTriangle,
|
LineStyle lineStyle, GridCoord grid, Axis axis, Axis relativedAxis, bool needTriangle,
|
||||||
|
|||||||
@@ -67,7 +67,6 @@ namespace XCharts.Runtime
|
|||||||
m_LastCheckContextFlag = needCheck;
|
m_LastCheckContextFlag = needCheck;
|
||||||
var themeSymbolSize = chart.theme.serie.lineSymbolSize;
|
var themeSymbolSize = chart.theme.serie.lineSymbolSize;
|
||||||
lineWidth = serie.lineStyle.GetWidth(chart.theme.serie.lineWidth);
|
lineWidth = serie.lineStyle.GetWidth(chart.theme.serie.lineWidth);
|
||||||
var symbolVisible = serie.symbol != null && serie.symbol.show && serie.symbol.type != SymbolType.None;
|
|
||||||
|
|
||||||
var needInteract = false;
|
var needInteract = false;
|
||||||
if (m_LegendEnter)
|
if (m_LegendEnter)
|
||||||
@@ -77,12 +76,9 @@ namespace XCharts.Runtime
|
|||||||
for (int i = 0; i < serie.dataCount; i++)
|
for (int i = 0; i < serie.dataCount; i++)
|
||||||
{
|
{
|
||||||
var serieData = serie.data[i];
|
var serieData = serie.data[i];
|
||||||
|
var size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize, SerieState.Emphasis);
|
||||||
serieData.context.highlight = true;
|
serieData.context.highlight = true;
|
||||||
if (symbolVisible)
|
serieData.interact.SetValue(ref needInteract, size);
|
||||||
{
|
|
||||||
var size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize, SerieState.Emphasis);
|
|
||||||
serieData.interact.SetValue(ref needInteract, size);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (serie.context.isTriggerByAxis)
|
else if (serie.context.isTriggerByAxis)
|
||||||
@@ -94,17 +90,13 @@ namespace XCharts.Runtime
|
|||||||
var serieData = serie.data[i];
|
var serieData = serie.data[i];
|
||||||
var highlight = i == serie.context.pointerItemDataIndex;
|
var highlight = i == serie.context.pointerItemDataIndex;
|
||||||
serieData.context.highlight = highlight;
|
serieData.context.highlight = highlight;
|
||||||
if (symbolVisible)
|
var state = SerieHelper.GetSerieState(serie, serieData, true);
|
||||||
{
|
var size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize, state);
|
||||||
var state = SerieHelper.GetSerieState(serie, serieData, true);
|
serieData.interact.SetValue(ref needInteract, size);
|
||||||
var size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize, state);
|
|
||||||
serieData.interact.SetValue(ref needInteract, size);
|
|
||||||
}
|
|
||||||
if (highlight)
|
if (highlight)
|
||||||
{
|
{
|
||||||
serie.context.pointerEnter = true;
|
serie.context.pointerEnter = true;
|
||||||
serie.context.pointerItemDataIndex = i;
|
serie.context.pointerItemDataIndex = i;
|
||||||
needInteract = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,21 +108,13 @@ namespace XCharts.Runtime
|
|||||||
for (int i = 0; i < serie.dataCount; i++)
|
for (int i = 0; i < serie.dataCount; i++)
|
||||||
{
|
{
|
||||||
var serieData = serie.data[i];
|
var serieData = serie.data[i];
|
||||||
var pointerOffset = (Vector2)chart.pointerPos - (Vector2)serieData.context.position;
|
var dist = Vector3.Distance(chart.pointerPos, serieData.context.position);
|
||||||
bool highlight;
|
var size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize);
|
||||||
if (symbolVisible)
|
var highlight = dist <= size;
|
||||||
{
|
|
||||||
var size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize);
|
|
||||||
highlight = pointerOffset.sqrMagnitude <= size * size;
|
|
||||||
var state = SerieHelper.GetSerieState(serie, serieData, true);
|
|
||||||
size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize, state);
|
|
||||||
serieData.interact.SetValue(ref needInteract, size);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
highlight = pointerOffset.sqrMagnitude <= themeSymbolSize * themeSymbolSize;
|
|
||||||
}
|
|
||||||
serieData.context.highlight = highlight;
|
serieData.context.highlight = highlight;
|
||||||
|
var state = SerieHelper.GetSerieState(serie, serieData, true);
|
||||||
|
size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize, state);
|
||||||
|
serieData.interact.SetValue(ref needInteract, size);
|
||||||
if (highlight)
|
if (highlight)
|
||||||
{
|
{
|
||||||
serie.context.pointerEnter = true;
|
serie.context.pointerEnter = true;
|
||||||
@@ -193,18 +177,6 @@ namespace XCharts.Runtime
|
|||||||
var dataChangeDuration = serie.animation.GetChangeDuration();
|
var dataChangeDuration = serie.animation.GetChangeDuration();
|
||||||
var dataAddDuration = serie.animation.GetAdditionDuration();
|
var dataAddDuration = serie.animation.GetAdditionDuration();
|
||||||
var unscaledTime = serie.animation.unscaledTime;
|
var unscaledTime = serie.animation.unscaledTime;
|
||||||
var useCurrentData = false;
|
|
||||||
List<double> sampleSumPrefix = null;
|
|
||||||
if (serie.animation.enable)
|
|
||||||
{
|
|
||||||
useCurrentData = DataHelper.IsAnyDataChanged(ref showData, serie.minShow, maxCount);
|
|
||||||
dataChanging = useCurrentData;
|
|
||||||
}
|
|
||||||
if (!useCurrentData && rate > 1 &&
|
|
||||||
(serie.sampleType == SampleType.Sum || serie.sampleType == SampleType.Average))
|
|
||||||
{
|
|
||||||
sampleSumPrefix = DataHelper.BuildSampleSumPrefix(ref showData, maxCount, relativedAxis.inverse);
|
|
||||||
}
|
|
||||||
|
|
||||||
var interacting = false;
|
var interacting = false;
|
||||||
var lineWidth = LineHelper.GetLineWidth(ref interacting, serie, chart.theme.serie.lineWidth);
|
var lineWidth = LineHelper.GetLineWidth(ref interacting, serie, chart.theme.serie.lineWidth);
|
||||||
@@ -232,8 +204,7 @@ namespace XCharts.Runtime
|
|||||||
var np = Vector3.zero;
|
var np = Vector3.zero;
|
||||||
var xValue = axis.IsCategory() ? i : serieData.GetData(0, axis.inverse);
|
var xValue = axis.IsCategory() ? i : serieData.GetData(0, axis.inverse);
|
||||||
var relativedValue = DataHelper.SampleValue(ref showData, serie.sampleType, rate, serie.minShow,
|
var relativedValue = DataHelper.SampleValue(ref showData, serie.sampleType, rate, serie.minShow,
|
||||||
maxCount, totalAverage, i, dataAddDuration, dataChangeDuration, ref dataChanging, relativedAxis,
|
maxCount, totalAverage, i, dataAddDuration, dataChangeDuration, ref dataChanging, relativedAxis, unscaledTime);
|
||||||
unscaledTime, useCurrentData, false, sampleSumPrefix);
|
|
||||||
|
|
||||||
serieData.context.stackHeight = GetDataPoint(isY, axis, relativedAxis, m_SerieGrid, xValue, relativedValue,
|
serieData.context.stackHeight = GetDataPoint(isY, axis, relativedAxis, m_SerieGrid, xValue, relativedValue,
|
||||||
i, scaleWid, scaleRelativedWid, false, ref np);
|
i, scaleWid, scaleRelativedWid, false, ref np);
|
||||||
|
|||||||
@@ -95,9 +95,6 @@ namespace XCharts.Runtime
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
itemFormatter = itemFormatter.Replace("\\n", "\n");
|
itemFormatter = itemFormatter.Replace("\\n", "\n");
|
||||||
var needTotal = itemFormatter.IndexOf("{d", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
|
|
||||||
itemFormatter.IndexOf("{f", System.StringComparison.OrdinalIgnoreCase) >= 0;
|
|
||||||
var total = needTotal ? serie.yTotal : 0;
|
|
||||||
var temp = itemFormatter.Split('\n');
|
var temp = itemFormatter.Split('\n');
|
||||||
for (int i = 0; i < temp.Length; i++)
|
for (int i = 0; i < temp.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -109,7 +106,7 @@ namespace XCharts.Runtime
|
|||||||
param.serieData = serieData;
|
param.serieData = serieData;
|
||||||
param.dataCount = serie.dataCount;
|
param.dataCount = serie.dataCount;
|
||||||
param.value = serieData.GetData(i);
|
param.value = serieData.GetData(i);
|
||||||
param.total = total;
|
param.total = serie.yTotal;
|
||||||
param.color = color;
|
param.color = color;
|
||||||
param.category = radar.GetIndicatorName(i);
|
param.category = radar.GetIndicatorName(i);
|
||||||
param.marker = marker;
|
param.marker = marker;
|
||||||
|
|||||||
@@ -323,9 +323,6 @@ namespace XCharts.Runtime
|
|||||||
[NonSerialized] internal bool m_NeedUpdateFilterData;
|
[NonSerialized] internal bool m_NeedUpdateFilterData;
|
||||||
[NonSerialized] public List<SerieData> m_FilterData = new List<SerieData>();
|
[NonSerialized] public List<SerieData> m_FilterData = new List<SerieData>();
|
||||||
[NonSerialized] private bool m_NameDirty;
|
[NonSerialized] private bool m_NameDirty;
|
||||||
[NonSerialized] private int m_YTotalCacheFrame = -1;
|
|
||||||
[NonSerialized] private double m_YTotalCacheValue = 0;
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// event callback when click serie.
|
/// event callback when click serie.
|
||||||
@@ -1242,9 +1239,6 @@ namespace XCharts.Runtime
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (m_YTotalCacheFrame == Time.frameCount)
|
|
||||||
return m_YTotalCacheValue;
|
|
||||||
|
|
||||||
double total = 0;
|
double total = 0;
|
||||||
if (IsPerformanceMode())
|
if (IsPerformanceMode())
|
||||||
{
|
{
|
||||||
@@ -1265,8 +1259,6 @@ namespace XCharts.Runtime
|
|||||||
total += sdata.GetCurrData(1, dataAddDuration, duration, unscaledTime);
|
total += sdata.GetCurrData(1, dataAddDuration, duration, unscaledTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_YTotalCacheFrame = Time.frameCount;
|
|
||||||
m_YTotalCacheValue = total;
|
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1317,7 +1309,6 @@ namespace XCharts.Runtime
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public override void ClearData()
|
public override void ClearData()
|
||||||
{
|
{
|
||||||
InvalidateTotalCache();
|
|
||||||
while (m_Data.Count > 0)
|
while (m_Data.Count > 0)
|
||||||
{
|
{
|
||||||
RemoveData(0);
|
RemoveData(0);
|
||||||
@@ -1345,7 +1336,6 @@ namespace XCharts.Runtime
|
|||||||
{
|
{
|
||||||
if (index >= 0 && index < m_Data.Count)
|
if (index >= 0 && index < m_Data.Count)
|
||||||
{
|
{
|
||||||
InvalidateTotalCache();
|
|
||||||
if (!string.IsNullOrEmpty(m_Data[index].name))
|
if (!string.IsNullOrEmpty(m_Data[index].name))
|
||||||
{
|
{
|
||||||
SetSerieNameDirty();
|
SetSerieNameDirty();
|
||||||
@@ -1394,7 +1384,6 @@ namespace XCharts.Runtime
|
|||||||
|
|
||||||
public virtual void AddSerieData(SerieData serieData)
|
public virtual void AddSerieData(SerieData serieData)
|
||||||
{
|
{
|
||||||
InvalidateTotalCache();
|
|
||||||
if (m_InsertDataToHead)
|
if (m_InsertDataToHead)
|
||||||
m_Data.Insert(0, serieData);
|
m_Data.Insert(0, serieData);
|
||||||
else
|
else
|
||||||
@@ -1835,7 +1824,6 @@ namespace XCharts.Runtime
|
|||||||
var flag = m_Data[index].UpdateData(dimension, value, animationOpen, unscaledTime, animationDuration);
|
var flag = m_Data[index].UpdateData(dimension, value, animationOpen, unscaledTime, animationDuration);
|
||||||
if (flag)
|
if (flag)
|
||||||
{
|
{
|
||||||
InvalidateTotalCache();
|
|
||||||
SetVerticesDirty();
|
SetVerticesDirty();
|
||||||
dataDirty = true;
|
dataDirty = true;
|
||||||
titleDirty = true;
|
titleDirty = true;
|
||||||
@@ -1857,7 +1845,6 @@ namespace XCharts.Runtime
|
|||||||
{
|
{
|
||||||
if (index >= 0 && index < m_Data.Count && values != null)
|
if (index >= 0 && index < m_Data.Count && values != null)
|
||||||
{
|
{
|
||||||
InvalidateTotalCache();
|
|
||||||
var serieData = m_Data[index];
|
var serieData = m_Data[index];
|
||||||
var animationOpen = animation.enable;
|
var animationOpen = animation.enable;
|
||||||
var animationDuration = animation.GetChangeDuration();
|
var animationDuration = animation.GetChangeDuration();
|
||||||
@@ -1871,18 +1858,6 @@ namespace XCharts.Runtime
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InvalidateTotalCache()
|
|
||||||
{
|
|
||||||
m_YTotalCacheFrame = -1;
|
|
||||||
m_YTotalCacheValue = 0;
|
|
||||||
InvalidateMinMaxCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InvalidateMinMaxCache()
|
|
||||||
{
|
|
||||||
context.InvalidateMinMaxCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool UpdateDataName(int index, string name)
|
public bool UpdateDataName(int index, string name)
|
||||||
{
|
{
|
||||||
if (index >= 0 && index < m_Data.Count)
|
if (index >= 0 && index < m_Data.Count)
|
||||||
|
|||||||
@@ -29,40 +29,6 @@ namespace XCharts.Runtime
|
|||||||
|
|
||||||
public class SerieContext
|
public class SerieContext
|
||||||
{
|
{
|
||||||
[System.NonSerialized] internal double[] cachedMin = new double[3] { double.MaxValue, double.MaxValue, double.MaxValue };
|
|
||||||
[System.NonSerialized] internal double[] cachedMax = new double[3] { double.MinValue, double.MinValue, double.MinValue };
|
|
||||||
[System.NonSerialized] internal bool[] cacheValid = new bool[3] { false, false, false };
|
|
||||||
|
|
||||||
internal void InvalidateMinMaxCache()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < cacheValid.Length; i++)
|
|
||||||
cacheValid[i] = false;
|
|
||||||
cachedMin[0] = cachedMin[1] = cachedMin[2] = double.MaxValue;
|
|
||||||
cachedMax[0] = cachedMax[1] = cachedMax[2] = double.MinValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool TryGetCachedMinMax(int dimension, out double minValue, out double maxValue)
|
|
||||||
{
|
|
||||||
minValue = 0; maxValue = 0;
|
|
||||||
if (dimension < 0 || dimension > 2) return false;
|
|
||||||
if (cacheValid[dimension])
|
|
||||||
{
|
|
||||||
minValue = cachedMin[dimension];
|
|
||||||
maxValue = cachedMax[dimension];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void SetCachedMinMax(int dimension, double minValue, double maxValue)
|
|
||||||
{
|
|
||||||
if (dimension < 0 || dimension > 2) return;
|
|
||||||
cachedMin[dimension] = minValue;
|
|
||||||
cachedMax[dimension] = maxValue;
|
|
||||||
cacheValid[dimension] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 鼠标是否进入serie
|
/// 鼠标是否进入serie
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@@ -69,7 +68,6 @@ namespace XCharts.Runtime
|
|||||||
protected bool m_ForceUpdateSerieContext = false;
|
protected bool m_ForceUpdateSerieContext = false;
|
||||||
protected int m_LegendEnterIndex;
|
protected int m_LegendEnterIndex;
|
||||||
protected ChartLabel m_EndLabel;
|
protected ChartLabel m_EndLabel;
|
||||||
private HashSet<int> m_DataIndexsSet = new HashSet<int>();
|
|
||||||
|
|
||||||
private float[] m_LastRadius = new float[2] { 0, 0 };
|
private float[] m_LastRadius = new float[2] { 0, 0 };
|
||||||
private float[] m_LastCenter = new float[2] { 0, 0 };
|
private float[] m_LastCenter = new float[2] { 0, 0 };
|
||||||
@@ -507,24 +505,15 @@ namespace XCharts.Runtime
|
|||||||
var dataAddDuration = serie.animation.GetAdditionDuration();
|
var dataAddDuration = serie.animation.GetAdditionDuration();
|
||||||
var unscaledTime = serie.animation.unscaledTime;
|
var unscaledTime = serie.animation.unscaledTime;
|
||||||
var needCheck = serie.context.dataIndexs.Count > 0;
|
var needCheck = serie.context.dataIndexs.Count > 0;
|
||||||
if (needCheck)
|
|
||||||
{
|
|
||||||
m_DataIndexsSet.Clear();
|
|
||||||
foreach (var idx in serie.context.dataIndexs)
|
|
||||||
m_DataIndexsSet.Add(idx);
|
|
||||||
}
|
|
||||||
var allLabelZeroPosition = true;
|
var allLabelZeroPosition = true;
|
||||||
var anyLabelActive = false;
|
var anyLabelActive = false;
|
||||||
SerieData lastActiveLabelSerieData = null;
|
|
||||||
var lastActiveLabelPos = Vector3.zero;
|
|
||||||
double lastActiveLabelValue = 0;
|
|
||||||
foreach (var serieData in serie.data)
|
foreach (var serieData in serie.data)
|
||||||
{
|
{
|
||||||
if (serieData.labelObject == null && serieData.context.dataLabels.Count <= 0)
|
if (serieData.labelObject == null && serieData.context.dataLabels.Count <= 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (needCheck && !m_DataIndexsSet.Contains(serieData.index))
|
if (needCheck && !serie.context.dataIndexs.Contains(serieData.index))
|
||||||
{
|
{
|
||||||
serieData.SetLabelActive(false);
|
serieData.SetLabelActive(false);
|
||||||
continue;
|
continue;
|
||||||
@@ -540,7 +529,7 @@ namespace XCharts.Runtime
|
|||||||
{
|
{
|
||||||
if (serie.multiDimensionLabel)
|
if (serie.multiDimensionLabel)
|
||||||
{
|
{
|
||||||
var total = FormatterHelper.NeedTotalContent(currLabel.formatter) ? serieData.GetTotalData() : 0;
|
var total = serieData.GetTotalData();
|
||||||
var color = chart.GetItemColor(serie, serieData);
|
var color = chart.GetItemColor(serie, serieData);
|
||||||
for (int i = 0; i < serieData.context.dataLabels.Count; i++)
|
for (int i = 0; i < serieData.context.dataLabels.Count; i++)
|
||||||
{
|
{
|
||||||
@@ -553,7 +542,6 @@ namespace XCharts.Runtime
|
|||||||
currLabel, color, chart);
|
currLabel, color, chart);
|
||||||
var offset = GetSerieDataLabelOffset(serieData, currLabel);
|
var offset = GetSerieDataLabelOffset(serieData, currLabel);
|
||||||
var active = currLabel.show && !isIgnore && !serie.IsMinShowLabelValue(value);
|
var active = currLabel.show && !isIgnore && !serie.IsMinShowLabelValue(value);
|
||||||
if (active) active = CheckLabelVisible(currLabel, serieData.index, value, i);
|
|
||||||
if (active)
|
if (active)
|
||||||
{
|
{
|
||||||
anyLabelActive = true;
|
anyLabelActive = true;
|
||||||
@@ -577,7 +565,7 @@ namespace XCharts.Runtime
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var value = serieData.GetCurrData(defaultDimension, dataAddDuration, dataChangeDuration, unscaledTime);
|
var value = serieData.GetCurrData(defaultDimension, dataAddDuration, dataChangeDuration, unscaledTime);
|
||||||
var total = FormatterHelper.NeedTotalContent(currLabel.formatter) ? serie.GetDataTotal(defaultDimension, serieData) : 0;
|
var total = serie.GetDataTotal(defaultDimension, serieData);
|
||||||
var color = chart.GetItemColor(serie, serieData);
|
var color = chart.GetItemColor(serie, serieData);
|
||||||
var content = string.IsNullOrEmpty(currLabel.formatter) ?
|
var content = string.IsNullOrEmpty(currLabel.formatter) ?
|
||||||
ChartCached.NumberToStr(value, currLabel.numericFormatter) :
|
ChartCached.NumberToStr(value, currLabel.numericFormatter) :
|
||||||
@@ -585,38 +573,6 @@ namespace XCharts.Runtime
|
|||||||
currLabel, color, chart);
|
currLabel, color, chart);
|
||||||
var labelPos = UpdateLabelPosition(serieData, currLabel);
|
var labelPos = UpdateLabelPosition(serieData, currLabel);
|
||||||
var active = currLabel.show && !isIgnore && !serie.IsMinShowLabelValue(value);
|
var active = currLabel.show && !isIgnore && !serie.IsMinShowLabelValue(value);
|
||||||
if (active) active = CheckLabelVisible(currLabel, serieData.index, value, defaultDimension);
|
|
||||||
if (active && currLabel.showMinGap > 0 && lastActiveLabelSerieData != null)
|
|
||||||
{
|
|
||||||
var dist = Mathf.Abs(labelPos.x - lastActiveLabelPos.x);
|
|
||||||
if (dist < currLabel.showMinGap)
|
|
||||||
{
|
|
||||||
var currValue = serieData.GetData(1);
|
|
||||||
if (Math.Abs(currValue) >= Math.Abs(lastActiveLabelValue))
|
|
||||||
{
|
|
||||||
lastActiveLabelSerieData.SetLabelActive(false);
|
|
||||||
lastActiveLabelSerieData = serieData;
|
|
||||||
lastActiveLabelPos = labelPos;
|
|
||||||
lastActiveLabelValue = currValue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
active = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lastActiveLabelSerieData = serieData;
|
|
||||||
lastActiveLabelPos = labelPos;
|
|
||||||
lastActiveLabelValue = serieData.GetData(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (active)
|
|
||||||
{
|
|
||||||
lastActiveLabelSerieData = serieData;
|
|
||||||
lastActiveLabelPos = labelPos;
|
|
||||||
lastActiveLabelValue = serieData.GetData(1);
|
|
||||||
}
|
|
||||||
if (active)
|
if (active)
|
||||||
{
|
{
|
||||||
anyLabelActive = true;
|
anyLabelActive = true;
|
||||||
@@ -650,66 +606,6 @@ namespace XCharts.Runtime
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CheckLabelVisible(LabelStyle label, int dataIndex, double value, int dimension)
|
|
||||||
{
|
|
||||||
// showCondition: 基于阈值的条件检查(AND showFilter)
|
|
||||||
bool conditionResult;
|
|
||||||
switch (label.showCondition)
|
|
||||||
{
|
|
||||||
case LabelStyle.ShowCondition.GreaterThan:
|
|
||||||
conditionResult = value > label.showThreshold;
|
|
||||||
break;
|
|
||||||
case LabelStyle.ShowCondition.LessThan:
|
|
||||||
conditionResult = value < label.showThreshold;
|
|
||||||
break;
|
|
||||||
default: // Always
|
|
||||||
conditionResult = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!conditionResult)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// showFilter: 基于数据形态的过滤检查
|
|
||||||
switch (label.showFilter)
|
|
||||||
{
|
|
||||||
case LabelStyle.ShowFilter.Peak:
|
|
||||||
{
|
|
||||||
bool isPeak = true;
|
|
||||||
bool hasNeighbor = false;
|
|
||||||
if (dataIndex > 0)
|
|
||||||
{
|
|
||||||
hasNeighbor = true;
|
|
||||||
isPeak &= value > serie.data[dataIndex - 1].GetData(dimension);
|
|
||||||
}
|
|
||||||
if (dataIndex < serie.dataCount - 1)
|
|
||||||
{
|
|
||||||
hasNeighbor = true;
|
|
||||||
isPeak &= value > serie.data[dataIndex + 1].GetData(dimension);
|
|
||||||
}
|
|
||||||
return isPeak && hasNeighbor;
|
|
||||||
}
|
|
||||||
case LabelStyle.ShowFilter.Valley:
|
|
||||||
{
|
|
||||||
bool isValley = true;
|
|
||||||
bool hasNeighbor = false;
|
|
||||||
if (dataIndex > 0)
|
|
||||||
{
|
|
||||||
hasNeighbor = true;
|
|
||||||
isValley &= value < serie.data[dataIndex - 1].GetData(dimension);
|
|
||||||
}
|
|
||||||
if (dataIndex < serie.dataCount - 1)
|
|
||||||
{
|
|
||||||
hasNeighbor = true;
|
|
||||||
isValley &= value < serie.data[dataIndex + 1].GetData(dimension);
|
|
||||||
}
|
|
||||||
return isValley && hasNeighbor;
|
|
||||||
}
|
|
||||||
default: // All
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void RefreshEndLabelInternal()
|
public virtual void RefreshEndLabelInternal()
|
||||||
{
|
{
|
||||||
if (m_EndLabel == null)
|
if (m_EndLabel == null)
|
||||||
@@ -798,14 +694,11 @@ namespace XCharts.Runtime
|
|||||||
if (itemFormatter == null) itemFormatter = "";
|
if (itemFormatter == null) itemFormatter = "";
|
||||||
var newItemFormatter = itemFormatter.Replace("\\n", "\n");
|
var newItemFormatter = itemFormatter.Replace("\\n", "\n");
|
||||||
var newNumericFormatter = SerieHelper.GetNumericFormatter(serie, serieData, numericFormatter);
|
var newNumericFormatter = SerieHelper.GetNumericFormatter(serie, serieData, numericFormatter);
|
||||||
var needTotal = newItemFormatter.IndexOf("{d", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
|
var temp = newItemFormatter.Split('\n');
|
||||||
newItemFormatter.IndexOf("{f", System.StringComparison.OrdinalIgnoreCase) >= 0;
|
for (int i = 0; i < temp.Length; i++)
|
||||||
var total = needTotal ? serie.yTotal : 0;
|
|
||||||
int newLinePos = newItemFormatter.IndexOf('\n');
|
|
||||||
if (newLinePos < 0)
|
|
||||||
{
|
{
|
||||||
var formatter = newItemFormatter;
|
var formatter = temp[i];
|
||||||
var param = serie.context.param;
|
var param = i == 0 ? serie.context.param : new SerieParams();
|
||||||
param.serieName = serie.serieName;
|
param.serieName = serie.serieName;
|
||||||
param.serieIndex = serie.index;
|
param.serieIndex = serie.index;
|
||||||
param.category = category;
|
param.category = category;
|
||||||
@@ -814,7 +707,7 @@ namespace XCharts.Runtime
|
|||||||
param.dataCount = serie.dataCount;
|
param.dataCount = serie.dataCount;
|
||||||
param.value = serieData.GetData(dimension);
|
param.value = serieData.GetData(dimension);
|
||||||
param.ignore = ignore;
|
param.ignore = ignore;
|
||||||
param.total = total;
|
param.total = serie.yTotal;
|
||||||
param.color = chart.GetMarkColor(serie, serieData);
|
param.color = chart.GetMarkColor(serie, serieData);
|
||||||
param.marker = SerieHelper.GetItemMarker(serie, serieData, marker);
|
param.marker = SerieHelper.GetItemMarker(serie, serieData, marker);
|
||||||
param.itemFormatter = formatter;
|
param.itemFormatter = formatter;
|
||||||
@@ -827,35 +720,6 @@ namespace XCharts.Runtime
|
|||||||
|
|
||||||
paramList.Add(param);
|
paramList.Add(param);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
var temp = newItemFormatter.Split('\n');
|
|
||||||
for (int i = 0; i < temp.Length; i++)
|
|
||||||
{
|
|
||||||
var formatter = temp[i];
|
|
||||||
var param = i == 0 ? serie.context.param : new SerieParams();
|
|
||||||
param.serieName = serie.serieName;
|
|
||||||
param.serieIndex = serie.index;
|
|
||||||
param.category = category;
|
|
||||||
param.dimension = dimension;
|
|
||||||
param.serieData = serieData;
|
|
||||||
param.dataCount = serie.dataCount;
|
|
||||||
param.value = serieData.GetData(dimension);
|
|
||||||
param.ignore = ignore;
|
|
||||||
param.total = total;
|
|
||||||
param.color = chart.GetMarkColor(serie, serieData);
|
|
||||||
param.marker = SerieHelper.GetItemMarker(serie, serieData, marker);
|
|
||||||
param.itemFormatter = formatter;
|
|
||||||
param.numericFormatter = newNumericFormatter;
|
|
||||||
param.columns.Clear();
|
|
||||||
|
|
||||||
param.columns.Add(param.marker);
|
|
||||||
param.columns.Add(showCategory ? category : serie.serieName);
|
|
||||||
param.columns.Add(ignore ? ignoreDataDefaultContent : ChartCached.NumberToStr(param.value, param.numericFormatter));
|
|
||||||
|
|
||||||
paramList.Add(param);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void UpdateItemSerieParams(ref List<SerieParams> paramList, ref string title,
|
protected void UpdateItemSerieParams(ref List<SerieParams> paramList, ref string title,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace XCharts.Runtime
|
|||||||
{
|
{
|
||||||
public static partial class SerieHelper
|
public static partial class SerieHelper
|
||||||
{
|
{
|
||||||
public static double GetMinData(Serie serie, int dimension = 1, DataZoom dataZoom = null, bool inverse = false)
|
public static double GetMinData(Serie serie, int dimension = 1, DataZoom dataZoom = null)
|
||||||
{
|
{
|
||||||
double min = double.MaxValue;
|
double min = double.MaxValue;
|
||||||
var dataList = serie.GetDataList(dataZoom);
|
var dataList = serie.GetDataList(dataZoom);
|
||||||
@@ -16,7 +16,7 @@ namespace XCharts.Runtime
|
|||||||
var serieData = dataList[i];
|
var serieData = dataList[i];
|
||||||
if (serieData.show && serieData.data.Count > dimension)
|
if (serieData.show && serieData.data.Count > dimension)
|
||||||
{
|
{
|
||||||
var value = serieData.GetData(dimension, inverse);
|
var value = serieData.data[dimension];
|
||||||
if (value < min && !serie.IsIgnoreValue(serieData, value)) min = value;
|
if (value < min && !serie.IsIgnoreValue(serieData, value)) min = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,7 +42,7 @@ namespace XCharts.Runtime
|
|||||||
}
|
}
|
||||||
return minData;
|
return minData;
|
||||||
}
|
}
|
||||||
public static double GetMaxData(Serie serie, int dimension = 1, DataZoom dataZoom = null, bool inverse = false)
|
public static double GetMaxData(Serie serie, int dimension = 1, DataZoom dataZoom = null)
|
||||||
{
|
{
|
||||||
double max = double.MinValue;
|
double max = double.MinValue;
|
||||||
var dataList = serie.GetDataList(dataZoom);
|
var dataList = serie.GetDataList(dataZoom);
|
||||||
@@ -51,7 +51,7 @@ namespace XCharts.Runtime
|
|||||||
var serieData = dataList[i];
|
var serieData = dataList[i];
|
||||||
if (serieData.show && serieData.data.Count > dimension)
|
if (serieData.show && serieData.data.Count > dimension)
|
||||||
{
|
{
|
||||||
var value = serieData.GetData(dimension, inverse);
|
var value = serieData.data[dimension];
|
||||||
if (value > max && !serie.IsIgnoreValue(serieData, value)) max = value;
|
if (value > max && !serie.IsIgnoreValue(serieData, value)) max = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,7 +159,7 @@ namespace XCharts.Runtime
|
|||||||
/// <param name="min"></param>
|
/// <param name="min"></param>
|
||||||
/// <param name="max"></param>
|
/// <param name="max"></param>
|
||||||
/// <param name="dataZoom"></param>
|
/// <param name="dataZoom"></param>
|
||||||
public static void GetMinMaxData(Serie serie, out double min, out double max, DataZoom dataZoom = null)
|
public static void GetMinMaxData(Serie serie, out double min, out double max, DataZoom dataZoom = null, int dimension = 0)
|
||||||
{
|
{
|
||||||
max = double.MinValue;
|
max = double.MinValue;
|
||||||
min = double.MaxValue;
|
min = double.MaxValue;
|
||||||
@@ -169,9 +169,11 @@ namespace XCharts.Runtime
|
|||||||
var serieData = dataList[i];
|
var serieData = dataList[i];
|
||||||
if (serieData.show)
|
if (serieData.show)
|
||||||
{
|
{
|
||||||
var count = serie.showDataDimension > 0 && serie.showDataDimension < serieData.data.Count ?
|
var count = 0;
|
||||||
serie.showDataDimension :
|
if (dimension > 0) count = dimension;
|
||||||
serieData.data.Count;
|
else count = serie.showDataDimension > serieData.data.Count ?
|
||||||
|
serieData.data.Count :
|
||||||
|
serie.showDataDimension;
|
||||||
for (int j = 0; j < count; j++)
|
for (int j = 0; j < count; j++)
|
||||||
{
|
{
|
||||||
var value = serieData.data[j];
|
var value = serieData.data[j];
|
||||||
@@ -867,36 +869,21 @@ namespace XCharts.Runtime
|
|||||||
private static void UpdateFilterData_Category(Serie serie, DataZoom dataZoom)
|
private static void UpdateFilterData_Category(Serie serie, DataZoom dataZoom)
|
||||||
{
|
{
|
||||||
var data = serie.data;
|
var data = serie.data;
|
||||||
// Use (N-1) intervals so that data point i maps to exactly i/(N-1)*100% of the
|
var range = Mathf.RoundToInt(data.Count * (dataZoom.end - dataZoom.start) / 100);
|
||||||
// DataZoom width — matching how the shadow draws data via scaleWid = width/(N-1).
|
if (range <= 0) range = 1;
|
||||||
// CeilToInt for start ensures we never include a point that lies before the filler.
|
int start = 0, end = 0;
|
||||||
// FloorToInt for end ensures we never include a point that lies after the filler.
|
if (dataZoom.context.invert)
|
||||||
int n = data.Count - 1;
|
|
||||||
int startIndex, endIndex;
|
|
||||||
if (n > 0)
|
|
||||||
{
|
{
|
||||||
if (dataZoom.context.invert)
|
end = Mathf.RoundToInt(data.Count * dataZoom.end / 100);
|
||||||
{
|
start = end - range;
|
||||||
startIndex = Mathf.CeilToInt((float)n * (100 - dataZoom.end) / 100);
|
if (start < 0) start = 0;
|
||||||
endIndex = Mathf.FloorToInt((float)n * (100 - dataZoom.start) / 100);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
startIndex = Mathf.CeilToInt((float)n * dataZoom.start / 100);
|
|
||||||
endIndex = Mathf.FloorToInt((float)n * dataZoom.end / 100);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
startIndex = 0;
|
start = Mathf.RoundToInt(data.Count * dataZoom.start / 100);
|
||||||
endIndex = 0;
|
end = start + range;
|
||||||
|
if (end > data.Count) end = data.Count;
|
||||||
}
|
}
|
||||||
var range = endIndex - startIndex + 1;
|
|
||||||
if (range <= 0) range = 1;
|
|
||||||
int start = startIndex;
|
|
||||||
if (start < 0) start = 0;
|
|
||||||
int end = start + range;
|
|
||||||
if (end > data.Count) end = data.Count;
|
|
||||||
var minZoomRatio = (int)(data.Count * dataZoom.minZoomRatio);
|
var minZoomRatio = (int)(data.Count * dataZoom.minZoomRatio);
|
||||||
if (start != serie.m_FilterStart || end != serie.m_FilterEnd ||
|
if (start != serie.m_FilterStart || end != serie.m_FilterEnd ||
|
||||||
minZoomRatio != serie.m_FilterMinShow || serie.m_NeedUpdateFilterData)
|
minZoomRatio != serie.m_FilterMinShow || serie.m_NeedUpdateFilterData)
|
||||||
|
|||||||
@@ -324,9 +324,9 @@ namespace XCharts.Runtime
|
|||||||
/// <param name="minValue"></param>
|
/// <param name="minValue"></param>
|
||||||
/// <param name="maxValue"></param>
|
/// <param name="maxValue"></param>
|
||||||
public static void GetXMinMaxValue(BaseChart chart, int axisIndex, bool inverse, out double minValue,
|
public static void GetXMinMaxValue(BaseChart chart, int axisIndex, bool inverse, out double minValue,
|
||||||
out double maxValue, bool isPolar = false, bool needAnimation = false)
|
out double maxValue, bool isPolar = false, bool filterByDataZoom = true, bool needAnimation = false)
|
||||||
{
|
{
|
||||||
GetMinMaxValue(chart, axisIndex, inverse, 0, out minValue, out maxValue, isPolar, needAnimation);
|
GetMinMaxValue(chart, axisIndex, inverse, 0, out minValue, out maxValue, isPolar, filterByDataZoom, needAnimation);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -337,9 +337,9 @@ namespace XCharts.Runtime
|
|||||||
/// <param name="minValue"></param>
|
/// <param name="minValue"></param>
|
||||||
/// <param name="maxValue"></param>
|
/// <param name="maxValue"></param>
|
||||||
public static void GetYMinMaxValue(BaseChart chart, int axisIndex, bool inverse, out double minValue,
|
public static void GetYMinMaxValue(BaseChart chart, int axisIndex, bool inverse, out double minValue,
|
||||||
out double maxValue, bool isPolar = false, bool needAnimation = false)
|
out double maxValue, bool isPolar = false, bool filterByDataZoom = true, bool needAnimation = false)
|
||||||
{
|
{
|
||||||
GetMinMaxValue(chart, axisIndex, inverse, 1, out minValue, out maxValue, isPolar, needAnimation);
|
GetMinMaxValue(chart, axisIndex, inverse, 1, out minValue, out maxValue, isPolar, filterByDataZoom, needAnimation);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -350,16 +350,16 @@ namespace XCharts.Runtime
|
|||||||
/// <param name="minValue"></param>
|
/// <param name="minValue"></param>
|
||||||
/// <param name="maxValue"></param>
|
/// <param name="maxValue"></param>
|
||||||
public static void GetZMinMaxValue(BaseChart chart, int axisIndex, bool inverse, out double minValue,
|
public static void GetZMinMaxValue(BaseChart chart, int axisIndex, bool inverse, out double minValue,
|
||||||
out double maxValue, bool isPolar = false, bool needAnimation = false)
|
out double maxValue, bool isPolar = false, bool filterByDataZoom = true, bool needAnimation = false)
|
||||||
{
|
{
|
||||||
GetMinMaxValue(chart, axisIndex, inverse, 2, out minValue, out maxValue, isPolar, needAnimation);
|
GetMinMaxValue(chart, axisIndex, inverse, 2, out minValue, out maxValue, isPolar, filterByDataZoom, needAnimation);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Dictionary<int, List<Serie>> _stackSeriesForMinMax = new Dictionary<int, List<Serie>>();
|
private static Dictionary<int, List<Serie>> _stackSeriesForMinMax = new Dictionary<int, List<Serie>>();
|
||||||
private static Dictionary<int, double> _serieTotalValueForMinMax = new Dictionary<int, double>();
|
private static Dictionary<int, double> _serieTotalValueForMinMax = new Dictionary<int, double>();
|
||||||
public static void GetMinMaxValue(BaseChart chart, int axisIndex,
|
public static void GetMinMaxValue(BaseChart chart, int axisIndex,
|
||||||
bool inverse, int dimension, out double minValue, out double maxValue, bool isPolar = false,
|
bool inverse, int dimension, out double minValue, out double maxValue, bool isPolar = false,
|
||||||
bool needAnimation = false)
|
bool filterByDataZoom = true, bool needAnimation = false)
|
||||||
{
|
{
|
||||||
double min = double.MaxValue;
|
double min = double.MaxValue;
|
||||||
double max = double.MinValue;
|
double max = double.MinValue;
|
||||||
@@ -376,48 +376,22 @@ namespace XCharts.Runtime
|
|||||||
var updateDuration = needAnimation ? serie.animation.GetChangeDuration() : 0;
|
var updateDuration = needAnimation ? serie.animation.GetChangeDuration() : 0;
|
||||||
var dataAddDuration = needAnimation ? serie.animation.GetAdditionDuration() : 0;
|
var dataAddDuration = needAnimation ? serie.animation.GetAdditionDuration() : 0;
|
||||||
var unscaledTime = serie.animation.unscaledTime;
|
var unscaledTime = serie.animation.unscaledTime;
|
||||||
|
|
||||||
// determine whether DataZoom filtering applies for this serie
|
|
||||||
var dz = chart.GetXDataZoomOfSerie(serie);
|
|
||||||
// Only apply DataZoom filter for non-X dimensions (dimension > 0, e.g. Y axis
|
|
||||||
// scaling to visible data). For dimension=0 (X axis whose range is controlled
|
|
||||||
// by DataZoom), using filtered X data would create a circular dependency:
|
|
||||||
// rawMin/rawMax would be set from filtered data, making the filter boundary
|
|
||||||
// relative to an already-filtered range instead of the full data range.
|
|
||||||
bool useDataZoomFilter = dimension > 0 && dz != null && dz.enable && dz.filterAxisRange;
|
|
||||||
|
|
||||||
// try per-serie cache when not filtering by dataZoom and not in animation mode
|
|
||||||
if (!useDataZoomFilter && !needAnimation)
|
|
||||||
{
|
|
||||||
double cmin, cmax;
|
|
||||||
if (serie.context.TryGetCachedMinMax(dimension, out cmin, out cmax))
|
|
||||||
{
|
|
||||||
if (cmax > max) max = cmax;
|
|
||||||
if (cmin < min) min = cmin;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double smin = double.MaxValue;
|
|
||||||
double smax = double.MinValue;
|
|
||||||
|
|
||||||
if (isPercentStack && SeriesHelper.IsPercentStack<Bar>(series, serie.serieName))
|
if (isPercentStack && SeriesHelper.IsPercentStack<Bar>(series, serie.serieName))
|
||||||
{
|
{
|
||||||
// percent stack per-serie considered as full range
|
if (100 > max) max = 100;
|
||||||
smin = 0;
|
if (0 < min) min = 0;
|
||||||
smax = 100;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var showData = serie.GetDataList(useDataZoomFilter ? dz : null);
|
var showData = serie.GetDataList(filterByDataZoom ? chart.GetXDataZoomOfSerie(serie) : null);
|
||||||
if (dimension > 0 && (serie is Candlestick || serie is SimplifiedCandlestick))
|
if (dimension > 0 && (serie is Candlestick || serie is SimplifiedCandlestick))
|
||||||
{
|
{
|
||||||
foreach (var data in showData)
|
foreach (var data in showData)
|
||||||
{
|
{
|
||||||
double dataMin, dataMax;
|
double dataMin, dataMax;
|
||||||
data.GetMinMaxData(1, inverse, out dataMin, out dataMax);
|
data.GetMinMaxData(1, inverse, out dataMin, out dataMax);
|
||||||
if (dataMax > smax) smax = dataMax;
|
if (dataMax > max) max = dataMax;
|
||||||
if (dataMin < smin) smin = dataMin;
|
if (dataMin < min) min = dataMin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -429,25 +403,12 @@ namespace XCharts.Runtime
|
|||||||
data.GetCurrData(dimension, dataAddDuration, updateDuration, unscaledTime, inverse);
|
data.GetCurrData(dimension, dataAddDuration, updateDuration, unscaledTime, inverse);
|
||||||
if (!serie.IsIgnoreValue(data, currData))
|
if (!serie.IsIgnoreValue(data, currData))
|
||||||
{
|
{
|
||||||
if (currData > smax) smax = currData;
|
if (currData > max) max = currData;
|
||||||
if (currData < smin) smin = currData;
|
if (currData < min) min = currData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if no data found for this serie, skip
|
|
||||||
if (smax == double.MinValue && smin == double.MaxValue)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// cache per-serie result for future calls
|
|
||||||
if (!needAnimation && !useDataZoomFilter)
|
|
||||||
{
|
|
||||||
serie.context.SetCachedMinMax(dimension, smin == double.MaxValue ? 0 : smin, smax == double.MinValue ? 0 : smax);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (smax > max) max = smax;
|
|
||||||
if (smin < min) min = smin;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -462,10 +423,7 @@ namespace XCharts.Runtime
|
|||||||
if ((isPolar && serie.polarIndex != axisIndex) ||
|
if ((isPolar && serie.polarIndex != axisIndex) ||
|
||||||
(!isPolar && serie.yAxisIndex != axisIndex) ||
|
(!isPolar && serie.yAxisIndex != axisIndex) ||
|
||||||
!serie.show) continue;
|
!serie.show) continue;
|
||||||
var stackDz = chart.GetXDataZoomOfSerie(serie);
|
var showData = serie.GetDataList(filterByDataZoom ? chart.GetXDataZoomOfSerie(serie) : null);
|
||||||
// Same rule as non-stack: don't use filtered data for dimension=0 (X axis).
|
|
||||||
if (stackDz != null && (dimension == 0 || !stackDz.filterAxisRange || !stackDz.enable)) stackDz = null;
|
|
||||||
var showData = serie.GetDataList(stackDz);
|
|
||||||
if (SeriesHelper.IsPercentStack<Bar>(series, serie.stack))
|
if (SeriesHelper.IsPercentStack<Bar>(series, serie.stack))
|
||||||
{
|
{
|
||||||
for (int j = 0; j < showData.Count; j++)
|
for (int j = 0; j < showData.Count; j++)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 529ec33cd4fb6466784b3a682204428c
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,291 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
using UnityEditor;
|
|
||||||
#endif
|
|
||||||
#if dUI_TextMeshPro
|
|
||||||
using TMPro;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace XCharts.Runtime
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Portable resource reference that supports a multi-level lookup strategy:
|
|
||||||
/// GUID → path → name → fallbackName → null/default
|
|
||||||
/// </summary>
|
|
||||||
[Serializable]
|
|
||||||
public class ResourceRef
|
|
||||||
{
|
|
||||||
/// <summary>Unity AssetDatabase GUID (editor-only, same project).</summary>
|
|
||||||
public string guid;
|
|
||||||
/// <summary>Asset path relative to project root e.g. "Assets/Fonts/Arial.ttf".</summary>
|
|
||||||
public string path;
|
|
||||||
/// <summary>Asset.name used for cross-project name search.</summary>
|
|
||||||
public string name;
|
|
||||||
/// <summary>Optional secondary name (system font alias, built-in default, etc.).</summary>
|
|
||||||
public string fallbackName;
|
|
||||||
/// <summary>Optional Base64-encoded asset bytes for full portability (< 100 KB recommended).</summary>
|
|
||||||
public string base64;
|
|
||||||
|
|
||||||
public bool IsEmpty()
|
|
||||||
{
|
|
||||||
return string.IsNullOrEmpty(guid)
|
|
||||||
&& string.IsNullOrEmpty(path)
|
|
||||||
&& string.IsNullOrEmpty(name)
|
|
||||||
&& string.IsNullOrEmpty(fallbackName)
|
|
||||||
&& string.IsNullOrEmpty(base64);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return string.Format("ResourceRef{{name={0}, path={1}, guid={2}}}", name, path, guid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handles serialization and resolution of Unity asset references for chart JSON export/import.
|
|
||||||
/// Supports Font, TMP_FontAsset, Sprite, Material, Texture2D.
|
|
||||||
/// </summary>
|
|
||||||
public static class ResourceRefHandler
|
|
||||||
{
|
|
||||||
// ─── Serialize ─────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Serializes a Unity Object into a portable ResourceRef.
|
|
||||||
/// </summary>
|
|
||||||
public static ResourceRef Serialize(UnityEngine.Object asset, bool includeBase64 = false)
|
|
||||||
{
|
|
||||||
if (asset == null) return null;
|
|
||||||
|
|
||||||
var refData = new ResourceRef
|
|
||||||
{
|
|
||||||
name = asset.name
|
|
||||||
};
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
var assetPath = AssetDatabase.GetAssetPath(asset);
|
|
||||||
if (!string.IsNullOrEmpty(assetPath))
|
|
||||||
{
|
|
||||||
refData.path = assetPath;
|
|
||||||
refData.guid = AssetDatabase.AssetPathToGUID(assetPath);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Optional base64 embedding for portability
|
|
||||||
if (includeBase64)
|
|
||||||
{
|
|
||||||
var b64 = TryEncodeBase64(asset);
|
|
||||||
if (!string.IsNullOrEmpty(b64))
|
|
||||||
refData.base64 = b64;
|
|
||||||
}
|
|
||||||
|
|
||||||
return refData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Serializes a Font with a fallback name hint.
|
|
||||||
/// </summary>
|
|
||||||
public static ResourceRef SerializeFont(Font font, string fallbackName = null)
|
|
||||||
{
|
|
||||||
if (font == null) return null;
|
|
||||||
var refData = Serialize(font) ?? new ResourceRef();
|
|
||||||
refData.fallbackName = fallbackName ?? "Arial";
|
|
||||||
return refData;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if dUI_TextMeshPro
|
|
||||||
/// <summary>
|
|
||||||
/// Serializes a TMP_FontAsset with a fallback name hint.
|
|
||||||
/// </summary>
|
|
||||||
public static ResourceRef SerializeTMPFont(TMP_FontAsset font, string fallbackName = null)
|
|
||||||
{
|
|
||||||
if (font == null) return null;
|
|
||||||
var refData = Serialize(font) ?? new ResourceRef();
|
|
||||||
refData.fallbackName = fallbackName ?? "LiberationSans SDF";
|
|
||||||
return refData;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// ─── Resolve ────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Attempts to resolve a ResourceRef back to a Unity asset using the fallback chain:<br/>
|
|
||||||
/// GUID → path → name → fallbackName → null
|
|
||||||
/// </summary>
|
|
||||||
public static T TryResolve<T>(ResourceRef refData) where T : UnityEngine.Object
|
|
||||||
{
|
|
||||||
if (refData == null || refData.IsEmpty()) return null;
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
// 1. GUID lookup (same project, exact)
|
|
||||||
if (!string.IsNullOrEmpty(refData.guid))
|
|
||||||
{
|
|
||||||
var guidPath = AssetDatabase.GUIDToAssetPath(refData.guid);
|
|
||||||
if (!string.IsNullOrEmpty(guidPath))
|
|
||||||
{
|
|
||||||
var asset = AssetDatabase.LoadAssetAtPath<T>(guidPath);
|
|
||||||
if (asset != null) return asset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Path-based lookup
|
|
||||||
if (!string.IsNullOrEmpty(refData.path))
|
|
||||||
{
|
|
||||||
var asset = AssetDatabase.LoadAssetAtPath<T>(refData.path);
|
|
||||||
if (asset != null) return asset;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Name-based search across project
|
|
||||||
if (!string.IsNullOrEmpty(refData.name))
|
|
||||||
{
|
|
||||||
var found = FindAssetByName<T>(refData.name);
|
|
||||||
if (found != null) return found;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// 4. Resources.Load by name
|
|
||||||
if (!string.IsNullOrEmpty(refData.name))
|
|
||||||
{
|
|
||||||
var asset = Resources.Load<T>(refData.name);
|
|
||||||
if (asset != null) return asset;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Fallback name
|
|
||||||
if (!string.IsNullOrEmpty(refData.fallbackName))
|
|
||||||
{
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
var found = FindAssetByName<T>(refData.fallbackName);
|
|
||||||
if (found != null) return found;
|
|
||||||
#endif
|
|
||||||
var asset = Resources.Load<T>(refData.fallbackName);
|
|
||||||
if (asset != null) return asset;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Base64 decode
|
|
||||||
if (!string.IsNullOrEmpty(refData.base64))
|
|
||||||
{
|
|
||||||
var decoded = TryDecodeBase64<T>(refData.base64, refData.name ?? "imported_asset");
|
|
||||||
if (decoded != null) return decoded;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IsUnityBuiltinDefaultResourceRef(refData))
|
|
||||||
Debug.LogWarning(string.Format("[XCharts] ResourceRefHandler: Could not resolve asset '{0}'. Using default.", refData));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsUnityBuiltinDefaultResourceRef(ResourceRef refData)
|
|
||||||
{
|
|
||||||
if (refData == null) return false;
|
|
||||||
|
|
||||||
bool pathIsBuiltin = !string.IsNullOrEmpty(refData.path)
|
|
||||||
&& refData.path.IndexOf("Library/unity default resources", StringComparison.OrdinalIgnoreCase) >= 0;
|
|
||||||
|
|
||||||
bool guidIsBuiltin = !string.IsNullOrEmpty(refData.guid)
|
|
||||||
&& string.Equals(refData.guid, "0000000000000000e000000000000000", StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
bool nameIsBuiltinFont = string.Equals(refData.name, "Arial", StringComparison.OrdinalIgnoreCase)
|
|
||||||
|| string.Equals(refData.fallbackName, "Arial", StringComparison.OrdinalIgnoreCase)
|
|
||||||
#if dUI_TextMeshPro
|
|
||||||
|| string.Equals(refData.name, "LiberationSans SDF", StringComparison.OrdinalIgnoreCase)
|
|
||||||
|| string.Equals(refData.fallbackName, "LiberationSans SDF", StringComparison.OrdinalIgnoreCase)
|
|
||||||
#endif
|
|
||||||
;
|
|
||||||
|
|
||||||
return pathIsBuiltin || guidIsBuiltin || nameIsBuiltinFont;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
private static T FindAssetByName<T>(string assetName) where T : UnityEngine.Object
|
|
||||||
{
|
|
||||||
string typeName = typeof(T).Name;
|
|
||||||
var guids = AssetDatabase.FindAssets(string.Format("{0} t:{1}", assetName, typeName));
|
|
||||||
foreach (var g in guids)
|
|
||||||
{
|
|
||||||
var p = AssetDatabase.GUIDToAssetPath(g);
|
|
||||||
var asset = AssetDatabase.LoadAssetAtPath<T>(p);
|
|
||||||
if (asset != null && asset.name == assetName)
|
|
||||||
return asset;
|
|
||||||
}
|
|
||||||
// Looser match (name contains)
|
|
||||||
foreach (var g in guids)
|
|
||||||
{
|
|
||||||
var p = AssetDatabase.GUIDToAssetPath(g);
|
|
||||||
var asset = AssetDatabase.LoadAssetAtPath<T>(p);
|
|
||||||
if (asset != null) return asset;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// ─── Base64 helpers ──────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
private static string TryEncodeBase64(UnityEngine.Object asset)
|
|
||||||
{
|
|
||||||
var texture = asset as Texture2D;
|
|
||||||
if (texture != null)
|
|
||||||
return Convert.ToBase64String(texture.EncodeToPNG());
|
|
||||||
// Font/Material: not trivially serializable as bytes at runtime; skip.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static T TryDecodeBase64<T>(string base64, string assetName) where T : UnityEngine.Object
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (typeof(T) == typeof(Texture2D) || typeof(T) == typeof(Sprite))
|
|
||||||
{
|
|
||||||
var bytes = Convert.FromBase64String(base64);
|
|
||||||
var tex = new Texture2D(2, 2);
|
|
||||||
if (tex.LoadImage(bytes))
|
|
||||||
{
|
|
||||||
tex.name = assetName;
|
|
||||||
if (typeof(T) == typeof(Sprite))
|
|
||||||
{
|
|
||||||
var sprite = Sprite.Create(tex,
|
|
||||||
new Rect(0, 0, tex.width, tex.height),
|
|
||||||
new Vector2(0.5f, 0.5f));
|
|
||||||
sprite.name = assetName;
|
|
||||||
return sprite as T;
|
|
||||||
}
|
|
||||||
return tex as T;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Debug.LogWarning(string.Format("[XCharts] ResourceRefHandler: Base64 decode failed: {0}", ex.Message));
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Convenience overloads ───────────────────────────────────────────────────
|
|
||||||
|
|
||||||
public static Font TryResolveFont(ResourceRef refData)
|
|
||||||
{
|
|
||||||
return TryResolve<Font>(refData);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Sprite TryResolveSprite(ResourceRef refData)
|
|
||||||
{
|
|
||||||
return TryResolve<Sprite>(refData);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Material TryResolveMaterial(ResourceRef refData)
|
|
||||||
{
|
|
||||||
return TryResolve<Material>(refData);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Texture2D TryResolveTexture(ResourceRef refData)
|
|
||||||
{
|
|
||||||
return TryResolve<Texture2D>(refData);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if dUI_TextMeshPro
|
|
||||||
public static TMP_FontAsset TryResolveTMPFont(ResourceRef refData)
|
|
||||||
{
|
|
||||||
return TryResolve<TMP_FontAsset>(refData);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 9c3e7340177cb43e488f9f9547ceea7b
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,299 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace XCharts.Runtime
|
|
||||||
{
|
|
||||||
[Serializable]
|
|
||||||
public class UIComponentJson
|
|
||||||
{
|
|
||||||
public string schemaVersion = "1.0";
|
|
||||||
public string componentType;
|
|
||||||
public string componentVersion;
|
|
||||||
public string exportedAt;
|
|
||||||
public string data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class UIComponentJsonSerializer
|
|
||||||
{
|
|
||||||
public static string Serialize(UIComponent component, bool prettyPrint = true)
|
|
||||||
{
|
|
||||||
if (component == null) throw new ArgumentNullException("component");
|
|
||||||
|
|
||||||
var currentJson = JsonUtility.ToJson(component);
|
|
||||||
var defaultJson = GetDefaultInstanceJson(component.GetType());
|
|
||||||
var dataJson = BuildDataJson(component.GetType(), currentJson, defaultJson);
|
|
||||||
|
|
||||||
var dto = new UIComponentJson
|
|
||||||
{
|
|
||||||
schemaVersion = "1.0",
|
|
||||||
componentType = component.GetType().Name,
|
|
||||||
componentVersion = XChartsMgr.version,
|
|
||||||
exportedAt = DateTime.UtcNow.ToString("o"),
|
|
||||||
data = dataJson
|
|
||||||
};
|
|
||||||
|
|
||||||
var json = JsonUtility.ToJson(dto, prettyPrint);
|
|
||||||
json = ChartJsonDataFieldCodec.ConvertEscapedDataStringToRawObject(json);
|
|
||||||
if (prettyPrint)
|
|
||||||
{
|
|
||||||
object parsedJson;
|
|
||||||
if (SimpleJson.TryParse(json, out parsedJson))
|
|
||||||
json = SimpleJson.Stringify(parsedJson, true);
|
|
||||||
}
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string BuildDataJson(Type componentType, string currentJson, string defaultJson)
|
|
||||||
{
|
|
||||||
if (componentType == null)
|
|
||||||
return JsonDiffPruner.PruneDefaults(currentJson, defaultJson);
|
|
||||||
|
|
||||||
if (string.Equals(componentType.Name, "UITable", StringComparison.Ordinal))
|
|
||||||
return PruneUITableData(componentType, currentJson, defaultJson);
|
|
||||||
|
|
||||||
return JsonDiffPruner.PruneDefaults(currentJson, defaultJson);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string PruneUITableData(Type tableType, string currentJson, string defaultJson)
|
|
||||||
{
|
|
||||||
object currentParsed;
|
|
||||||
object defaultParsed;
|
|
||||||
if (!SimpleJson.TryParse(currentJson, out currentParsed))
|
|
||||||
return JsonDiffPruner.PruneDefaults(currentJson, defaultJson);
|
|
||||||
if (!SimpleJson.TryParse(defaultJson, out defaultParsed))
|
|
||||||
return JsonDiffPruner.PruneDefaults(currentJson, defaultJson);
|
|
||||||
|
|
||||||
var currentRoot = currentParsed as System.Collections.Generic.Dictionary<string, object>;
|
|
||||||
var defaultRoot = defaultParsed as System.Collections.Generic.Dictionary<string, object>;
|
|
||||||
if (currentRoot == null)
|
|
||||||
return JsonDiffPruner.PruneDefaults(currentJson, defaultJson);
|
|
||||||
|
|
||||||
var prunedRootObj = JsonDiffPruner.PruneParsedDefaults(currentRoot, defaultRoot);
|
|
||||||
var prunedRoot = prunedRootObj as System.Collections.Generic.Dictionary<string, object>;
|
|
||||||
if (prunedRoot == null)
|
|
||||||
prunedRoot = new System.Collections.Generic.Dictionary<string, object>();
|
|
||||||
|
|
||||||
object currentRowsObj;
|
|
||||||
if (!currentRoot.TryGetValue("m_Data", out currentRowsObj))
|
|
||||||
return SimpleJson.Stringify(prunedRoot);
|
|
||||||
|
|
||||||
var currentRows = currentRowsObj as System.Collections.Generic.List<object>;
|
|
||||||
if (currentRows == null)
|
|
||||||
return SimpleJson.Stringify(prunedRoot);
|
|
||||||
|
|
||||||
var rowDefaultObj = CreateDefaultParsedObject(tableType.Assembly, "XCharts.Runtime.UI.TableRow");
|
|
||||||
if (rowDefaultObj == null)
|
|
||||||
return JsonDiffPruner.PruneDefaults(currentJson, defaultJson);
|
|
||||||
|
|
||||||
var cellDefaultObj = CreateDefaultParsedObject(tableType.Assembly, "XCharts.Runtime.UI.TableCell");
|
|
||||||
|
|
||||||
var prunedRows = new System.Collections.Generic.List<object>(currentRows.Count);
|
|
||||||
for (int i = 0; i < currentRows.Count; i++)
|
|
||||||
{
|
|
||||||
var prunedRow = PruneTableRowKeepCellShape(currentRows[i], rowDefaultObj, cellDefaultObj);
|
|
||||||
prunedRows.Add(prunedRow ?? new System.Collections.Generic.Dictionary<string, object>());
|
|
||||||
}
|
|
||||||
|
|
||||||
prunedRoot["m_Data"] = prunedRows;
|
|
||||||
return SimpleJson.Stringify(prunedRoot);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static object PruneTableRowKeepCellShape(object rowObj, object rowDefaultObj, object cellDefaultObj)
|
|
||||||
{
|
|
||||||
var rowDict = rowObj as System.Collections.Generic.Dictionary<string, object>;
|
|
||||||
if (rowDict == null)
|
|
||||||
return JsonDiffPruner.PruneParsedDefaults(rowObj, rowDefaultObj);
|
|
||||||
|
|
||||||
object rawCells;
|
|
||||||
rowDict.TryGetValue("m_Data", out rawCells);
|
|
||||||
var currentCells = rawCells as System.Collections.Generic.List<object>;
|
|
||||||
|
|
||||||
var prunedRowObj = JsonDiffPruner.PruneParsedDefaults(rowObj, rowDefaultObj);
|
|
||||||
var prunedRowDict = prunedRowObj as System.Collections.Generic.Dictionary<string, object>;
|
|
||||||
if (prunedRowDict == null)
|
|
||||||
prunedRowDict = new System.Collections.Generic.Dictionary<string, object>();
|
|
||||||
|
|
||||||
if (currentCells != null)
|
|
||||||
{
|
|
||||||
var prunedCells = new System.Collections.Generic.List<object>(currentCells.Count);
|
|
||||||
for (int i = 0; i < currentCells.Count; i++)
|
|
||||||
{
|
|
||||||
var prunedCell = cellDefaultObj != null
|
|
||||||
? JsonDiffPruner.PruneParsedDefaults(currentCells[i], cellDefaultObj)
|
|
||||||
: JsonDiffPruner.PruneParsedDefaults(currentCells[i], null);
|
|
||||||
prunedCells.Add(prunedCell ?? new System.Collections.Generic.Dictionary<string, object>());
|
|
||||||
}
|
|
||||||
prunedRowDict["m_Data"] = prunedCells;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prunedRowDict.Count == 0)
|
|
||||||
return null;
|
|
||||||
return prunedRowDict;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static object CreateDefaultParsedObject(Assembly assembly, string fullTypeName)
|
|
||||||
{
|
|
||||||
if (assembly == null || string.IsNullOrEmpty(fullTypeName))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var type = assembly.GetType(fullTypeName);
|
|
||||||
if (type == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var instance = Activator.CreateInstance(type);
|
|
||||||
if (instance == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var json = JsonUtility.ToJson(instance);
|
|
||||||
object parsed;
|
|
||||||
if (!SimpleJson.TryParse(json, out parsed))
|
|
||||||
return null;
|
|
||||||
return parsed;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetDefaultInstanceJson(Type type)
|
|
||||||
{
|
|
||||||
if (type == null) return "{}";
|
|
||||||
|
|
||||||
GameObject tempObject = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (typeof(MonoBehaviour).IsAssignableFrom(type))
|
|
||||||
{
|
|
||||||
tempObject = new GameObject("__XCharts_UIJson_Default__");
|
|
||||||
tempObject.hideFlags = HideFlags.HideAndDontSave;
|
|
||||||
var defaultComponent = tempObject.AddComponent(type) as UIComponent;
|
|
||||||
if (defaultComponent != null)
|
|
||||||
return JsonUtility.ToJson(defaultComponent);
|
|
||||||
return "{}";
|
|
||||||
}
|
|
||||||
|
|
||||||
var instance = Activator.CreateInstance(type);
|
|
||||||
if (instance == null)
|
|
||||||
return "{}";
|
|
||||||
return JsonUtility.ToJson(instance);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return "{}";
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (tempObject != null)
|
|
||||||
{
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
UnityEngine.Object.DestroyImmediate(tempObject);
|
|
||||||
#else
|
|
||||||
UnityEngine.Object.Destroy(tempObject);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class UIComponentJsonDeserializer
|
|
||||||
{
|
|
||||||
private const string LOG_TAG = "[XCharts] UIComponentJsonDeserializer";
|
|
||||||
|
|
||||||
public static void Deserialize(string json, UIComponent target)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(json)) throw new ArgumentNullException("json");
|
|
||||||
if (target == null) throw new ArgumentNullException("target");
|
|
||||||
|
|
||||||
json = ChartJsonDataFieldCodec.ConvertRawObjectDataToEscapedString(json);
|
|
||||||
|
|
||||||
UIComponentJson dto;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
dto = JsonUtility.FromJson<UIComponentJson>(json);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
throw new ArgumentException(string.Format("Invalid JSON format: {0}", ex.Message), ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dto == null || string.IsNullOrEmpty(dto.schemaVersion))
|
|
||||||
throw new ArgumentException("JSON does not appear to be a valid XCharts UI component export (missing schemaVersion).");
|
|
||||||
if (!dto.schemaVersion.StartsWith("1."))
|
|
||||||
throw new ArgumentException(string.Format("Unsupported schema version '{0}'. This version only supports '1.x'.", dto.schemaVersion));
|
|
||||||
|
|
||||||
ValidateComponentType(dto.componentType, target);
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(dto.data))
|
|
||||||
JsonUtility.FromJsonOverwrite(dto.data, target);
|
|
||||||
|
|
||||||
target.RefreshAllComponent();
|
|
||||||
target.RefreshGraph();
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
UnityEditor.EditorUtility.SetDirty(target);
|
|
||||||
#endif
|
|
||||||
Debug.Log(string.Format("{0}: Import complete for '{1}'.", LOG_TAG, target.GetType().Name));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ValidateComponentType(string typeName, UIComponent target)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(typeName) || target == null) return;
|
|
||||||
|
|
||||||
var resolved = ResolveType(typeName);
|
|
||||||
if (resolved == null) return;
|
|
||||||
|
|
||||||
if (!resolved.IsAssignableFrom(target.GetType()))
|
|
||||||
throw new ArgumentException(string.Format("JSON is for UI component '{0}', target is '{1}'.", resolved.Name, target.GetType().Name));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Type ResolveType(string typeName)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(typeName)) return null;
|
|
||||||
|
|
||||||
var type = Type.GetType(typeName);
|
|
||||||
if (type != null) return type;
|
|
||||||
|
|
||||||
var shortName = typeName.Split(',')[0].Trim();
|
|
||||||
var simpleName = shortName.Contains(".") ? shortName.Substring(shortName.LastIndexOf(".") + 1) : shortName;
|
|
||||||
|
|
||||||
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
type = asm.GetType(shortName);
|
|
||||||
if (type != null) return type;
|
|
||||||
|
|
||||||
var types = asm.GetTypes();
|
|
||||||
for (int i = 0; i < types.Length; i++)
|
|
||||||
{
|
|
||||||
var candidate = types[i];
|
|
||||||
if (candidate == null) continue;
|
|
||||||
if (string.Equals(candidate.Name, simpleName, StringComparison.Ordinal))
|
|
||||||
return candidate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (ReflectionTypeLoadException rtle)
|
|
||||||
{
|
|
||||||
var partialTypes = rtle.Types;
|
|
||||||
if (partialTypes == null) continue;
|
|
||||||
for (int i = 0; i < partialTypes.Length; i++)
|
|
||||||
{
|
|
||||||
var candidate = partialTypes[i];
|
|
||||||
if (candidate == null) continue;
|
|
||||||
if (string.Equals(candidate.Name, simpleName, StringComparison.Ordinal))
|
|
||||||
return candidate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 20febe04d2ef346e196ab69da9c1d7d4
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -140,26 +140,19 @@ namespace XUGL
|
|||||||
|
|
||||||
public static void DrawLine(VertexHelper vh, List<Vector3> points, float width, Color32 color, bool smooth, bool closepath = false)
|
public static void DrawLine(VertexHelper vh, List<Vector3> points, float width, Color32 color, bool smooth, bool closepath = false)
|
||||||
{
|
{
|
||||||
if (points == null || points.Count < 2) return;
|
for (int i = points.Count - 1; i >= 1; i--)
|
||||||
|
|
||||||
// Compact duplicate consecutive points into a reusable buffer to avoid repeated RemoveAt (O(n^2)).
|
|
||||||
s_CurvesPosList.Clear();
|
|
||||||
s_CurvesPosList.Add(points[0]);
|
|
||||||
for (int i = 1; i < points.Count; i++)
|
|
||||||
{
|
{
|
||||||
if (!UGLHelper.IsValueEqualsVector3(points[i], points[i - 1]))
|
if (UGLHelper.IsValueEqualsVector3(points[i], points[i - 1]))
|
||||||
s_CurvesPosList.Add(points[i]);
|
points.RemoveAt(i);
|
||||||
}
|
}
|
||||||
|
if (points.Count < 2) return;
|
||||||
var pts = s_CurvesPosList;
|
else if (points.Count <= 2)
|
||||||
if (pts.Count < 2) return;
|
|
||||||
else if (pts.Count == 2)
|
|
||||||
{
|
{
|
||||||
DrawLine(vh, pts[0], pts[1], width, color);
|
DrawLine(vh, points[0], points[1], width, color);
|
||||||
}
|
}
|
||||||
else if (smooth)
|
else if (smooth)
|
||||||
{
|
{
|
||||||
DrawCurves(vh, pts, width, color, 2, 2, Direction.XAxis, float.NaN, closepath);
|
DrawCurves(vh, points, width, color, 2, 2, Direction.XAxis, float.NaN, closepath);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -171,14 +164,14 @@ namespace XUGL
|
|||||||
var ibp = Vector3.zero;
|
var ibp = Vector3.zero;
|
||||||
var ctp = Vector3.zero;
|
var ctp = Vector3.zero;
|
||||||
var cbp = Vector3.zero;
|
var cbp = Vector3.zero;
|
||||||
if (closepath && !UGLHelper.IsValueEqualsVector3(pts[pts.Count - 1], pts[0]))
|
if (closepath && !UGLHelper.IsValueEqualsVector3(points[points.Count - 1], points[0]))
|
||||||
{
|
{
|
||||||
pts.Add(pts[0]);
|
points.Add(points[0]);
|
||||||
}
|
}
|
||||||
for (int i = 1; i < pts.Count - 1; i++)
|
for (int i = 1; i < points.Count - 1; i++)
|
||||||
{
|
{
|
||||||
bool bitp = true, bibp = true;
|
bool bitp = true, bibp = true;
|
||||||
UGLHelper.GetLinePoints(pts[i - 1], pts[i], pts[i + 1], width,
|
UGLHelper.GetLinePoints(points[i - 1], points[i], points[i + 1], width,
|
||||||
ref ltp, ref lbp,
|
ref ltp, ref lbp,
|
||||||
ref ntp, ref nbp,
|
ref ntp, ref nbp,
|
||||||
ref itp, ref ibp,
|
ref itp, ref ibp,
|
||||||
|
|||||||
Reference in New Issue
Block a user