diff --git a/CHANGELOG-EN.md b/CHANGELOG-EN.md
index f257937a..6d918c6f 100644
--- a/CHANGELOG-EN.md
+++ b/CHANGELOG-EN.md
@@ -32,6 +32,7 @@
## Latest
+* (2021.03.25) Added `Ganttchart`
* (2021.03.22) Added `Theme` `Unbind` button to unbind theme when copying chart #118
* (2021.03.18) Fixed an issue where the check box after `Foldout` in `Inspector` could not be checked
* (2021.03.18) Fixed an issue with `BarChart` displaying an exception in the `0` value
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 095c5502..7d44c99c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -32,6 +32,7 @@
## Latest
+* (2021.03.25) 增加`GanttChart`甘特图
* (2021.03.22) 增加`Theme`的`Unbind`按钮用于解绑复制图表时的主题 #118
* (2021.03.18) 修复`Inspector`下`Foldout`后的勾选框无法选中的问题
* (2021.03.18) 修复`BarChart`在`0`数值时显示异常的问题
diff --git a/Documentation/XCharts配置项手册.md b/Documentation/XCharts配置项手册.md
index 70cfeb9f..1ec65948 100644
--- a/Documentation/XCharts配置项手册.md
+++ b/Documentation/XCharts配置项手册.md
@@ -24,6 +24,7 @@
* [Serie-Ring 环形图](#Serie-Ring)
* [Serie-Liquid 水位图](#Serie-Liquid)
* [Serie-Candlestick K线图](#Serie-Candlestick)
+* [Serie-Gantt 甘特图](#Serie-Gantt)
* [Settings 设置](#Settings)
* [Theme 主题](#Theme)
* [Tooltip 提示框](#Tooltip)
@@ -774,6 +775,24 @@ K线图系列。
* `animation`:起始动画 [SerieAnimation](#SerieAnimation)。
* `data`:系列中的数据项 [SerieData](#SerieData) 数组,K线图至少需要4个维度的数组`[open, close, lowest, highest]`。
+## `Serie-Gantt`
+
+甘特图系列。支持类目轴和时间轴的甘特图,当 `X` 轴为类目轴时,数据为类目的索引,`X` 轴为时间轴时,数据为时间戳(秒为单位)。`Y` 轴默认为类目轴,显示的数据来源于`Serie`的`Data`的`Name`。
+甘特图默认支持开始和结束时间,也可以额外支持实际开始和结束时间。
+
+* `show`:系列是否显示在图表上。
+* `type`:`Gantt`。
+* `name`:系列名称。用于 `tooltip` 的显示,`legend` 的图例筛选。
+* `xAxisIndex`:使用的坐标轴X轴的 `index`,在单个图表实例中存在多个坐标轴的时候有用。
+* `yAxisIndex`:使用的坐标轴Y轴的 `index`,在单个图表实例中存在多个坐标轴的时候有用。
+* `clip`:是否裁剪超出坐标系部分的图形。
+* `large`:是否开启大数据量优化,在数据图形特别多而出现卡顿时候可以开启。开启后配合 largeThreshold 在数据量大于指定阈值的时候对绘制进行优化。缺点:优化后不能自定义设置单个数据项的样式,不能显示Label,折线图不绘制Symbol。
+* `largeThreshold`:开启大数量优化的阈值。只有当开启了large并且数据量大于该阀值时才进入性能模式。
+* `itemStyle`:甘特图的柱条样式,包括设置背景颜色和边框等 [ItemStyle](#ItemStyle)。
+* `emphasis`:高亮样式 [Emphasis](#Emphasis)。
+* `animation`:起始动画 [SerieAnimation](#SerieAnimation)。
+* `data`:系列中的数据项 [SerieData](#SerieData) 数组,甘特图至少需要2个维度的数组`[start, end]`,也支持4个维度的数组`[start, end, actualStart, actualEnd]`。当 X 轴为类目轴时,数据为类目的索引,X 轴为时间轴时,数据为时间戳(秒为单位)。
+
## `Settings`
全局参数设置组件。一般情况下可使用默认值,当有需要时可进行调整。
diff --git a/Documentation/xcharts-configuration-EN.md b/Documentation/xcharts-configuration-EN.md
index 2d89fc0e..e24991ee 100644
--- a/Documentation/xcharts-configuration-EN.md
+++ b/Documentation/xcharts-configuration-EN.md
@@ -26,6 +26,7 @@ __Main component:__
* [Serie-Ring](#Serie-Ring)
* [Serie-Liquid](#Serie-Liquid)
* [Serie-Candlestick](#Serie-Candlestick)
+* [Serie-Gantt](#Serie-Gantt)
* [Settings](#Settings)
* [Theme](#Theme)
* [Title](#Title)
@@ -667,6 +668,24 @@ K线图系列。
* `animation`:起始动画 [SerieAnimation](#SerieAnimation)。
* `data`:系列中的数据项 [SerieData](#SerieData) 数组,K线图至少需要4个维度的数组`[open, close, lowest, highest]`。
+## `Serie-Gantt`
+
+甘特图系列。支持类目轴和时间轴的甘特图,当 `X` 轴为类目轴时,数据为类目的索引,`X` 轴为时间轴时,数据为时间戳(秒为单位)。`Y` 轴默认为类目轴,显示的数据来源于`Serie`的`Data`的`Name`。
+甘特图默认支持开始和结束时间,也可以额外支持实际开始和结束时间。
+
+* `show`:系列是否显示在图表上。
+* `type`:`Gantt`。
+* `name`:系列名称。用于 `tooltip` 的显示,`legend` 的图例筛选。
+* `xAxisIndex`:使用的坐标轴X轴的 `index`,在单个图表实例中存在多个坐标轴的时候有用。
+* `yAxisIndex`:使用的坐标轴Y轴的 `index`,在单个图表实例中存在多个坐标轴的时候有用。
+* `clip`:是否裁剪超出坐标系部分的图形。
+* `large`:是否开启大数据量优化,在数据图形特别多而出现卡顿时候可以开启。开启后配合 largeThreshold 在数据量大于指定阈值的时候对绘制进行优化。缺点:优化后不能自定义设置单个数据项的样式,不能显示Label,折线图不绘制Symbol。
+* `largeThreshold`:开启大数量优化的阈值。只有当开启了large并且数据量大于该阀值时才进入性能模式。
+* `itemStyle`:甘特图的柱条样式,包括设置背景颜色和边框等 [ItemStyle](#ItemStyle)。
+* `emphasis`:高亮样式 [Emphasis](#Emphasis)。
+* `animation`:起始动画 [SerieAnimation](#SerieAnimation)。
+* `data`:系列中的数据项 [SerieData](#SerieData) 数组,甘特图至少需要2个维度的数组`[start, end]`,也支持4个维度的数组`[start, end, actualStart, actualEnd]`。当 X 轴为类目轴时,数据为类目的索引,X 轴为时间轴时,数据为时间戳(秒为单位)。
+
## `Settings`
全局参数设置组件。一般情况下可使用默认值,当有需要时可进行调整。
diff --git a/Editor/BarChartEditor.cs b/Editor/BarChartEditor.cs
index 96435653..db83e0eb 100644
--- a/Editor/BarChartEditor.cs
+++ b/Editor/BarChartEditor.cs
@@ -19,6 +19,7 @@ namespace XCharts
protected override void OnEnable()
{
base.OnEnable();
+ if(target == null) return;
m_Chart = (BarChart)target;
}
diff --git a/Editor/BaseChartEditor.cs b/Editor/BaseChartEditor.cs
index f6904a12..5ca8f65c 100644
--- a/Editor/BaseChartEditor.cs
+++ b/Editor/BaseChartEditor.cs
@@ -123,7 +123,7 @@ namespace XCharts
BlockEnd();
BlockStart();
- m_BaseFoldout = EditorGUILayout.Foldout(m_BaseFoldout, "Base");
+ m_BaseFoldout = EditorGUILayout.Foldout(m_BaseFoldout, "Base", true);
if (m_BaseFoldout)
{
EditorGUILayout.PropertyField(m_Script);
@@ -196,7 +196,7 @@ namespace XCharts
if (all)
{
var flag = m_Flodouts.ContainsKey(prop.displayName) && m_Flodouts[prop.displayName];
- m_Flodouts[prop.displayName] = EditorGUILayout.Foldout(flag, prop.displayName);
+ m_Flodouts[prop.displayName] = EditorGUILayout.Foldout(flag, prop.displayName, true);
if (m_Flodouts[prop.displayName])
{
EditorGUI.indentLevel++;
diff --git a/Editor/CandlestickChartEditor.cs b/Editor/CandlestickChartEditor.cs
index 6d3c047f..52a04dd7 100644
--- a/Editor/CandlestickChartEditor.cs
+++ b/Editor/CandlestickChartEditor.cs
@@ -18,6 +18,7 @@ namespace XCharts
protected override void OnEnable()
{
base.OnEnable();
+ if(target == null) return;
m_Chart = (CandlestickChart)target;
}
}
diff --git a/Editor/CoordinateChartEditor.cs b/Editor/CoordinateChartEditor.cs
index 437b5f2e..deffe8b7 100644
--- a/Editor/CoordinateChartEditor.cs
+++ b/Editor/CoordinateChartEditor.cs
@@ -27,6 +27,7 @@ namespace XCharts
protected override void OnEnable()
{
base.OnEnable();
+ if(target == null) return;
m_Chart = (CoordinateChart)target;
m_Grids = serializedObject.FindProperty("m_Grids");
m_XAxes = serializedObject.FindProperty("m_XAxes");
diff --git a/Editor/GanttChartEditor.cs b/Editor/GanttChartEditor.cs
new file mode 100644
index 00000000..d1a33604
--- /dev/null
+++ b/Editor/GanttChartEditor.cs
@@ -0,0 +1,25 @@
+/************************************************/
+/* */
+/* Copyright (c) 2018 - 2021 monitor1394 */
+/* https://github.com/monitor1394 */
+/* */
+/************************************************/
+
+using UnityEditor;
+
+namespace XCharts
+{
+ ///
+ /// Editor class used to edit UI GanttChart.
+ ///
+ [CustomEditor(typeof(GanttChart), false)]
+ public class GanttChartEditor : CoordinateChartEditor
+ {
+ protected override void OnEnable()
+ {
+ base.OnEnable();
+ if(target == null) return;
+ m_Chart = (GanttChart)target;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Editor/GanttChartEditor.cs.meta b/Editor/GanttChartEditor.cs.meta
new file mode 100644
index 00000000..336d56c4
--- /dev/null
+++ b/Editor/GanttChartEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c1b5a1dfca8e5476b98c807c66783d85
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Editor/GaugeChartEditor.cs b/Editor/GaugeChartEditor.cs
index a89de203..0d9d6d42 100644
--- a/Editor/GaugeChartEditor.cs
+++ b/Editor/GaugeChartEditor.cs
@@ -18,6 +18,7 @@ namespace XCharts
protected override void OnEnable()
{
base.OnEnable();
+ if(target == null) return;
m_Chart = (GaugeChart)target;
}
}
diff --git a/Editor/HeatmapChartEditor.cs b/Editor/HeatmapChartEditor.cs
index b1e81067..ed765800 100644
--- a/Editor/HeatmapChartEditor.cs
+++ b/Editor/HeatmapChartEditor.cs
@@ -18,6 +18,7 @@ namespace XCharts
protected override void OnEnable()
{
base.OnEnable();
+ if(target == null) return;
m_Chart = (HeatmapChart)target;
}
}
diff --git a/Editor/LineChartEditor.cs b/Editor/LineChartEditor.cs
index d321bb74..70f2d635 100644
--- a/Editor/LineChartEditor.cs
+++ b/Editor/LineChartEditor.cs
@@ -18,6 +18,7 @@ namespace XCharts
protected override void OnEnable()
{
base.OnEnable();
+ if(target == null) return;
m_Chart = (LineChart)target;
}
}
diff --git a/Editor/PieChartEditor.cs b/Editor/PieChartEditor.cs
index d410a4b7..1fc0f6f5 100644
--- a/Editor/PieChartEditor.cs
+++ b/Editor/PieChartEditor.cs
@@ -18,6 +18,7 @@ namespace XCharts
protected override void OnEnable()
{
base.OnEnable();
+ if(target == null) return;
m_Chart = (PieChart)target;
}
}
diff --git a/Editor/PolarChartEditor.cs b/Editor/PolarChartEditor.cs
index 57b3b476..b1f02ced 100644
--- a/Editor/PolarChartEditor.cs
+++ b/Editor/PolarChartEditor.cs
@@ -22,6 +22,7 @@ namespace XCharts
protected override void OnEnable()
{
base.OnEnable();
+ if(target == null) return;
m_Chart = (PolarChart)target;
m_Polars = serializedObject.FindProperty("m_Polars");
m_RadiusAxes = serializedObject.FindProperty("m_RadiusAxes");
diff --git a/Editor/PropertyDrawers/AxisDrawer.cs b/Editor/PropertyDrawers/AxisDrawer.cs
index c93aa7a5..a9f29c5c 100644
--- a/Editor/PropertyDrawers/AxisDrawer.cs
+++ b/Editor/PropertyDrawers/AxisDrawer.cs
@@ -42,7 +42,7 @@ namespace XCharts
}
EditorGUI.EndChangeCheck();
}
- if (type == Axis.AxisType.Value)
+ if (type == Axis.AxisType.Value || type == Axis.AxisType.Time)
{
PropertyField(prop, "m_MinMaxType");
Axis.AxisMinMaxType minMaxType = (Axis.AxisMinMaxType)m_MinMaxType.enumValueIndex;
@@ -60,7 +60,10 @@ namespace XCharts
break;
}
PropertyField(prop, "m_CeilRate");
- PropertyField(prop, "m_Inverse");
+ if (type == Axis.AxisType.Value)
+ {
+ PropertyField(prop, "m_Inverse");
+ }
}
PropertyField(prop, "m_SplitNumber");
if (type == Axis.AxisType.Category)
diff --git a/Editor/PropertyDrawers/SerieDrawer.cs b/Editor/PropertyDrawers/SerieDrawer.cs
index cee241b9..cd96e351 100644
--- a/Editor/PropertyDrawers/SerieDrawer.cs
+++ b/Editor/PropertyDrawers/SerieDrawer.cs
@@ -197,6 +197,17 @@ namespace XCharts
PropertyField(prop, "m_Label");
PropertyField(prop, "m_Emphasis");
break;
+ case SerieType.Gantt:
+ PropertyField(prop, "m_XAxisIndex");
+ PropertyField(prop, "m_YAxisIndex");
+ PropertyField(prop, "m_BarWidth");
+ PropertyField(prop, "m_Clip");
+ PropertyField(prop, "m_Large");
+ PropertyField(prop, "m_LargeThreshold");
+ PropertyField(prop, "m_ItemStyle");
+ PropertyField(prop, "m_Label");
+ PropertyField(prop, "m_Emphasis");
+ break;
}
PropertyField(prop, "m_Animation");
DrawData(pos, prop, serieType, ref m_DrawRect);
@@ -340,7 +351,7 @@ namespace XCharts
var startX = drawRect.x + EditorGUIUtility.labelWidth - EditorGUI.indentLevel * 15 + gap;
var dataWidTotal = (currentWidth - (startX + 20.5f + 1));
var dataWid = dataWidTotal / fieldCount;
- var xWid = dataWid - 4;
+ var xWid = dataWid - 2;
for (int i = 0; i < dimension; i++)
{
var dataCount = i < 1 ? 2 : i + 1;
@@ -407,7 +418,7 @@ namespace XCharts
var str = prop.propertyPath.Substring(sindex + 1, eindex - sindex - 1);
int.TryParse(str, out index);
}
- if (index >= m_DataFoldout.Count)
+ while (index >= m_DataFoldout.Count)
{
m_DataFoldout.Add(false);
}
diff --git a/Editor/RingChartEditor.cs b/Editor/RingChartEditor.cs
index cf054b61..2c76dd68 100644
--- a/Editor/RingChartEditor.cs
+++ b/Editor/RingChartEditor.cs
@@ -18,6 +18,7 @@ namespace XCharts
protected override void OnEnable()
{
base.OnEnable();
+ if(target == null) return;
m_Chart = (RingChart)target;
}
}
diff --git a/Editor/ScatterChartEditor.cs b/Editor/ScatterChartEditor.cs
index e6c0494b..1453d79c 100644
--- a/Editor/ScatterChartEditor.cs
+++ b/Editor/ScatterChartEditor.cs
@@ -18,6 +18,7 @@ namespace XCharts
protected override void OnEnable()
{
base.OnEnable();
+ if(target == null) return;
m_Chart = (ScatterChart)target;
}
}
diff --git a/Editor/XChartEditor.cs b/Editor/XChartEditor.cs
index e7364e1a..884f4b06 100644
--- a/Editor/XChartEditor.cs
+++ b/Editor/XChartEditor.cs
@@ -146,11 +146,11 @@ namespace XCharts
AddChart("LiquidChart");
}
- [MenuItem("XCharts/CandlestickChart", priority = 54)]
- [MenuItem("GameObject/XCharts/CandlestickChart", priority = 54)]
- public static void AddCandlestickChart()
+ [MenuItem("XCharts/GanttChart", priority = 54)]
+ [MenuItem("GameObject/XCharts/GanttChart", priority = 54)]
+ public static void AddGanttChart()
{
- AddChart("CandlestickChart");
+ AddChart("GanttChart");
}
[MenuItem("XCharts/Themes Reload")]
diff --git a/Examples/Runtime/Example100_Gantt_Category.cs b/Examples/Runtime/Example100_Gantt_Category.cs
new file mode 100644
index 00000000..6b5d666b
--- /dev/null
+++ b/Examples/Runtime/Example100_Gantt_Category.cs
@@ -0,0 +1,80 @@
+/************************************************/
+/* */
+/* Copyright (c) 2018 - 2021 monitor1394 */
+/* https://github.com/monitor1394 */
+/* */
+/************************************************/
+
+
+using UnityEngine;
+
+namespace XCharts.Examples
+{
+ [DisallowMultipleComponent]
+ [ExecuteInEditMode]
+ public class Example100_Gantt_Category : MonoBehaviour
+ {
+ private GanttChart chart;
+ private float updateTime;
+ public int dayCount = 10;
+ public int taskCount = 5;
+
+ void Awake()
+ {
+ chart = gameObject.GetComponent();
+ if (chart == null)
+ {
+ chart = gameObject.AddComponent();
+ }
+ GenerateCategoryData();
+ }
+
+ void Update()
+ {
+ if (Input.GetKeyDown(KeyCode.Space))
+ {
+ AddData();
+ }
+ }
+
+ void AddData()
+ {
+ for (int i = 0; i < taskCount; i++)
+ {
+ var taskName = "task-" + (i + 1);
+ var startIndex = Random.Range(0, (int)(dayCount * 2.0f / 3));
+ var endIndex = Random.Range(startIndex, dayCount);
+ chart.UpdateData(0, i, 0, startIndex);
+ chart.UpdateData(0, i, 1, endIndex);
+ }
+ }
+
+ void GenerateCategoryData()
+ {
+ chart.RemoveData();
+
+ chart.grid.left = 100;
+ chart.xAxis0.type = Axis.AxisType.Category;
+ chart.xAxis0.boundaryGap = false;
+ chart.xAxis0.splitNumber = dayCount;
+
+ chart.yAxis0.type = Axis.AxisType.Category;
+ chart.yAxis0.boundaryGap = true;
+ chart.yAxis0.splitNumber = 0;
+
+ for (int i = 0; i < dayCount; i++)
+ {
+ chart.AddXAxisData("day" + (i + 1));
+ }
+
+ var serie = chart.AddSerie(SerieType.Gantt, "任务进度表");
+ for (int i = 0; i < taskCount; i++)
+ {
+ var taskName = "task-" + (i + 1);
+ var startIndex = Random.Range(0, (int)(dayCount * 2.0f / 3));
+ var endIndex = Random.Range(startIndex, dayCount);
+ chart.AddData(0, startIndex, endIndex, taskName);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Examples/Runtime/Example100_Gantt_Category.cs.meta b/Examples/Runtime/Example100_Gantt_Category.cs.meta
new file mode 100644
index 00000000..1c2b67ac
--- /dev/null
+++ b/Examples/Runtime/Example100_Gantt_Category.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c383c3eae67ed461693e18a807b2e599
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Examples/Runtime/Example101_Gantt_Time.cs b/Examples/Runtime/Example101_Gantt_Time.cs
new file mode 100644
index 00000000..ebe47aa8
--- /dev/null
+++ b/Examples/Runtime/Example101_Gantt_Time.cs
@@ -0,0 +1,99 @@
+/************************************************/
+/* */
+/* Copyright (c) 2018 - 2021 monitor1394 */
+/* https://github.com/monitor1394 */
+/* */
+/************************************************/
+
+
+using UnityEngine;
+
+namespace XCharts.Examples
+{
+ [DisallowMultipleComponent]
+ [ExecuteInEditMode]
+ public class Example101_Gantt_Time : MonoBehaviour
+ {
+ private GanttChart chart;
+ private float updateTime;
+ public int taskCount = 5;
+
+ void Awake()
+ {
+ chart = gameObject.GetComponent();
+ if (chart == null)
+ {
+ chart = gameObject.AddComponent();
+ }
+ GenerateTimeData();
+ }
+
+ void Update()
+ {
+ if (Input.GetKeyDown(KeyCode.Space))
+ {
+ AddData();
+ }
+ }
+
+ void AddData()
+ {
+ chart.ClearData();
+ for (int i = 0; i < taskCount; i++)
+ {
+ var taskName = "张三-任务-" + (i + 1);
+ var nowTimestamp = DateTimeUtil.GetTimestamp();
+ var startTimestamp = nowTimestamp + Random.Range(1, 6) * 3600 * 24;
+ var endTimestamp = startTimestamp + Random.Range(1, 10) * 3600 * 24;
+ chart.AddData(0, startTimestamp, endTimestamp, taskName);
+ }
+ var serie2 = chart.AddSerie(SerieType.Gantt, "李四");
+ for (int i = 0; i < taskCount; i++)
+ {
+ var taskName = "李四-任务-" + (i + 1);
+ var nowTimestamp = DateTimeUtil.GetTimestamp();
+ var startTimestamp = nowTimestamp + Random.Range(1, 6) * 3600 * 24;
+ var endTimestamp = startTimestamp + Random.Range(1, 10) * 3600 * 24;
+ chart.AddData(1, startTimestamp, endTimestamp, taskName);
+ }
+ }
+
+ void GenerateTimeData()
+ {
+ chart.RemoveData();
+
+ chart.grid.left = 100;
+ chart.xAxis0.type = Axis.AxisType.Time;
+ chart.xAxis0.boundaryGap = false;
+ chart.xAxis0.splitNumber = 5;
+
+ chart.xAxis0.axisLabel.numericFormatter = "HH:mm:ss";
+ chart.xAxis0.axisLabel.formatter = "time:{value}";
+
+ chart.yAxis0.type = Axis.AxisType.Category;
+ chart.yAxis0.boundaryGap = true;
+ chart.yAxis0.splitNumber = 0;
+
+
+ var serie1 = chart.AddSerie(SerieType.Gantt, "张三");
+ serie1.label.show = true;
+ for (int i = 0; i < taskCount; i++)
+ {
+ var taskName = "张三-任务-" + (i + 1);
+ var nowTimestamp = DateTimeUtil.GetTimestamp();
+ var startTimestamp = nowTimestamp + Random.Range(1, 6) * 3600 * 24;
+ var endTimestamp = startTimestamp + Random.Range(1, 10) * 3600 * 24;
+ chart.AddData(0, startTimestamp, endTimestamp, taskName);
+ }
+ var serie2 = chart.AddSerie(SerieType.Gantt, "李四");
+ for (int i = 0; i < taskCount; i++)
+ {
+ var taskName = "李四-任务-" + (i + 1);
+ var nowTimestamp = DateTimeUtil.GetTimestamp();
+ var startTimestamp = nowTimestamp + Random.Range(1, 6) * 3600 * 24;
+ var endTimestamp = startTimestamp + Random.Range(1, 10) * 3600 * 24;
+ chart.AddData(1, startTimestamp, endTimestamp, taskName);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Examples/Runtime/Example101_Gantt_Time.cs.meta b/Examples/Runtime/Example101_Gantt_Time.cs.meta
new file mode 100644
index 00000000..8adb651e
--- /dev/null
+++ b/Examples/Runtime/Example101_Gantt_Time.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d546ff7dfa6104a739c1accdb415ef54
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Examples/Runtime/Example90_Candlestick.cs b/Examples/Runtime/Example90_Candlestick.cs
index 5da1af93..1ab23d78 100644
--- a/Examples/Runtime/Example90_Candlestick.cs
+++ b/Examples/Runtime/Example90_Candlestick.cs
@@ -45,7 +45,6 @@ namespace XCharts.Examples
chart.ClearData();
var xValue = System.DateTime.Now;
- var minute = 60 * 1000;
var baseValue = Random.Range(0f, 1f) * 12000;
var boxVals = new float[4];
var dayRange = 12;
diff --git a/Runtime/API/BaseGraph_API.cs b/Runtime/API/BaseGraph_API.cs
index fe954bdb..4c99d1ca 100644
--- a/Runtime/API/BaseGraph_API.cs
+++ b/Runtime/API/BaseGraph_API.cs
@@ -134,6 +134,7 @@ namespace XCharts
public void RefreshAllComponent()
{
SetAllComponentDirty();
+ RefreshGraph();
}
///
diff --git a/Runtime/API/CoordinateChart_API.cs b/Runtime/API/CoordinateChart_API.cs
index c42289ae..357905d1 100644
--- a/Runtime/API/CoordinateChart_API.cs
+++ b/Runtime/API/CoordinateChart_API.cs
@@ -89,8 +89,16 @@ namespace XCharts
///
public void ClearAxisData()
{
- foreach (var item in m_XAxes) item.data.Clear();
- foreach (var item in m_YAxes) item.data.Clear();
+ foreach (var axis in m_XAxes)
+ {
+ axis.data.Clear();
+ axis.SetAllDirty();
+ }
+ foreach (var axis in m_YAxes)
+ {
+ axis.data.Clear();
+ axis.SetAllDirty();
+ }
}
///
diff --git a/Runtime/Component/Main/Axis.cs b/Runtime/Component/Main/Axis.cs
index da7078dd..c80e4374 100644
--- a/Runtime/Component/Main/Axis.cs
+++ b/Runtime/Component/Main/Axis.cs
@@ -39,7 +39,12 @@ namespace XCharts
/// Log axis, suitable for log data.
/// 对数轴。适用于对数数据。
///
- Log
+ Log,
+ ///
+ /// Time axis, suitable for continuous time series data.
+ /// 时间轴。适用于连续的时序数据。
+ ///
+ Time
}
///
@@ -408,6 +413,7 @@ namespace XCharts
public int runtimeMaxLogIndex { get { return logBaseE ? (int)Mathf.Log(runtimeMaxValue) : (int)Mathf.Log(runtimeMaxValue, logBase); } }
internal bool runtimeLastCheckInverse { get; set; }
internal float runtimeMinMaxRange { get { return m_MinMaxValueRange; } set { m_MinMaxValueRange = value; } }
+ internal List runtimeData { get { return m_RuntimeData; } }
private int filterStart;
private int filterEnd;
private int filterMinShow;
@@ -426,6 +432,7 @@ namespace XCharts
private float m_RuntimeMaxValueUpdateTime;
private bool m_RuntimeMinValueFirstChanged = true;
private bool m_RuntimeMaxValueFirstChanged = true;
+ protected List m_RuntimeData = new List();
public Axis Clone()
{
@@ -484,6 +491,7 @@ namespace XCharts
public void ClearData()
{
m_Data.Clear();
+ m_RuntimeData.Clear();
SetAllDirty();
}
@@ -574,10 +582,14 @@ namespace XCharts
}
else
{
- return m_Data;
+ return m_Data.Count > 0 ? m_Data : m_RuntimeData;
}
}
+ internal List GetDataList(){
+ return m_Data.Count > 0 ? m_Data : m_RuntimeData;
+ }
+
private List emptyFliter = new List();
///
/// 更新dataZoom对应的类目数据列表
@@ -596,24 +608,25 @@ namespace XCharts
filterEnd = endIndex;
filterMinShow = dataZoom.minShowNum;
m_NeedUpdateFilterData = false;
- if (m_Data.Count > 0)
+ var data = GetDataList();
+ if (data.Count > 0)
{
var count = endIndex == startIndex ? 1 : endIndex - startIndex + 1;
if (count < dataZoom.minShowNum)
{
- if (dataZoom.minShowNum > m_Data.Count) count = m_Data.Count;
+ if (dataZoom.minShowNum > data.Count) count = data.Count;
else count = dataZoom.minShowNum;
}
- if (startIndex + count > m_Data.Count)
+ if (startIndex + count > data.Count)
{
int start = endIndex - count;
- filterData = m_Data.GetRange(start < 0 ? 0 : start, count);
+ filterData = data.GetRange(start < 0 ? 0 : start, count);
}
- else filterData = m_Data.GetRange(startIndex, count);
+ else filterData = data.GetRange(startIndex, count);
}
else
{
- filterData = m_Data;
+ filterData = data;
}
}
else if (endIndex == 0)
diff --git a/Runtime/Component/Main/Serie.cs b/Runtime/Component/Main/Serie.cs
index a89a273e..3811bc88 100644
--- a/Runtime/Component/Main/Serie.cs
+++ b/Runtime/Component/Main/Serie.cs
@@ -64,6 +64,10 @@ namespace XCharts
/// K线图。K线图的data至少包含四个数据:[open, close, lowest, highest]
///
Candlestick,
+ ///
+ /// 甘特图。甘特图的data至少包含两个数据:[start, end]
+ ///
+ Gantt,
}
///
@@ -781,7 +785,7 @@ namespace XCharts
///
/// 数据项里的数据维数。
///
- public int showDataDimension { get { return m_ShowDataDimension; } }
+ public int showDataDimension { get { return m_ShowDataDimension; } internal set { m_ShowDataDimension = value; } }
///
/// 在Editor的inpsector上是否显示name参数
///
@@ -1234,6 +1238,7 @@ namespace XCharts
{
CheckMaxCache();
var serieData = SerieDataPool.Get();
+ serieData.data.Clear();
serieData.data.Add(xValue);
serieData.data.Add(yValue);
serieData.name = dataName;
@@ -1245,10 +1250,20 @@ namespace XCharts
return serieData;
}
+ ///
+ /// 添加 (open, close, lowest, heighest) 数据
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
public SerieData AddData(float open, float close, float lowest, float heighest, string dataName = null)
{
CheckMaxCache();
var serieData = SerieDataPool.Get();
+ serieData.data.Clear();
serieData.data.Add(open);
serieData.data.Add(close);
serieData.data.Add(lowest);
@@ -1678,7 +1693,9 @@ namespace XCharts
return type == SerieType.Line
|| type == SerieType.Bar
|| type == SerieType.Scatter
- || type == SerieType.Heatmap;
+ || type == SerieType.Heatmap
+ || type == SerieType.Gantt
+ || type == SerieType.Candlestick;
}
///
diff --git a/Runtime/Component/Sub/AxisLabel.cs b/Runtime/Component/Sub/AxisLabel.cs
index 148351d9..822776e4 100644
--- a/Runtime/Component/Sub/AxisLabel.cs
+++ b/Runtime/Component/Sub/AxisLabel.cs
@@ -188,9 +188,8 @@ namespace XCharts
}
else
{
- var content = m_Formatter.Replace("{value}", category);
- content = content.Replace("\\n", "\n");
- content = content.Replace("
", "\n");
+ var content = m_Formatter;
+ FormatterHelper.ReplaceAxisLabelContent(ref content, category);
return m_TextLimit.GetLimitContent(content);
}
}
@@ -224,5 +223,21 @@ namespace XCharts
return content;
}
}
+
+ public string GetFormatterDateTime(DateTime dateTime)
+ {
+ var format = string.IsNullOrEmpty(numericFormatter) ? "yyyy/M/d" : numericFormatter;
+ if (!string.IsNullOrEmpty(m_Formatter))
+ {
+ var content = m_Formatter;
+ FormatterHelper.ReplaceAxisLabelContent(ref content, dateTime.ToString(format));
+ return m_TextLimit.GetLimitContent(content);
+ }
+ else
+ {
+ var content = dateTime.ToString(format);
+ return m_TextLimit.GetLimitContent(content);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Runtime/Component/Sub/SerieData.cs b/Runtime/Component/Sub/SerieData.cs
index 6966a7f5..ad09a74b 100644
--- a/Runtime/Component/Sub/SerieData.cs
+++ b/Runtime/Component/Sub/SerieData.cs
@@ -5,11 +5,8 @@
/* */
/************************************************/
-using System.Linq;
using System.Collections.Generic;
using UnityEngine;
-using UnityEngine.UI;
-using System;
namespace XCharts
{
@@ -191,6 +188,10 @@ namespace XCharts
///
public float runtimePieOffsetRadius { get; internal set; }
public Vector3 runtimePosition { get; internal set; }
+ ///
+ /// 绘制区域。
+ ///
+ public Rect runtimeRect { get; internal set; }
public float runtimeAngle { get; internal set; }
public Vector3 runtiemPieOffsetCenter { get; internal set; }
public float runtimeStackHig { get; internal set; }
diff --git a/Runtime/Component/Sub/TitleStyle.cs b/Runtime/Component/Sub/TitleStyle.cs
index fd532327..a460d9bd 100644
--- a/Runtime/Component/Sub/TitleStyle.cs
+++ b/Runtime/Component/Sub/TitleStyle.cs
@@ -85,5 +85,13 @@ namespace XCharts
runtimeText.SetText(text);
}
}
+
+ public void SetColor(Color color)
+ {
+ if (runtimeText != null)
+ {
+ runtimeText.SetColor(color);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Runtime/GanttChart.cs b/Runtime/GanttChart.cs
new file mode 100644
index 00000000..f3c2525b
--- /dev/null
+++ b/Runtime/GanttChart.cs
@@ -0,0 +1,137 @@
+
+/************************************************/
+/* */
+/* Copyright (c) 2018 - 2021 monitor1394 */
+/* https://github.com/monitor1394 */
+/* */
+/************************************************/
+
+using UnityEngine;
+
+namespace XCharts
+{
+ [AddComponentMenu("XCharts/GanttChart", 22)]
+ [ExecuteInEditMode]
+ [RequireComponent(typeof(RectTransform))]
+ [DisallowMultipleComponent]
+ public partial class GanttChart : CoordinateChart
+ {
+
+#if UNITY_EDITOR
+ protected override void Reset()
+ {
+ base.Reset();
+ title.text = "GanttChart";
+ var xCount = 5;
+ var yCount = 5;
+
+ m_Grids[0].left = 60;
+ m_Grids[0].right = 50;
+ m_XAxes[0].type = Axis.AxisType.Time;
+ m_XAxes[0].boundaryGap = false;
+ m_XAxes[0].splitNumber = xCount;
+ m_YAxes[0].type = Axis.AxisType.Category;
+ m_YAxes[0].boundaryGap = true;
+ m_YAxes[0].splitNumber = 0;
+
+ RemoveData();
+ SerieTemplate.AddDefaultTimeGanttSerie(this, "task", yCount);
+ }
+#endif
+ protected override void GetSeriesMinMaxValue(Axis axis, int axisIndex, out float tempMinValue, out float tempMaxValue)
+ {
+ tempMinValue = int.MaxValue;
+ tempMaxValue = int.MinValue;
+ foreach (var serie in m_Series.list)
+ {
+ if (serie.type != SerieType.Gantt) continue;
+ if (serie.xAxisIndex != axis.index) continue;
+ foreach (var serieData in serie.data)
+ {
+ if (serieData.data.Count >= 2)
+ {
+ var xData = serieData.data[0];
+ var yData = serieData.data[1];
+ if (xData < tempMinValue) tempMinValue = xData;
+ if (yData > tempMaxValue) tempMaxValue = yData;
+ }
+ }
+ }
+ if (tempMinValue == int.MaxValue) tempMinValue = 0;
+ if (tempMaxValue == int.MinValue) tempMaxValue = 0;
+ //AxisHelper.AdjustMinMaxValue(axis, ref tempMinValue, ref tempMaxValue, true, 60);
+ }
+
+ protected override void OnRefreshLabel()
+ {
+ for (int i = 0; i < m_Series.Count; i++)
+ {
+ var serie = m_Series.GetSerie(i);
+ if (serie.IsPerformanceMode()) continue;
+ if (serie.type != SerieType.Gantt) continue;
+ foreach (var serieData in serie.data)
+ {
+ if (serieData.labelObject == null) continue;
+ var serieLabel = SerieHelper.GetSerieLabel(serie, serieData);
+ var labelShow = serie.show && serieLabel.show;
+ serieData.SetLabelActive(labelShow);
+ if (labelShow)
+ {
+ var labelColor = serieLabel.textStyle.GetColor(m_Theme.axis.textColor);
+ var labelPos = serieData.runtimePosition;
+ SerieLabelHelper.ResetLabel(serieData.labelObject.label, serieLabel, m_Theme, i);
+ serieData.labelObject.SetPosition(labelPos);
+ serieData.labelObject.SetLabelColor(labelColor);
+ serieData.labelObject.SetText(serieData.name);
+ }
+ }
+ }
+ }
+
+ protected override void UpdateTooltipValue(Vector2 local)
+ {
+ var grid = GetGrid(tooltip.runtimeGridIndex);
+ if (grid == null) return;
+ tooltip.runtimeDataIndex.Clear();
+ foreach (var serie in m_Series.list)
+ {
+ var serieGrid = GetSerieGridOrDefault(serie);
+ if (grid.index != serieGrid.index) continue;
+ for (int i = 0; i < serie.data.Count; i++)
+ {
+ var serieData = serie.GetSerieData(i);
+ var highlight = serieData.runtimeRect.Contains(local);
+ serieData.highlighted = highlight;
+ if (highlight)
+ {
+
+ tooltip.runtimeDataIndex.Add(serie.index);
+ tooltip.runtimeDataIndex.Add(i);
+ return;
+ }
+ }
+ }
+ }
+
+ protected override void UpdateTooltip()
+ {
+ if (tooltip.runtimeDataIndex.Count == 0)
+ {
+ if (tooltip.IsActive())
+ {
+ tooltip.SetActive(false);
+ RefreshChart();
+ }
+ return;
+ }
+ var serieIndex = tooltip.runtimeDataIndex[0];
+ var dataIndex = tooltip.runtimeDataIndex[1];
+ var serie = m_Series.GetSerie(serieIndex);
+ if (serie == null) return;
+ var serieData = serie.GetSerieData(dataIndex);
+ var category = serieData == null ? serie.name : serieData.name;
+ TooltipHelper.SetContentAndPosition(tooltip, category, chartRect);
+ tooltip.SetActive(true);
+ }
+ }
+}
diff --git a/Runtime/GanttChart.cs.meta b/Runtime/GanttChart.cs.meta
new file mode 100644
index 00000000..2b699350
--- /dev/null
+++ b/Runtime/GanttChart.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d20186b31c74d4711870603e97fd65bc
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Helper/FormatterHelper.cs b/Runtime/Helper/FormatterHelper.cs
index d3fef71b..55e5af88 100644
--- a/Runtime/Helper/FormatterHelper.cs
+++ b/Runtime/Helper/FormatterHelper.cs
@@ -198,6 +198,20 @@ namespace XCharts
content = TrimAndReplaceLine(content);
}
+ public static void ReplaceAxisLabelContent(ref string content, string value)
+ {
+ var mc = s_RegexForAxisLabel.Matches(content);
+ foreach (var m in mc)
+ {
+ var old = m.ToString();
+ var args = s_RegexSubForAxisLabel.Matches(m.ToString());
+ var argsCount = args.Count;
+ if (argsCount <= 0) continue;
+ content = content.Replace(old, value);
+ }
+ content = TrimAndReplaceLine(content);
+ }
+
public static void ReplaceSerieLabelContent(ref string content, string numericFormatter, float value, float total,
string serieName, string dataName)
{
diff --git a/Runtime/Internal/BaseChart.cs b/Runtime/Internal/BaseChart.cs
index 246b3986..41a26bed 100644
--- a/Runtime/Internal/BaseChart.cs
+++ b/Runtime/Internal/BaseChart.cs
@@ -284,6 +284,7 @@ namespace XCharts
foreach (var component in m_Radars) component.SetAllDirty();
m_ReinitLabel = true;
m_ReinitTitle = true;
+ m_RefreshChart = true;
}
protected override void OnDestroy()
diff --git a/Runtime/Internal/CoordinateChart.cs b/Runtime/Internal/CoordinateChart.cs
index 1eda45fc..2bf8017c 100644
--- a/Runtime/Internal/CoordinateChart.cs
+++ b/Runtime/Internal/CoordinateChart.cs
@@ -178,6 +178,9 @@ namespace XCharts
case SerieType.Candlestick:
DrawCandlestickSerie(vh, colorIndex, serie);
break;
+ case SerieType.Gantt:
+ DrawGanttSerie(vh, colorIndex, serie);
+ break;
}
}
@@ -240,7 +243,7 @@ namespace XCharts
}
}
- protected void UpdateTooltipValue(Vector2 local)
+ protected virtual void UpdateTooltipValue(Vector2 local)
{
var isCartesian = IsValue();
var dataCount = m_Series.list.Count > 0 ? m_Series.list[0].GetDataList(dataZoom).Count : 0;
@@ -495,6 +498,7 @@ namespace XCharts
yAxis.painter = m_Painter;
yAxis.refreshComponent = delegate ()
{
+ InitAxisRuntimeData(yAxis);
string objName = ChartCached.GetYAxisName(yAxisIndex);
var axisObj = ChartHelper.AddObject(objName, transform, graphAnchorMin,
graphAnchorMax, chartPivot, new Vector2(chartWidth, chartHeight));
@@ -593,6 +597,26 @@ namespace XCharts
yAxis.refreshComponent();
}
+ private void InitAxisRuntimeData(Axis axis)
+ {
+ if (axis.type != Axis.AxisType.Category) return;
+ if (axis.data.Count > 0) return;
+ var isYAxis = axis is YAxis;
+ if (this is GanttChart)
+ {
+ axis.runtimeData.Clear();
+ for (int i = 0; i < m_Series.Count; i++)
+ {
+ var serie = m_Series.GetSerie(i);
+ if(serie.yAxisIndex != axis.index) continue;
+ for (int j = serie.data.Count - 1; j >= 0; j--)
+ {
+ axis.runtimeData.Add(serie.data[j].name);
+ }
+ }
+ }
+ }
+
private void InitAxisX()
{
for (int i = 0; i < m_XAxes.Count; i++)
@@ -782,7 +806,7 @@ namespace XCharts
return new Vector3(grid.runtimeX + scaleWid, posY);
}
- private void CheckMinMaxValue()
+ protected virtual void CheckMinMaxValue()
{
if (m_XAxes == null || m_YAxes == null) return;
for (int i = 0; i < m_XAxes.Count; i++)
@@ -806,23 +830,7 @@ namespace XCharts
}
float tempMinValue = 0;
float tempMaxValue = 0;
-
- if (IsValue())
- {
- if (axis is XAxis)
- {
- SeriesHelper.GetXMinMaxValue(m_Series, null, axisIndex, true, axis.inverse, out tempMinValue, out tempMaxValue);
- }
- else
- {
- SeriesHelper.GetYMinMaxValue(m_Series, null, axisIndex, true, axis.inverse, out tempMinValue, out tempMaxValue);
- }
- }
- else
- {
- SeriesHelper.GetYMinMaxValue(m_Series, null, axisIndex, false, axis.inverse, out tempMinValue, out tempMaxValue);
- }
- AxisHelper.AdjustMinMaxValue(axis, ref tempMinValue, ref tempMaxValue, true);
+ GetSeriesMinMaxValue(axis, axisIndex, out tempMinValue, out tempMaxValue);
if (tempMinValue != axis.runtimeMinValue || tempMaxValue != axis.runtimeMaxValue)
{
m_IsPlayingAnimation = true;
@@ -866,6 +874,26 @@ namespace XCharts
}
}
+ protected virtual void GetSeriesMinMaxValue(Axis axis, int axisIndex, out float tempMinValue, out float tempMaxValue)
+ {
+ if (IsValue())
+ {
+ if (axis is XAxis)
+ {
+ SeriesHelper.GetXMinMaxValue(m_Series, null, axisIndex, true, axis.inverse, out tempMinValue, out tempMaxValue);
+ }
+ else
+ {
+ SeriesHelper.GetYMinMaxValue(m_Series, null, axisIndex, true, axis.inverse, out tempMinValue, out tempMaxValue);
+ }
+ }
+ else
+ {
+ SeriesHelper.GetYMinMaxValue(m_Series, null, axisIndex, false, axis.inverse, out tempMinValue, out tempMaxValue);
+ }
+ AxisHelper.AdjustMinMaxValue(axis, ref tempMinValue, ref tempMaxValue, true);
+ }
+
protected void UpdateAxisLabelText(Axis axis)
{
var grid = GetAxisGridOrDefault(axis);
@@ -1571,6 +1599,7 @@ namespace XCharts
{
base.OnRefreshLabel();
var anyPercentStack = SeriesHelper.IsPercentStack(m_Series, SerieType.Bar);
+
for (int i = 0; i < m_Series.Count; i++)
{
var serie = m_Series.GetSerie(i);
@@ -1578,6 +1607,7 @@ namespace XCharts
if (!serie.IsCoordinateSerie()) continue;
var total = serie.yTotal;
var isPercentStack = SeriesHelper.IsPercentStack(m_Series, serie.stack, SerieType.Bar);
+
for (int j = 0; j < serie.data.Count; j++)
{
var serieData = serie.data[j];
@@ -1602,7 +1632,7 @@ namespace XCharts
dimension = VisualMapHelper.GetDimension(visualMap, serieData.data.Count);
}
- SerieLabelHelper.ResetLabel(serieData, serieLabel, theme, i);
+ SerieLabelHelper.ResetLabel(serieData.labelObject.label, serieLabel, theme, i);
value = serieData.data[dimension];
var content = "";
diff --git a/Runtime/Internal/CoordinateChart_DrawGantt.cs b/Runtime/Internal/CoordinateChart_DrawGantt.cs
new file mode 100644
index 00000000..0b885de9
--- /dev/null
+++ b/Runtime/Internal/CoordinateChart_DrawGantt.cs
@@ -0,0 +1,178 @@
+/************************************************/
+/* */
+/* Copyright (c) 2018 - 2021 monitor1394 */
+/* https://github.com/monitor1394 */
+/* */
+/************************************************/
+
+using UnityEngine;
+using UnityEngine.UI;
+using XUGL;
+
+namespace XCharts
+{
+ public partial class CoordinateChart
+ {
+ protected void DrawGanttSerie(VertexHelper vh, int colorIndex, Serie serie)
+ {
+ if (!IsActive(serie.index)) return;
+ if (serie.animation.HasFadeOut()) return;
+ var showData = serie.GetDataList(null);
+ var yAxis = m_YAxes[serie.yAxisIndex];
+ var xAxis = m_XAxes[serie.xAxisIndex];
+ var grid = GetSerieGridOrDefault(serie);
+ var xCategoryWidth = AxisHelper.GetDataWidth(xAxis, grid.runtimeWidth, showData.Count, dataZoom);
+ var yCategoryWidth = AxisHelper.GetDataWidth(yAxis, grid.runtimeHeight, showData.Count, dataZoom);
+ var barGap = GetBarGap();
+ var barWidth = serie.GetBarWidth(yCategoryWidth);
+ var space = (yCategoryWidth - barWidth) / 2;
+ var dataChanging = false;
+ var dataChangeDuration = serie.animation.GetUpdateAnimationDuration();
+ var minValue = xAxis.GetCurrMinValue(dataChangeDuration);
+ var maxValue = xAxis.GetCurrMaxValue(dataChangeDuration);
+ var pX = grid.runtimeX + (xAxis.boundaryGap ? xCategoryWidth / 2 : 0);
+ var pY = 0f;
+ var startY = grid.runtimeY - (yAxis.boundaryGap ? 0 : yCategoryWidth / 2);
+ var isTime = xAxis.type == Axis.AxisType.Time;
+
+ var categoryIndex = GetGanttSerieCategoryIndex(serie, grid.index);
+ var dataCount = serie.data.Count;
+ for (int i = 0; i < dataCount; i++)
+ {
+ var serieData = serie.data[i];
+ pY = startY + (categoryIndex - 1 - i) * yCategoryWidth;
+ DrawSerieData(vh, grid, serie, serieData, colorIndex, pX, pY, space, barWidth, isTime, minValue,
+ maxValue, xCategoryWidth);
+ }
+ if (dataChanging)
+ {
+ RefreshPainter(serie);
+ }
+ }
+
+ private void DrawSerieData(VertexHelper vh, Grid grid, Serie serie, SerieData serieData, int colorIndex,
+ float pX, float pY, float space, float barWidth, bool isTime, float minValue, float maxValue,
+ float xCategoryWidth)
+ {
+ var xStart = 0f;
+ var xEnd = 0f;
+ var xActualStart = 0f;
+ var xActualEnd = 0f;
+ var start = (int)serieData.GetData(0);
+ var end = (int)serieData.GetData(1);
+ var actualStart = (int)serieData.GetData(2);
+ var actualEnd = (int)serieData.GetData(3);
+ var enableActual = actualStart > 0 && actualEnd > 0;
+ if (isTime)
+ {
+ var valueTotal = maxValue - minValue;
+ xStart = pX + (start - minValue) / valueTotal * grid.runtimeWidth;
+ xEnd = pX + (end - minValue) / valueTotal * grid.runtimeWidth;
+ if (enableActual)
+ {
+ xActualStart = pX + (actualStart - minValue) / valueTotal * grid.runtimeWidth;
+ xActualEnd = pX + (actualEnd - minValue) / valueTotal * grid.runtimeWidth;
+ }
+ }
+ else
+ {
+ xStart = pX + start * xCategoryWidth;
+ xEnd = pX + end * xCategoryWidth;
+ if (enableActual)
+ {
+ xActualStart = pX + actualStart * xCategoryWidth;
+ xActualEnd = pX + actualEnd * xCategoryWidth;
+ }
+ }
+ var highlight = (serieData != null && serieData.highlighted)
+ || serie.highlighted;
+ var itemStyle = SerieHelper.GetItemStyle(serie, serieData, highlight);
+ var color = SerieHelper.GetItemColor(serie, serieData, m_Theme, colorIndex, highlight);
+ var borderWidth = itemStyle.borderWidth;
+
+ var rect = DrawGanttBar(vh, grid, serie, serieData, itemStyle, color, pY, pY, space, barWidth, xStart,
+ xEnd);
+ if (enableActual)
+ {
+ var defaultActualColor = SerieHelper.GetItemColor(serie, serieData, m_Theme, colorIndex, true);
+ var actualColor = SerieHelper.GetItemColor0(serie, serieData, m_Theme, highlight, defaultActualColor);
+ var rect2 = DrawGanttBar(vh, grid, serie, serieData, itemStyle, actualColor, pY, pY, space, barWidth,
+ xActualStart, xActualEnd);
+ var rect3X = Mathf.Min(rect.x, rect2.x);
+ var rect3Width = Mathf.Max(rect.x + rect.width, rect2.x + rect2.width) - rect3X;
+ var rect3 = new Rect(rect3X, rect.y, rect3Width, rect.height);
+ serie.dataPoints.Add(rect3.center);
+ serieData.runtimePosition = rect3.center;
+ serieData.labelPosition = rect3.center;
+ serieData.runtimeRect = rect3;
+ }
+ else
+ {
+ serie.dataPoints.Add(rect.center);
+ serieData.runtimePosition = rect.center;
+ serieData.labelPosition = rect.center;
+ serieData.runtimeRect = rect;
+ }
+ }
+
+ private Rect DrawGanttBar(VertexHelper vh, Grid grid, Serie serie, SerieData serieData, ItemStyle itemStyle,
+ Color32 color, float pX, float pY, float space, float barWidth, float xStart, float xEnd)
+ {
+
+ var borderWidth = itemStyle.borderWidth;
+ var plb = new Vector3(xStart + borderWidth, pY + space + borderWidth);
+ var plt = new Vector3(xStart + borderWidth, pY + space + barWidth - borderWidth);
+ var prt = new Vector3(xEnd - borderWidth, pY + space + barWidth - borderWidth);
+ var prb = new Vector3(xEnd - borderWidth, pY + space + borderWidth);
+ var center = new Vector3((plb.x + prt.x) / 2, (plt.y + prb.y) / 2);
+ var itemWidth = Mathf.Abs(prt.x - plb.x);
+ var itemHeight = Mathf.Abs(plt.y - prb.y);
+ if (serie.clip)
+ {
+ plb = ClampInGrid(grid, plb);
+ plt = ClampInGrid(grid, plt);
+ prt = ClampInGrid(grid, prt);
+ prb = ClampInGrid(grid, prb);
+ center = ClampInGrid(grid, center);
+ }
+ if (ItemStyleHelper.IsNeedCorner(itemStyle))
+ {
+ UGL.DrawRoundRectangle(vh, center, itemWidth, itemHeight, color, color, 0,
+ itemStyle.cornerRadius, true, 0.5f);
+ }
+ else
+ {
+ CheckClipAndDrawPolygon(vh, ref prb, ref plb, ref plt, ref prt, color, color,
+ serie.clip, grid);
+ }
+ if (borderWidth != 0)
+ {
+ UGL.DrawBorder(vh, center, itemWidth, itemHeight, borderWidth, itemStyle.borderColor, 0,
+ itemStyle.cornerRadius, true, 0.5f);
+ }
+ return new Rect(plb.x, plb.y, xEnd - xStart, barWidth);
+ }
+
+ private int GetGanttSerieCategoryIndex(Serie currSerie, int gridIndex)
+ {
+ var count = m_Series.Count;
+ var index = 0;
+ for (int i = 0; i < count; i++)
+ {
+ var serie = m_Series.GetSerie(i);
+ if (serie.type != SerieType.Gantt) continue;
+ var grid = GetSerieGridOrDefault(serie);
+ if (grid.index != gridIndex) continue;
+ foreach (var serieData in serie.data)
+ {
+ index++;
+ }
+ if (serie.index == currSerie.index)
+ {
+ return index;
+ }
+ }
+ return index;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Runtime/Internal/CoordinateChart_DrawGantt.cs.meta b/Runtime/Internal/CoordinateChart_DrawGantt.cs.meta
new file mode 100644
index 00000000..b779eed2
--- /dev/null
+++ b/Runtime/Internal/CoordinateChart_DrawGantt.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 81895b3c97e684e8090572c7e64b396e
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Internal/DrawSerieRadar.cs b/Runtime/Internal/DrawSerieRadar.cs
index f40d06c7..75b534ff 100644
--- a/Runtime/Internal/DrawSerieRadar.cs
+++ b/Runtime/Internal/DrawSerieRadar.cs
@@ -99,7 +99,7 @@ namespace XCharts
{
var value = serieData.GetCurrData(1);
var max = radar.GetIndicatorMax(n);
- SerieLabelHelper.ResetLabel(serieData, serieLabel, chart.theme, i);
+ SerieLabelHelper.ResetLabel(serieData.labelObject.label, serieLabel, chart.theme, i);
serieData.SetLabelActive(serieData.labelPosition != Vector3.zero);
serieData.labelObject.SetLabelPosition(serieLabel.offset);
var content = SerieLabelHelper.GetFormatterContent(serie, serieData, value, max, serieLabel);
diff --git a/Runtime/Internal/Helper/AxisHelper.cs b/Runtime/Internal/Helper/AxisHelper.cs
index 9544fc64..c7c258ba 100644
--- a/Runtime/Internal/Helper/AxisHelper.cs
+++ b/Runtime/Internal/Helper/AxisHelper.cs
@@ -53,6 +53,25 @@ namespace XCharts
return axis.splitNumber > 0 ? axis.splitNumber : 4;
}
}
+ else if (axis.type == Axis.AxisType.Time)
+ {
+ if (axis.interval > 0)
+ {
+ if (coordinateWid <= 0) return 0;
+ int num = Mathf.CeilToInt(axis.runtimeMinMaxRange / axis.interval);
+ int maxNum = Mathf.CeilToInt(coordinateWid / 15);
+ if (num > maxNum)
+ {
+ axis.interval *= 2;
+ num = Mathf.CeilToInt(axis.runtimeMinMaxRange / axis.interval);
+ }
+ return num;
+ }
+ else
+ {
+ return axis.splitNumber > 0 ? axis.splitNumber : 4;
+ }
+ }
else if (axis.type == Axis.AxisType.Log)
{
return axis.splitNumber > 0 ? axis.splitNumber : 4;
@@ -146,6 +165,23 @@ namespace XCharts
}
return axis.axisLabel.GetFormatterContent(value, minValue, maxValue, true);
}
+ else if (axis.type == Axis.AxisType.Time)
+ {
+ if (minValue == 0 && maxValue == 0) return string.Empty;
+ var value = 0f;
+ if (axis.interval > 0)
+ {
+ if (index == split) value = maxValue;
+ else value = minValue + index * axis.interval;
+ }
+ else
+ {
+ value = minValue + (maxValue - minValue) * index / split;
+ }
+ var timestamp = (int)value;
+ var dateTime = DateTimeUtil.GetDateTime(timestamp);
+ return axis.axisLabel.GetFormatterDateTime(dateTime);
+ }
var showData = axis.GetDataList(dataZoom);
int dataCount = showData.Count;
if (dataCount <= 0) return "";
@@ -177,11 +213,12 @@ namespace XCharts
int splitNum = GetSplitNumber(axis, coordinateWidth, dataZoom);
if (axis.IsCategory())
{
- int tick = Mathf.RoundToInt(axis.data.Count * 1f / splitNum);
+ var data = axis.GetDataList();
+ int tick = Mathf.RoundToInt(data.Count * 1f / splitNum);
if (axis.boundaryGap)
- return Mathf.CeilToInt(axis.data.Count * 1.0f / tick) + 1;
+ return Mathf.CeilToInt(data.Count * 1.0f / tick) + 1;
else
- return Mathf.CeilToInt(axis.data.Count * 1.0f / tick);
+ return Mathf.CeilToInt(data.Count * 1.0f / tick);
}
else
{
@@ -209,10 +246,11 @@ namespace XCharts
}
else
{
- if (axis.IsCategory() && axis.data.Count > 0)
+ var data = axis.GetDataList();
+ if (axis.IsCategory() && data.Count > 0)
{
- int tick = Mathf.RoundToInt(axis.data.Count * 1f / splitNum);
- var count = axis.boundaryGap ? axis.data.Count : axis.data.Count - 1;
+ int tick = Mathf.RoundToInt(data.Count * 1f / splitNum);
+ var count = axis.boundaryGap ? data.Count : data.Count - 1;
if (count <= 0) return 0;
var each = coordinateWidth / count;
if (index >= num - 1)
@@ -232,9 +270,10 @@ namespace XCharts
internal static float GetEachWidth(Axis axis, float coordinateWidth, DataZoom dataZoom = null)
{
- if (axis.data.Count > 0)
+ var data = axis.GetDataList();
+ if (data.Count > 0)
{
- var count = axis.boundaryGap ? axis.data.Count : axis.data.Count - 1;
+ var count = axis.boundaryGap ? data.Count : data.Count - 1;
return count > 0 ? coordinateWidth / count : coordinateWidth;
}
else
@@ -249,7 +288,7 @@ namespace XCharts
///
///
///
- internal static void AdjustMinMaxValue(Axis axis, ref float minValue, ref float maxValue, bool needFormat)
+ internal static void AdjustMinMaxValue(Axis axis, ref float minValue, ref float maxValue, bool needFormat, int ceilRate = 0)
{
if (axis.type == Axis.AxisType.Log)
{
@@ -278,6 +317,7 @@ namespace XCharts
}
else
{
+ if (ceilRate == 0) ceilRate = axis.ceilRate;
switch (axis.minMaxType)
{
case Axis.AxisMinMaxType.Default:
@@ -287,22 +327,22 @@ namespace XCharts
else if (minValue > 0 && maxValue > 0)
{
minValue = 0;
- maxValue = needFormat ? ChartHelper.GetMaxDivisibleValue(maxValue, axis.ceilRate) : maxValue;
+ maxValue = needFormat ? ChartHelper.GetMaxDivisibleValue(maxValue, ceilRate) : maxValue;
}
else if (minValue < 0 && maxValue < 0)
{
- minValue = needFormat ? ChartHelper.GetMinDivisibleValue(minValue, axis.ceilRate) : minValue;
+ minValue = needFormat ? ChartHelper.GetMinDivisibleValue(minValue, ceilRate) : minValue;
maxValue = 0;
}
else
{
- minValue = needFormat ? ChartHelper.GetMinDivisibleValue(minValue, axis.ceilRate) : minValue;
- maxValue = needFormat ? ChartHelper.GetMaxDivisibleValue(maxValue, axis.ceilRate) : maxValue;
+ minValue = needFormat ? ChartHelper.GetMinDivisibleValue(minValue, ceilRate) : minValue;
+ maxValue = needFormat ? ChartHelper.GetMaxDivisibleValue(maxValue, ceilRate) : maxValue;
}
break;
case Axis.AxisMinMaxType.MinMax:
- minValue = needFormat ? ChartHelper.GetMinDivisibleValue(minValue, axis.ceilRate) : minValue;
- maxValue = needFormat ? ChartHelper.GetMaxDivisibleValue(maxValue, axis.ceilRate) : maxValue;
+ minValue = needFormat ? ChartHelper.GetMinDivisibleValue(minValue, ceilRate) : minValue;
+ maxValue = needFormat ? ChartHelper.GetMaxDivisibleValue(maxValue, ceilRate) : maxValue;
break;
}
}
@@ -320,7 +360,7 @@ namespace XCharts
internal static bool NeedShowSplit(Axis axis)
{
if (!axis.show) return false;
- if (axis.IsCategory() && axis.data.Count <= 0) return false;
+ if (axis.IsCategory() && axis.GetDataList().Count <= 0) return false;
else if (axis.IsValue() && axis.runtimeMinValue == 0 && axis.runtimeMaxValue == 0) return false;
else return true;
}
diff --git a/Runtime/Internal/Helper/SerieHelper.cs b/Runtime/Internal/Helper/SerieHelper.cs
index 59eb1878..4433a1d2 100644
--- a/Runtime/Internal/Helper/SerieHelper.cs
+++ b/Runtime/Internal/Helper/SerieHelper.cs
@@ -70,6 +70,32 @@ namespace XCharts
return color;
}
}
+ internal static Color32 GetItemColor0(Serie serie, SerieData serieData, ChartTheme theme, bool highlight, Color32 defaultColor)
+ {
+ if (serie == null) return ChartConst.clearColor32;
+ if (highlight)
+ {
+ var itemStyleEmphasis = GetItemStyleEmphasis(serie, serieData);
+ if (itemStyleEmphasis != null && !ChartHelper.IsClearColor(itemStyleEmphasis.color))
+ {
+ var color = itemStyleEmphasis.color0;
+ ChartHelper.SetColorOpacity(ref color, itemStyleEmphasis.opacity);
+ return color;
+ }
+ }
+ var itemStyle = GetItemStyle(serie, serieData);
+ if (!ChartHelper.IsClearColor(itemStyle.color0))
+ {
+ return itemStyle.GetColor0();
+ }
+ else
+ {
+ var color = defaultColor;
+ if (highlight) color = ChartHelper.GetHighlightColor(color);
+ ChartHelper.SetColorOpacity(ref color, itemStyle.opacity);
+ return color;
+ }
+ }
internal static Color32 GetItemToColor(Serie serie, SerieData serieData, ChartTheme theme, int index, bool highlight)
{
diff --git a/Runtime/Internal/Helper/SerieLabelHelper.cs b/Runtime/Internal/Helper/SerieLabelHelper.cs
index ae11d392..9cc66317 100644
--- a/Runtime/Internal/Helper/SerieLabelHelper.cs
+++ b/Runtime/Internal/Helper/SerieLabelHelper.cs
@@ -75,14 +75,13 @@ namespace XCharts
}
}
- public static void ResetLabel(SerieData serieData, SerieLabel label, ChartTheme theme, int colorIndex)
+ public static void ResetLabel(ChartText labelObject, SerieLabel label, ChartTheme theme, int colorIndex)
{
- if (serieData.labelObject == null) return;
- if (serieData.labelObject.label == null) return;
- serieData.labelObject.label.SetColor(!ChartHelper.IsClearColor(label.textStyle.color) ? label.textStyle.color :
+ if (labelObject == null) return;
+ labelObject.SetColor(!ChartHelper.IsClearColor(label.textStyle.color) ? label.textStyle.color :
(Color)theme.GetColor(colorIndex));
- serieData.labelObject.label.SetFontSize(label.textStyle.GetFontSize(theme.common));
- serieData.labelObject.label.SetFontStyle(label.textStyle.fontStyle);
+ labelObject.SetFontSize(label.textStyle.GetFontSize(theme.common));
+ labelObject.SetFontStyle(label.textStyle.fontStyle);
}
public static bool CanShowLabel(Serie serie, SerieData serieData, SerieLabel label, int dimesion)
diff --git a/Runtime/Internal/Helper/TooltipHelper.cs b/Runtime/Internal/Helper/TooltipHelper.cs
index a1090d3c..1101bcb5 100644
--- a/Runtime/Internal/Helper/TooltipHelper.cs
+++ b/Runtime/Internal/Helper/TooltipHelper.cs
@@ -232,6 +232,14 @@ namespace XCharts
}
}
+ private static void InitGanttTooltip(ref StringBuilder sb, Tooltip tooltip, Serie serie, int index,
+ ChartTheme theme, string category)
+ {
+ //if (tooltip.runtimeGridIndex >= 0) return;
+ //if (serie.index != index || serie.type != SerieType.Gantt) return;
+ sb.Append(serie.name);
+ }
+
private static void InitDefaultContent(ref StringBuilder sb, Tooltip tooltip, Serie serie, int index,
string category, ChartTheme theme = null, DataZoom dataZoom = null, bool isCartesian = false,
Radar radar = null)
@@ -261,6 +269,9 @@ namespace XCharts
case SerieType.Gauge:
InitGaugeTooltip(ref sb, tooltip, serie, index, theme);
break;
+ case SerieType.Gantt:
+ InitGanttTooltip(ref sb, tooltip, serie, index, theme, category);
+ break;
}
}
diff --git a/Runtime/Template/SerieTemplate.cs b/Runtime/Template/SerieTemplate.cs
index ada7f05c..3e2aeff4 100644
--- a/Runtime/Template/SerieTemplate.cs
+++ b/Runtime/Template/SerieTemplate.cs
@@ -28,6 +28,7 @@ namespace XCharts
case SerieType.Gauge: AddDefaultGaugeSerie(chart, serieName); break;
case SerieType.Ring: AddDefaultRingSerie(chart, serieName); break;
case SerieType.Candlestick: AddDefaultCandlestickSerie(chart, serieName); break;
+ case SerieType.Gantt: AddDefaultCategoryGanttSerie(chart, serieName); break;
default: Debug.LogError("AddDefaultSerie: not support serieType yet:" + serieType); break;
}
}
@@ -183,5 +184,35 @@ namespace XCharts
}
return defaultDataCount;
}
+
+ public static Serie AddDefaultCategoryGanttSerie(BaseChart chart, string serieName, int dataCount = 0, int min = 0, int max = 0)
+ {
+ var serie = chart.AddSerie(SerieType.Gantt, serieName);
+ serie.showDataName = true;
+ serie.showDataDimension = 2;
+ for (int i = 0; i < dataCount; i++)
+ {
+ var start = Random.Range(min, max);
+ var end = Random.Range(start + 1, max);
+ serie.AddXYData(start, end, "task-" + (i + 1));
+ }
+ return serie;
+ }
+
+ public static Serie AddDefaultTimeGanttSerie(BaseChart chart, string serieName, int dataCount = 0)
+ {
+ var serie = chart.AddSerie(SerieType.Gantt, serieName);
+ serie.showDataName = true;
+ serie.showDataDimension = 2;
+ var timestamp = DateTimeUtil.GetTimestamp();
+ var now = DateTimeUtil.GetDateTime(timestamp);
+ for (int i = 0; i < dataCount; i++)
+ {
+ var start = timestamp + Random.Range(1, 6) * 3600 * 24;
+ var end = start + Random.Range(1, 10) * 3600 * 24;
+ serie.AddXYData(start, end, "task-" + (i + 1));
+ }
+ return serie;
+ }
}
}
\ No newline at end of file
diff --git a/Runtime/Utils/DateTimeUtil.cs b/Runtime/Utils/DateTimeUtil.cs
new file mode 100644
index 00000000..674297f9
--- /dev/null
+++ b/Runtime/Utils/DateTimeUtil.cs
@@ -0,0 +1,33 @@
+/************************************************/
+/* */
+/* Copyright (c) 2018 - 2021 monitor1394 */
+/* https://github.com/monitor1394 */
+/* */
+/************************************************/
+
+using System;
+using UnityEngine;
+
+namespace XCharts
+{
+ public static class DateTimeUtil
+ {
+ private static readonly DateTime k_DateTime1970 = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
+
+ public static int GetTimestamp()
+ {
+ return (int)(DateTime.Now - k_DateTime1970).TotalSeconds;
+ }
+
+ public static int GetTimestamp(DateTime time)
+ {
+ return (int)(time - k_DateTime1970).TotalSeconds;
+ }
+
+ public static DateTime GetDateTime(int timestamp)
+ {
+ long span = ((long)timestamp) * 10000000;
+ return k_DateTime1970.Add(new TimeSpan(span));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Runtime/Utils/DateTimeUtil.cs.meta b/Runtime/Utils/DateTimeUtil.cs.meta
new file mode 100644
index 00000000..ac2244b3
--- /dev/null
+++ b/Runtime/Utils/DateTimeUtil.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0f0ac80f189a04b5c826f40c8bc8af64
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: