using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; using XUGL; namespace XCharts.Runtime { [UnityEngine.Scripting.Preserve] internal sealed class DataZoomHandler : MainComponentHandler { private static readonly string s_DefaultDataZoom = "datazoom"; private Vector2 m_LastTouchPos0; private Vector2 m_LastTouchPos1; private bool m_CheckDataZoomLabel; private float m_DataZoomLastStartIndex; private float m_DataZoomLastEndIndex; public override void InitComponent() { var dataZoom = component; dataZoom.painter = chart.m_PainterTop; dataZoom.refreshComponent = delegate() { var dataZoomObject = ChartHelper.AddObject(s_DefaultDataZoom + dataZoom.index, chart.transform, chart.chartMinAnchor, chart.chartMaxAnchor, chart.chartPivot, chart.chartSizeDelta); dataZoom.gameObject = dataZoomObject; dataZoomObject.hideFlags = chart.chartHideFlags; ChartHelper.HideAllObject(dataZoomObject); var startLabel = ChartHelper.AddChartLabel(s_DefaultDataZoom + "start", dataZoomObject.transform, dataZoom.labelStyle, chart.theme.dataZoom, "", Color.clear, TextAnchor.MiddleRight); startLabel.gameObject.SetActive(true); var endLabel = ChartHelper.AddChartLabel(s_DefaultDataZoom + "end", dataZoomObject.transform, dataZoom.labelStyle, chart.theme.dataZoom, "", Color.clear, TextAnchor.MiddleLeft); endLabel.gameObject.SetActive(true); dataZoom.SetStartLabel(startLabel); dataZoom.SetEndLabel(endLabel); dataZoom.SetLabelActive(false); foreach (var index in dataZoom.xAxisIndexs) { var xAxis = chart.GetChartComponent(index); if (xAxis != null) { xAxis.UpdateFilterData(dataZoom); } } foreach (var serie in chart.series) { SerieHelper.UpdateFilterData(serie, dataZoom); } }; dataZoom.refreshComponent(); } public override void Update() { CheckDataZoomScale(component); CheckDataZoomLabel(component); } public override void DrawTop(VertexHelper vh) { if (chart == null) return; var dataZoom = component; switch (dataZoom.orient) { case Orient.Horizonal: DrawHorizonalDataZoomSlider(vh, dataZoom); break; case Orient.Vertical: DrawVerticalDataZoomSlider(vh, dataZoom); break; } } public override void OnBeginDrag(PointerEventData eventData) { if (chart == null) return; if (Input.touchCount > 1) return; Vector2 pos; if (!chart.ScreenPointToChartPoint(eventData.position, out pos)) return; var dataZoom = component; if (!dataZoom.enable) return; var grid = chart.GetGridOfDataZoom(dataZoom); if (dataZoom.supportInside && dataZoom.supportInsideDrag) { if (grid.Contains(pos)) { dataZoom.context.isCoordinateDrag = true; } } if (dataZoom.supportSlider) { if (!dataZoom.zoomLock) { if (dataZoom.IsInStartZoom(pos)) { dataZoom.context.isStartDrag = true; } else if (dataZoom.IsInEndZoom(pos)) { dataZoom.context.isEndDrag = true; } else if (dataZoom.IsInSelectedZoom(pos)) { dataZoom.context.isDrag = true; } } else if (dataZoom.IsInSelectedZoom(pos)) { dataZoom.context.isDrag = true; } } } public override void OnDrag(PointerEventData eventData) { if (chart == null) return; if (Input.touchCount > 1) return; var dataZoom = component; var grid = chart.GetGridOfDataZoom(dataZoom); switch (dataZoom.orient) { case Orient.Horizonal: var deltaPercent = eventData.delta.x / grid.context.width * 100; OnDragInside(dataZoom, deltaPercent); OnDragSlider(dataZoom, deltaPercent); break; case Orient.Vertical: deltaPercent = eventData.delta.y / grid.context.height * 100; OnDragInside(dataZoom, deltaPercent); OnDragSlider(dataZoom, deltaPercent); break; } } public override void OnEndDrag(PointerEventData eventData) { if (chart == null) return; var dataZoom = component; if (dataZoom.context.isDrag || dataZoom.context.isStartDrag || dataZoom.context.isEndDrag || dataZoom.context.isCoordinateDrag) { chart.RefreshChart(); } dataZoom.context.isDrag = false; dataZoom.context.isCoordinateDrag = false; dataZoom.context.isStartDrag = false; dataZoom.context.isEndDrag = false; } public override void OnPointerDown(PointerEventData eventData) { if (chart == null) return; if (Input.touchCount > 1) return; Vector2 localPos; if (!chart.ScreenPointToChartPoint(eventData.position, out localPos)) return; var dataZoom = component; var grid = chart.GetGridOfDataZoom(dataZoom); if (dataZoom.IsInStartZoom(localPos) || dataZoom.IsInEndZoom(localPos)) { return; } if (dataZoom.IsInZoom(localPos) && !dataZoom.IsInSelectedZoom(localPos)) { var pointerX = localPos.x; var selectWidth = grid.context.width * (dataZoom.end - dataZoom.start) / 100; var startX = pointerX - selectWidth / 2; var endX = pointerX + selectWidth / 2; if (startX < grid.context.x) { startX = grid.context.x; endX = grid.context.x + selectWidth; } 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); } } public override void OnScroll(PointerEventData eventData) { if (chart == null) return; if (Input.touchCount > 1) return; Vector2 pos; if (!chart.ScreenPointToChartPoint(eventData.position, out pos)) return; var dataZoom = component; if (!dataZoom.enable || dataZoom.zoomLock) return; var grid = chart.GetGridOfDataZoom(dataZoom); if ((dataZoom.supportInside && dataZoom.supportInsideScroll && grid.Contains(pos)) || dataZoom.IsInZoom(pos)) { ScaleDataZoom(dataZoom, eventData.scrollDelta.y * dataZoom.scrollSensitivity); } } private void OnDragInside(DataZoom dataZoom, float deltaPercent) { if (deltaPercent == 0) return; if (Input.touchCount > 1) return; if (!dataZoom.supportInside || !dataZoom.supportInsideDrag) return; if (!dataZoom.context.isCoordinateDrag) return; var diff = dataZoom.end - dataZoom.start; if (deltaPercent > 0) { if (dataZoom.start > 0) { var start = dataZoom.start - deltaPercent; if (start < 0) start = 0; var end = start + diff; UpdateDataZoomRange(dataZoom, start, end); } } else { if (dataZoom.end < 100) { var end = dataZoom.end - deltaPercent; if (end > 100) end = 100; var start = end - diff; UpdateDataZoomRange(dataZoom, start, end); } } } private void OnDragSlider(DataZoom dataZoom, float deltaPercent) { if (Input.touchCount > 1) return; if (!dataZoom.supportSlider) return; if (dataZoom.context.isStartDrag) { var start = dataZoom.start + deltaPercent; if (start > dataZoom.end) { start = dataZoom.end; dataZoom.context.isEndDrag = true; dataZoom.context.isStartDrag = false; } UpdateDataZoomRange(dataZoom, start, dataZoom.end); } else if (dataZoom.context.isEndDrag) { var end = dataZoom.end + deltaPercent; if (end < dataZoom.start) { end = dataZoom.start; dataZoom.context.isStartDrag = true; dataZoom.context.isEndDrag = false; } UpdateDataZoomRange(dataZoom, dataZoom.start, end); } else if (dataZoom.context.isDrag) { if (deltaPercent > 0) { if (dataZoom.end + deltaPercent > 100) deltaPercent = 100 - dataZoom.end; } else { if (dataZoom.start + deltaPercent < 0) deltaPercent = -dataZoom.start; } UpdateDataZoomRange(dataZoom, dataZoom.start + deltaPercent, dataZoom.end + deltaPercent); } } private void ScaleDataZoom(DataZoom dataZoom, float delta) { var grid = chart.GetGridOfDataZoom(dataZoom); var deltaPercent = dataZoom.orient == Orient.Horizonal ? Mathf.Abs(delta / grid.context.width * 100) : Mathf.Abs(delta / grid.context.height * 100); if (delta > 0) { if (dataZoom.end <= dataZoom.start) return; UpdateDataZoomRange(dataZoom, dataZoom.start + deltaPercent, dataZoom.end - deltaPercent); } else { UpdateDataZoomRange(dataZoom, dataZoom.start - deltaPercent, dataZoom.end + deltaPercent); } } public void UpdateDataZoomRange(DataZoom dataZoom, float start, float end) { if (end > 100) end = 100; if (start < 0) start = 0; if (end < start) end = start; dataZoom.start = start; dataZoom.end = end; if (dataZoom.realtime) { chart.OnDataZoomRangeChanged(dataZoom); chart.RefreshChart(); } } public void RefreshDataZoomLabel() { m_CheckDataZoomLabel = true; } private void CheckDataZoomScale(DataZoom dataZoom) { if (!dataZoom.enable || dataZoom.zoomLock || !dataZoom.supportInside || !dataZoom.supportInsideDrag) return; if (Input.touchCount == 2) { var touch0 = Input.GetTouch(0); var touch1 = Input.GetTouch(1); if (touch1.phase == TouchPhase.Began) { m_LastTouchPos0 = touch0.position; m_LastTouchPos1 = touch1.position; } else if (touch0.phase == TouchPhase.Moved || touch1.phase == TouchPhase.Moved) { var tempPos0 = touch0.position; var tempPos1 = touch1.position; var currDist = Vector2.Distance(tempPos0, tempPos1); var lastDist = Vector2.Distance(m_LastTouchPos0, m_LastTouchPos1); var delta = (currDist - lastDist); ScaleDataZoom(dataZoom, delta / dataZoom.scrollSensitivity); m_LastTouchPos0 = tempPos0; m_LastTouchPos1 = tempPos1; } } } private void CheckDataZoomLabel(DataZoom dataZoom) { if (dataZoom.enable && dataZoom.supportSlider && dataZoom.showDetail) { Vector2 local; if (!chart.ScreenPointToChartPoint(Input.mousePosition, out local)) { dataZoom.SetLabelActive(false); return; } if (dataZoom.IsInSelectedZoom(local) || dataZoom.IsInStartZoom(local) || dataZoom.IsInEndZoom(local)) { dataZoom.SetLabelActive(true); RefreshDataZoomLabel(); } else { dataZoom.SetLabelActive(false); } } if (m_CheckDataZoomLabel && dataZoom.xAxisIndexs.Count > 0) { m_CheckDataZoomLabel = false; var xAxis = chart.GetChartComponent(dataZoom.xAxisIndexs[0]); var startIndex = (int) ((xAxis.data.Count - 1) * dataZoom.start / 100); var endIndex = (int) ((xAxis.data.Count - 1) * dataZoom.end / 100); if (m_DataZoomLastStartIndex != startIndex || m_DataZoomLastEndIndex != endIndex) { m_DataZoomLastStartIndex = startIndex; m_DataZoomLastEndIndex = endIndex; if (xAxis.data.Count > 0) { dataZoom.SetStartLabelText(xAxis.data[startIndex]); dataZoom.SetEndLabelText(xAxis.data[endIndex]); } else if (xAxis.IsTime()) { //TODO: dataZoom.SetStartLabelText(""); dataZoom.SetEndLabelText(""); } xAxis.SetAllDirty(); } var start = dataZoom.context.x + dataZoom.context.width * dataZoom.start / 100; var end = dataZoom.context.x + dataZoom.context.width * dataZoom.end / 100; var hig = dataZoom.context.height; dataZoom.UpdateStartLabelPosition(new Vector3(start - 10, chart.chartY + dataZoom.bottom + hig / 2)); dataZoom.UpdateEndLabelPosition(new Vector3(end + 10, chart.chartY + dataZoom.bottom + hig / 2)); } } private void DrawHorizonalDataZoomSlider(VertexHelper vh, DataZoom dataZoom) { if (!dataZoom.enable || !dataZoom.supportSlider) return; var p1 = new Vector3(dataZoom.context.x, dataZoom.context.y); var p2 = new Vector3(dataZoom.context.x, dataZoom.context.y + dataZoom.context.height); var p3 = new Vector3(dataZoom.context.x + dataZoom.context.width, dataZoom.context.y + dataZoom.context.height); var p4 = new Vector3(dataZoom.context.x + dataZoom.context.width, dataZoom.context.y); var lineColor = dataZoom.lineStyle.GetColor(chart.theme.dataZoom.dataLineColor); var lineWidth = dataZoom.lineStyle.GetWidth(chart.theme.dataZoom.dataLineWidth); var borderWidth = dataZoom.borderWidth == 0 ? chart.theme.dataZoom.borderWidth : dataZoom.borderWidth; var borderColor = dataZoom.GetBorderColor(chart.theme.dataZoom.borderColor); var backgroundColor = dataZoom.GetBackgroundColor(chart.theme.dataZoom.backgroundColor); var areaColor = dataZoom.areaStyle.GetColor(chart.theme.dataZoom.dataAreaColor); UGL.DrawQuadrilateral(vh, p1, p2, p3, p4, backgroundColor); var centerPos = new Vector3(dataZoom.context.x + dataZoom.context.width / 2, dataZoom.context.y + dataZoom.context.height / 2); UGL.DrawBorder(vh, centerPos, dataZoom.context.width, dataZoom.context.height, borderWidth, borderColor); if (dataZoom.showDataShadow && chart.series.Count > 0) { Serie serie = chart.series[0]; Axis axis = chart.GetChartComponent(0); var showData = serie.GetDataList(null); float scaleWid = dataZoom.context.width / (showData.Count - 1); Vector3 lp = Vector3.zero; Vector3 np = Vector3.zero; double minValue = 0; double maxValue = 0; SeriesHelper.GetYMinMaxValue(chart.series, null, 0, chart.IsAllAxisValue(), axis.inverse, out minValue, out maxValue); AxisHelper.AdjustMinMaxValue(axis, ref minValue, ref maxValue, true); int rate = 1; var sampleDist = serie.sampleDist < 2 ? 2 : serie.sampleDist; var maxCount = showData.Count; if (sampleDist > 0) rate = (int) ((maxCount - serie.minShow) / (dataZoom.context.width / sampleDist)); if (rate < 1) rate = 1; var totalAverage = serie.sampleAverage > 0 ? serie.sampleAverage : DataHelper.DataAverage(ref showData, serie.sampleType, serie.minShow, maxCount, rate); var dataChanging = false; for (int i = 0; i < maxCount; i += rate) { double value = DataHelper.SampleValue(ref showData, serie.sampleType, rate, serie.minShow, maxCount, totalAverage, i, serie.animation.GetUpdateAnimationDuration(), ref dataChanging, axis); float pX = dataZoom.context.x + i * scaleWid; float dataHig = (float) ((maxValue - minValue) == 0 ? 0 : (value - minValue) / (maxValue - minValue) * dataZoom.context.height); np = new Vector3(pX, chart.chartY + dataZoom.bottom + dataHig); if (i > 0) { UGL.DrawLine(vh, lp, np, lineWidth, lineColor); Vector3 alp = new Vector3(lp.x, lp.y - lineWidth); Vector3 anp = new Vector3(np.x, np.y - lineWidth); Vector3 tnp = new Vector3(np.x, chart.chartY + dataZoom.bottom + lineWidth); Vector3 tlp = new Vector3(lp.x, chart.chartY + dataZoom.bottom + lineWidth); UGL.DrawQuadrilateral(vh, alp, anp, tnp, tlp, areaColor); } lp = np; } if (dataChanging) { chart.RefreshTopPainter(); } } switch (dataZoom.rangeMode) { case DataZoom.RangeMode.Percent: var start = dataZoom.context.x + dataZoom.context.width * dataZoom.start / 100; var end = dataZoom.context.x + dataZoom.context.width * dataZoom.end / 100; var fillerColor = dataZoom.GetFillerColor(chart.theme.dataZoom.fillerColor); p1 = new Vector2(start, dataZoom.context.y); p2 = new Vector2(start, dataZoom.context.y + dataZoom.context.height); p3 = new Vector2(end, dataZoom.context.y + dataZoom.context.height); p4 = new Vector2(end, dataZoom.context.y); UGL.DrawQuadrilateral(vh, p1, p2, p3, p4, fillerColor); UGL.DrawLine(vh, p1, p2, lineWidth, fillerColor); UGL.DrawLine(vh, p3, p4, lineWidth, fillerColor); break; } } private void DrawVerticalDataZoomSlider(VertexHelper vh, DataZoom dataZoom) { if (!dataZoom.enable || !dataZoom.supportSlider) return; var p1 = new Vector3(dataZoom.context.x, dataZoom.context.y); var p2 = new Vector3(dataZoom.context.x, dataZoom.context.y + dataZoom.context.height); var p3 = new Vector3(dataZoom.context.x + dataZoom.context.width, dataZoom.context.y + dataZoom.context.height); var p4 = new Vector3(dataZoom.context.x + dataZoom.context.width, dataZoom.context.y); var lineColor = dataZoom.lineStyle.GetColor(chart.theme.dataZoom.dataLineColor); var lineWidth = dataZoom.lineStyle.GetWidth(chart.theme.dataZoom.dataLineWidth); var borderWidth = dataZoom.borderWidth == 0 ? chart.theme.dataZoom.borderWidth : dataZoom.borderWidth; var borderColor = dataZoom.GetBorderColor(chart.theme.dataZoom.borderColor); var backgroundColor = dataZoom.GetBackgroundColor(chart.theme.dataZoom.backgroundColor); var areaColor = dataZoom.areaStyle.GetColor(chart.theme.dataZoom.dataAreaColor); UGL.DrawQuadrilateral(vh, p1, p2, p3, p4, backgroundColor); var centerPos = new Vector3(dataZoom.context.x + dataZoom.context.width / 2, dataZoom.context.y + dataZoom.context.height / 2); UGL.DrawBorder(vh, centerPos, dataZoom.context.width, dataZoom.context.height, borderWidth, borderColor); if (dataZoom.showDataShadow && chart.series.Count > 0) { Serie serie = chart.series[0]; Axis axis = chart.GetChartComponent(0); var showData = serie.GetDataList(null); float scaleWid = dataZoom.context.height / (showData.Count - 1); Vector3 lp = Vector3.zero; Vector3 np = Vector3.zero; double minValue = 0; double maxValue = 0; SeriesHelper.GetYMinMaxValue(chart.series, null, 0, chart.IsAllAxisValue(), axis.inverse, out minValue, out maxValue); AxisHelper.AdjustMinMaxValue(axis, ref minValue, ref maxValue, true); int rate = 1; var sampleDist = serie.sampleDist < 2 ? 2 : serie.sampleDist; var maxCount = showData.Count; if (sampleDist > 0) rate = (int) ((maxCount - serie.minShow) / (dataZoom.context.height / sampleDist)); if (rate < 1) rate = 1; var totalAverage = serie.sampleAverage > 0 ? serie.sampleAverage : DataHelper.DataAverage(ref showData, serie.sampleType, serie.minShow, maxCount, rate); var dataChanging = false; for (int i = 0; i < maxCount; i += rate) { double value = DataHelper.SampleValue(ref showData, serie.sampleType, rate, serie.minShow, maxCount, totalAverage, i, serie.animation.GetUpdateAnimationDuration(), ref dataChanging, axis); float pY = dataZoom.context.y + i * scaleWid; float dataHig = (maxValue - minValue) == 0 ? 0 : (float) ((value - minValue) / (maxValue - minValue) * dataZoom.context.width); np = new Vector3(chart.chartX + chart.chartWidth - dataZoom.right - dataHig, pY); if (i > 0) { UGL.DrawLine(vh, lp, np, lineWidth, lineColor); Vector3 alp = new Vector3(lp.x, lp.y - lineWidth); Vector3 anp = new Vector3(np.x, np.y - lineWidth); Vector3 tnp = new Vector3(np.x, chart.chartY + dataZoom.bottom + lineWidth); Vector3 tlp = new Vector3(lp.x, chart.chartY + dataZoom.bottom + lineWidth); UGL.DrawQuadrilateral(vh, alp, anp, tnp, tlp, areaColor); } lp = np; } if (dataChanging) { chart.RefreshTopPainter(); } } switch (dataZoom.rangeMode) { case DataZoom.RangeMode.Percent: var start = dataZoom.context.y + dataZoom.context.height * dataZoom.start / 100; var end = dataZoom.context.y + dataZoom.context.height * dataZoom.end / 100; var fillerColor = dataZoom.GetFillerColor(chart.theme.dataZoom.fillerColor); p1 = new Vector2(dataZoom.context.x, start); p2 = new Vector2(dataZoom.context.x + dataZoom.context.width, start); p3 = new Vector2(dataZoom.context.x + dataZoom.context.width, end); p4 = new Vector2(dataZoom.context.x, end); UGL.DrawQuadrilateral(vh, p1, p2, p3, p4, fillerColor); UGL.DrawLine(vh, p1, p2, lineWidth, fillerColor); UGL.DrawLine(vh, p3, p4, lineWidth, fillerColor); break; } } } }