Ei kuvausta
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

HeatmapHandler.cs 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. using UnityEngine.UI;
  4. using XUGL;
  5. namespace XCharts.Runtime
  6. {
  7. [UnityEngine.Scripting.Preserve]
  8. internal sealed partial class HeatmapHandler : SerieHandler<Heatmap>
  9. {
  10. private GridCoord m_SerieGrid;
  11. private Dictionary<int, int> m_CountDict = new Dictionary<int, int>();
  12. public override int defaultDimension { get { return 2; } }
  13. public static int GetGridKey(int x, int y)
  14. {
  15. return x * 100000 + y;
  16. }
  17. public static void GetGridXYByKey(int key, out int x, out int y)
  18. {
  19. x = key / 100000;
  20. y = key % 100000;
  21. }
  22. public override void Update()
  23. {
  24. base.Update();
  25. if (serie.IsUseCoord<GridCoord>())
  26. UpdateSerieContext();
  27. else if (serie.IsUseCoord<PolarCoord>())
  28. UpdateSeriePolarContext();
  29. }
  30. public override void DrawSerie(VertexHelper vh)
  31. {
  32. if (serie.heatmapType == HeatmapType.Count)
  33. DrawCountHeatmapSerie(vh, serie);
  34. else
  35. {
  36. if (serie.IsUseCoord<PolarCoord>())
  37. {
  38. DrawPolarHeatmap(vh, serie);
  39. }
  40. else if (serie.IsUseCoord<GridCoord>())
  41. {
  42. DrawDataHeatmapSerie(vh, serie);
  43. }
  44. }
  45. }
  46. public override void UpdateTooltipSerieParams(int dataIndex, bool showCategory, string category,
  47. string marker, string itemFormatter, string numericFormatter, string ignoreDataDefaultContent,
  48. ref List<SerieParams> paramList, ref string title)
  49. {
  50. dataIndex = serie.context.pointerItemDataIndex;
  51. if (serie.heatmapType == HeatmapType.Count)
  52. {
  53. int value;
  54. if (!m_CountDict.TryGetValue(dataIndex, out value)) return;
  55. var visualMap = chart.GetVisualMapOfSerie(serie);
  56. var dimension = VisualMapHelper.GetDimension(visualMap, defaultDimension);
  57. title = serie.serieName;
  58. var param = serie.context.param;
  59. param.serieName = serie.serieName;
  60. param.serieIndex = serie.index;
  61. param.dimension = dimension;
  62. param.dataCount = serie.dataCount;
  63. param.serieData = null;
  64. param.color = visualMap.GetColor(value);
  65. param.marker = SerieHelper.GetItemMarker(serie, null, marker);
  66. param.itemFormatter = SerieHelper.GetItemFormatter(serie, null, itemFormatter);
  67. param.numericFormatter = SerieHelper.GetNumericFormatter(serie, null, numericFormatter);
  68. param.columns.Clear();
  69. param.columns.Add(param.marker);
  70. param.columns.Add("count");
  71. param.columns.Add(ChartCached.NumberToStr(value, param.numericFormatter));
  72. paramList.Add(param);
  73. }
  74. else
  75. {
  76. if (dataIndex < 0)
  77. return;
  78. var serieData = serie.GetSerieData(dataIndex);
  79. if (serieData == null)
  80. return;
  81. var visualMap = chart.GetVisualMapOfSerie(serie);
  82. var dimension = VisualMapHelper.GetDimension(visualMap, defaultDimension);
  83. if (string.IsNullOrEmpty(category))
  84. {
  85. var xAxis = chart.GetChartComponent<XAxis>(serie.xAxisIndex);
  86. if (xAxis != null)
  87. category = xAxis.GetData((int) serieData.GetData(0));
  88. }
  89. title = serie.serieName;
  90. var param = serie.context.param;
  91. param.serieName = serie.serieName;
  92. param.serieIndex = serie.index;
  93. param.dimension = dimension;
  94. param.dataCount = serie.dataCount;
  95. param.serieData = serieData;
  96. param.color = serieData.context.color;
  97. param.marker = SerieHelper.GetItemMarker(serie, serieData, marker);
  98. param.itemFormatter = SerieHelper.GetItemFormatter(serie, serieData, itemFormatter);
  99. param.numericFormatter = SerieHelper.GetNumericFormatter(serie, serieData, numericFormatter);
  100. param.columns.Clear();
  101. param.columns.Add(param.marker);
  102. param.columns.Add(category);
  103. param.columns.Add(ChartCached.NumberToStr(serieData.GetData(dimension), param.numericFormatter));
  104. paramList.Add(param);
  105. }
  106. }
  107. private void UpdateSerieContext()
  108. {
  109. if (m_SerieGrid == null)
  110. return;
  111. var needCheck = (chart.isPointerInChart && m_SerieGrid.IsPointerEnter()) || m_LegendEnter;
  112. var needInteract = false;
  113. if (!needCheck)
  114. {
  115. if (m_LastCheckContextFlag != needCheck)
  116. {
  117. m_LastCheckContextFlag = needCheck;
  118. serie.context.pointerItemDataIndex = -1;
  119. serie.context.pointerEnter = false;
  120. foreach (var serieData in serie.data)
  121. {
  122. serieData.context.highlight = false;
  123. }
  124. chart.RefreshPainter(serie);
  125. }
  126. return;
  127. }
  128. if (serie.heatmapType == HeatmapType.Count)
  129. return;
  130. m_LastCheckContextFlag = needCheck;
  131. if (m_LegendEnter)
  132. {
  133. serie.context.pointerEnter = true;
  134. foreach (var serieData in serie.data)
  135. {
  136. serieData.context.highlight = true;
  137. }
  138. }
  139. else
  140. {
  141. serie.context.pointerItemDataIndex = -1;
  142. serie.context.pointerEnter = false;
  143. foreach (var serieData in serie.data)
  144. {
  145. if (!needInteract && serieData.context.rect.Contains(chart.pointerPos))
  146. {
  147. serie.context.pointerItemDataIndex = serieData.index;
  148. serie.context.pointerEnter = true;
  149. serieData.context.highlight = true;
  150. needInteract = true;
  151. }
  152. else
  153. {
  154. serieData.context.highlight = false;
  155. }
  156. }
  157. }
  158. if (needInteract)
  159. {
  160. chart.RefreshPainter(serie);
  161. }
  162. }
  163. private void DrawDataHeatmapSerie(VertexHelper vh, Heatmap serie)
  164. {
  165. if (!serie.show || serie.animation.HasFadeOut()) return;
  166. XAxis xAxis;
  167. YAxis yAxis;
  168. if (!chart.TryGetChartComponent<XAxis>(out xAxis, serie.xAxisIndex)) return;
  169. if (!chart.TryGetChartComponent<YAxis>(out yAxis, serie.yAxisIndex)) return;
  170. m_SerieGrid = chart.GetChartComponent<GridCoord>(xAxis.gridIndex);
  171. xAxis.boundaryGap = true;
  172. yAxis.boundaryGap = true;
  173. var visualMap = chart.GetVisualMapOfSerie(serie);
  174. var emphasisStyle = serie.emphasisStyle;
  175. var xCount = AxisHelper.GetTotalSplitGridNum(xAxis);
  176. var yCount = AxisHelper.GetTotalSplitGridNum(yAxis);
  177. var xWidth = m_SerieGrid.context.width / xCount;
  178. var yWidth = m_SerieGrid.context.height / yCount;
  179. var zeroX = m_SerieGrid.context.x;
  180. var zeroY = m_SerieGrid.context.y;
  181. var borderWidth = serie.itemStyle.show ? serie.itemStyle.borderWidth : 0;
  182. var splitWid = xWidth - 2 * borderWidth;
  183. var splitHig = yWidth - 2 * borderWidth;
  184. var defaultSymbolSize = Mathf.Min(splitWid, splitHig) * 0.25f;
  185. serie.animation.InitProgress(0, xCount);
  186. var animationIndex = serie.animation.GetCurrIndex();
  187. var dataChangeDuration = serie.animation.GetUpdateAnimationDuration();
  188. var unscaledTime = serie.animation.unscaledTime;
  189. var dataChanging = false;
  190. serie.containerIndex = m_SerieGrid.index;
  191. serie.containterInstanceId = m_SerieGrid.instanceId;
  192. var dimension = VisualMapHelper.GetDimension(visualMap, defaultDimension);
  193. if (visualMap.autoMinMax)
  194. {
  195. double maxValue, minValue;
  196. SerieHelper.GetMinMaxData(serie, dimension, out minValue, out maxValue);
  197. VisualMapHelper.SetMinMax(visualMap, minValue, maxValue);
  198. }
  199. var rangeMin = visualMap.rangeMin;
  200. var rangeMax = visualMap.rangeMax;
  201. var color = chart.theme.GetColor(serie.index);
  202. float symbolBorder = 0f;
  203. float[] cornerRadius = null;
  204. Color32 borderColor;
  205. for (int n = 0; n < serie.dataCount; n++)
  206. {
  207. var serieData = serie.data[n];
  208. var xValue = serieData.GetData(0);
  209. var yValue = serieData.GetData(1);
  210. var i = AxisHelper.GetAxisValueSplitIndex(xAxis, xValue, xCount);
  211. var j = AxisHelper.GetAxisValueSplitIndex(yAxis, yValue, yCount);
  212. if (serie.IsIgnoreValue(serieData, dimension))
  213. {
  214. serie.context.dataPoints.Add(Vector3.zero);
  215. serie.context.dataIndexs.Add(serieData.index);
  216. continue;
  217. }
  218. var state = SerieHelper.GetSerieState(serie, serieData, true);
  219. var symbol = SerieHelper.GetSerieSymbol(serie, serieData, state);
  220. var isRectSymbol = symbol.type == SymbolType.Rect;
  221. SerieHelper.GetSymbolInfo(out borderColor, out symbolBorder, out cornerRadius, serie, serieData, chart.theme, state);
  222. var value = serieData.GetCurrData(dimension, dataChangeDuration, yAxis.inverse,
  223. yAxis.context.minValue, yAxis.context.maxValue, unscaledTime);
  224. if (serieData.IsDataChanged()) dataChanging = true;
  225. var pos = new Vector3(zeroX + (i + 0.5f) * xWidth,
  226. zeroY + (j + 0.5f) * yWidth);
  227. serie.context.dataPoints.Add(pos);
  228. serie.context.dataIndexs.Add(serieData.index);
  229. serieData.context.position = pos;
  230. serieData.context.canShowLabel = false;
  231. if ((value < rangeMin && rangeMin != visualMap.min) ||
  232. (value > rangeMax && rangeMax != visualMap.max))
  233. {
  234. continue;
  235. }
  236. if (!visualMap.IsInSelectedValue(value)) continue;
  237. if (animationIndex >= 0 && i > animationIndex) continue;
  238. color = visualMap.GetColor(value);
  239. if (serieData.context.highlight)
  240. color = ChartHelper.GetHighlightColor(color);
  241. serieData.context.canShowLabel = true;
  242. serieData.context.color = color;
  243. var highlight = (serieData.context.highlight) ||
  244. visualMap.context.pointerIndex > 0;
  245. var rectWid = 0f;
  246. var rectHig = 0f;
  247. if (isRectSymbol)
  248. {
  249. if (symbol.size == 0 && symbol.sizeType == SymbolSizeType.Custom)
  250. {
  251. rectWid = splitWid;
  252. rectHig = splitHig;
  253. }
  254. else
  255. {
  256. var symbolSize = SerieHelper.GetSysmbolSize(serie, serieData, chart.theme, defaultSymbolSize, state);
  257. rectWid = symbolSize;
  258. rectHig = symbolSize;
  259. }
  260. serieData.context.rect = new Rect(pos.x - rectWid / 2, pos.y - rectHig / 2, rectWid, rectHig);
  261. UGL.DrawRectangle(vh, serieData.context.rect, color);
  262. if (borderWidth > 0 && !ChartHelper.IsClearColor(borderColor))
  263. {
  264. UGL.DrawBorder(vh, pos, rectWid, rectHig, borderWidth, borderColor, borderColor);
  265. }
  266. }
  267. else
  268. {
  269. var symbolSize = SerieHelper.GetSysmbolSize(serie, serieData, chart.theme, defaultSymbolSize, state);
  270. var emptyColor = SerieHelper.GetItemBackgroundColor(serie, serieData, chart.theme, serie.context.colorIndex, state);
  271. serieData.context.rect = new Rect(pos.x - symbolSize / 2, pos.y - symbolSize / 2, symbolSize, symbolSize);
  272. chart.DrawSymbol(vh, symbol.type, symbolSize, symbolBorder, pos,
  273. color, color, emptyColor, borderColor, symbol.gap, cornerRadius);
  274. }
  275. if (visualMap.hoverLink && highlight && emphasisStyle != null &&
  276. emphasisStyle.itemStyle.borderWidth > 0)
  277. {
  278. var emphasisItemStyle = emphasisStyle.itemStyle;
  279. var emphasisBorderWidth = emphasisItemStyle.borderWidth;
  280. var emphasisBorderColor = emphasisItemStyle.opacity > 0 ?
  281. emphasisItemStyle.borderColor : ChartConst.clearColor32;
  282. var emphasisBorderToColor = emphasisItemStyle.opacity > 0 ?
  283. emphasisItemStyle.borderToColor : ChartConst.clearColor32;
  284. UGL.DrawBorder(vh, pos, rectWid, rectHig, emphasisBorderWidth, emphasisBorderColor,
  285. emphasisBorderToColor);
  286. }
  287. }
  288. if (!serie.animation.IsFinish())
  289. {
  290. serie.animation.CheckProgress(xCount);
  291. chart.RefreshPainter(serie);
  292. }
  293. if (dataChanging)
  294. {
  295. chart.RefreshPainter(serie);
  296. }
  297. }
  298. private void DrawCountHeatmapSerie(VertexHelper vh, Heatmap serie)
  299. {
  300. if (!serie.show || serie.animation.HasFadeOut()) return;
  301. XAxis xAxis;
  302. YAxis yAxis;
  303. if (!chart.TryGetChartComponent<XAxis>(out xAxis, serie.xAxisIndex)) return;
  304. if (!chart.TryGetChartComponent<YAxis>(out yAxis, serie.yAxisIndex)) return;
  305. m_SerieGrid = chart.GetChartComponent<GridCoord>(xAxis.gridIndex);
  306. xAxis.boundaryGap = true;
  307. yAxis.boundaryGap = true;
  308. var visualMap = chart.GetVisualMapOfSerie(serie);
  309. var emphasisStyle = serie.emphasisStyle;
  310. var xCount = AxisHelper.GetTotalSplitGridNum(xAxis);
  311. var yCount = AxisHelper.GetTotalSplitGridNum(yAxis);
  312. var xWidth = m_SerieGrid.context.width / xCount;
  313. var yWidth = m_SerieGrid.context.height / yCount;
  314. var zeroX = m_SerieGrid.context.x;
  315. var zeroY = m_SerieGrid.context.y;
  316. var borderWidth = serie.itemStyle.show ? serie.itemStyle.borderWidth : 0;
  317. var splitWid = xWidth - 2 * borderWidth;
  318. var splitHig = yWidth - 2 * borderWidth;
  319. var defaultSymbolSize = Mathf.Min(splitWid, splitHig) * 0.25f;
  320. serie.animation.InitProgress(0, xCount);
  321. var animationIndex = serie.animation.GetCurrIndex();
  322. var dataChanging = false;
  323. serie.containerIndex = m_SerieGrid.index;
  324. serie.containterInstanceId = m_SerieGrid.instanceId;
  325. m_CountDict.Clear();
  326. double minCount = 0, maxCount = 0;
  327. foreach (var serieData in serie.data)
  328. {
  329. var xValue = serieData.GetData(0);
  330. var yValue = serieData.GetData(1);
  331. var i = AxisHelper.GetAxisValueSplitIndex(xAxis, xValue, xCount);
  332. var j = AxisHelper.GetAxisValueSplitIndex(yAxis, yValue, yCount);
  333. var key = GetGridKey(i, j);
  334. var count = 0;
  335. if (!m_CountDict.TryGetValue(key, out count))
  336. count = 1;
  337. else
  338. count++;
  339. if (count > maxCount)
  340. maxCount = count;
  341. m_CountDict[key] = count;
  342. }
  343. if (visualMap.autoMinMax)
  344. {
  345. VisualMapHelper.SetMinMax(visualMap, minCount, maxCount);
  346. }
  347. var rangeMin = visualMap.rangeMin;
  348. var rangeMax = visualMap.rangeMax;
  349. int highlightX = -1;
  350. int highlightY = -1;
  351. if (serie.context.pointerItemDataIndex > 0)
  352. {
  353. if (m_CountDict.ContainsKey(serie.context.pointerItemDataIndex))
  354. {
  355. GetGridXYByKey(serie.context.pointerItemDataIndex, out highlightX, out highlightY);
  356. }
  357. }
  358. var state = SerieHelper.GetSerieState(serie, null, true);
  359. var symbol = SerieHelper.GetSerieSymbol(serie, null, state);
  360. var symbolSize = SerieHelper.GetSysmbolSize(serie, null, chart.theme, defaultSymbolSize, state);
  361. var isRectSymbol = symbol.type == SymbolType.Rect;
  362. float symbolBorder = 0f;
  363. float[] cornerRadius = null;
  364. Color32 color, toColor, emptyColor, borderColor;
  365. SerieHelper.GetItemColor(out color, out toColor, out emptyColor, serie, null, chart.theme, serie.context.colorIndex, state);
  366. SerieHelper.GetSymbolInfo(out borderColor, out symbolBorder, out cornerRadius, serie, null, chart.theme, state);
  367. foreach (var kv in m_CountDict)
  368. {
  369. int i, j;
  370. GetGridXYByKey(kv.Key, out i, out j);
  371. var value = kv.Value;
  372. if (serie.IsIgnoreValue(value))
  373. {
  374. continue;
  375. }
  376. if ((value < rangeMin && rangeMin != visualMap.min) ||
  377. (value > rangeMax && rangeMax != visualMap.max))
  378. {
  379. continue;
  380. }
  381. if (!visualMap.IsInSelectedValue(value))
  382. continue;
  383. if (animationIndex >= 0 && i > animationIndex)
  384. continue;
  385. var highlight = i == highlightX && j == highlightY;
  386. color = visualMap.GetColor(value);
  387. if (highlight)
  388. color = ChartHelper.GetHighlightColor(color);
  389. var pos = new Vector3(zeroX + (i + 0.5f) * xWidth,
  390. zeroY + (j + 0.5f) * yWidth);
  391. var rectWid = 0f;
  392. var rectHig = 0f;
  393. if (isRectSymbol)
  394. {
  395. if (symbol.size == 0 && symbol.sizeType == SymbolSizeType.Custom)
  396. {
  397. rectWid = splitWid;
  398. rectHig = splitHig;
  399. }
  400. else
  401. {
  402. rectWid = symbolSize;
  403. rectHig = symbolSize;
  404. }
  405. var rect = new Rect(pos.x - rectWid / 2, pos.y - rectHig / 2, rectWid, rectHig);
  406. UGL.DrawRectangle(vh, rect, color);
  407. if (borderWidth > 0 && !ChartHelper.IsClearColor(borderColor))
  408. {
  409. UGL.DrawBorder(vh, pos, rectWid, rectHig, borderWidth, borderColor, borderColor);
  410. }
  411. }
  412. else
  413. {
  414. chart.DrawSymbol(vh, symbol.type, symbolSize, symbolBorder, pos,
  415. color, color, emptyColor, borderColor, symbol.gap, cornerRadius);
  416. }
  417. if (visualMap.hoverLink && highlight && emphasisStyle != null &&
  418. emphasisStyle.itemStyle.borderWidth > 0)
  419. {
  420. var emphasisItemStyle = emphasisStyle.itemStyle;
  421. var emphasisBorderWidth = emphasisItemStyle.borderWidth;
  422. var emphasisBorderColor = emphasisItemStyle.opacity > 0 ?
  423. emphasisItemStyle.borderColor : ChartConst.clearColor32;
  424. var emphasisBorderToColor = emphasisItemStyle.opacity > 0 ?
  425. emphasisItemStyle.borderToColor : ChartConst.clearColor32;
  426. UGL.DrawBorder(vh, pos, rectWid, rectHig, emphasisBorderWidth, emphasisBorderColor,
  427. emphasisBorderToColor);
  428. }
  429. }
  430. if (!serie.animation.IsFinish())
  431. {
  432. serie.animation.CheckProgress(xCount);
  433. chart.RefreshPainter(serie);
  434. }
  435. if (dataChanging)
  436. {
  437. chart.RefreshPainter(serie);
  438. }
  439. }
  440. }
  441. }