diff --git a/CHANGELOG-EN.md b/CHANGELOG-EN.md index 5897536a..30e27a35 100644 --- a/CHANGELOG-EN.md +++ b/CHANGELOG-EN.md @@ -34,6 +34,7 @@ ## Latest +* (2021.05.08) Added `Liquidchart` support for `Rect` shape * (2021.05.07) Improved the `Axis` scale performance #135 * (2021.05.01) Added `Settings` parameters for painter's material #140 * (2021.05.01) Fixed an issue where some super large or super small values could not be properly represented diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dcff23e..95beadce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ ## Latest +* (2021.05.08) 增加`LiquidChart`的方形水位图支持 * (2021.05.07) 优化`Axis`的刻度表现 #135 * (2021.05.01) 增加`Settings`中关于关于材质球设置的参数 #140 * (2021.05.01) 修复无法正确表示部分超大或超小数值的问题 diff --git a/Documentation/XCharts配置项手册.md b/Documentation/XCharts配置项手册.md index 5bbe1acc..91b6531c 100644 --- a/Documentation/XCharts配置项手册.md +++ b/Documentation/XCharts配置项手册.md @@ -260,11 +260,14 @@ * `shapeWidth`:容器的厚度。 * `gap`:间隙。容器和液体的间隙。 * `center`:中心点。数组的第一项是横坐标,第二项是纵坐标。当值为0-1之间时表示百分比,设置成百分比时表示图表宽高最小值的百分比。 -* `radius`:半径。 -* `smoothness`:开启或关闭缩放区域功能。 * `backgroundColor`:背景色,默认透明。 * `color`:容器颜色。当`autoColor`为`false`时生效。 * `autoColor`:是否自动颜色。默认`true`。为`true`时颜色会和`serie`一致。 +* `radius`:半径。 +* `smoothness`:开启或关闭缩放区域功能。 +* `width`:容器的宽。shape为Rect时有效。 +* `height`:容器的高。shape为Rect时有效。 +* `cornerRadius`: 容器的圆角半径。用数组分别指定4个圆角半径(顺时针左上,右上,右下,左下)。shape为Rect时有效。 ## `DataZoom` diff --git a/Documentation/xcharts-configuration-EN.md b/Documentation/xcharts-configuration-EN.md index 1acf386e..0f5a3baf 100644 --- a/Documentation/xcharts-configuration-EN.md +++ b/Documentation/xcharts-configuration-EN.md @@ -266,6 +266,9 @@ Vessel component for liquid chart. There can be multiple vessels in a Chart, whi * `backgroundColor`: Background color of polar, which is transparent by default. [default: `Color.clear`] * `color`: Vessel color. The default is consistent with Serie. [default: `Color32(70, 70, 240, 255)`] * `autoColor`: Whether automatic color. If true, the color matches serie. [default: `true`] +* `width`:The width of vessel. This value is valid when `shape` is `Rect`. +* `height`:The height of vessel. This value is valid when `shape` is `Rect`. +* `cornerRadius`: The radius of rounded corner. This value is valid when `shape` is `Rect`. ## `DataZoom` diff --git a/Editor/PropertyDrawers/VesselDrawer.cs b/Editor/PropertyDrawers/VesselDrawer.cs index c2bbdf48..325ebfc9 100644 --- a/Editor/PropertyDrawers/VesselDrawer.cs +++ b/Editor/PropertyDrawers/VesselDrawer.cs @@ -20,15 +20,26 @@ namespace XCharts if (MakeFoldout(prop, "m_Show")) { ++EditorGUI.indentLevel; + var shape = (Vessel.Shape)prop.FindPropertyRelative("m_Shape").intValue; PropertyField(prop, "m_Shape"); PropertyField(prop, "m_ShapeWidth"); PropertyField(prop, "m_Gap"); PropertyTwoFiled(prop, "m_Center"); - PropertyField(prop, "m_Radius"); PropertyField(prop, "m_BackgroundColor"); PropertyField(prop, "m_Color"); PropertyField(prop, "m_AutoColor"); - PropertyField(prop, "m_Smoothness"); + switch (shape) + { + case Vessel.Shape.Circle: + PropertyField(prop, "m_Radius"); + PropertyField(prop, "m_Smoothness"); + break; + case Vessel.Shape.Rect: + PropertyField(prop, "m_Width"); + PropertyField(prop, "m_Height"); + PropertyField(prop, "m_CornerRadius"); + break; + } --EditorGUI.indentLevel; } } diff --git a/Runtime/Component/Main/Vessel.cs b/Runtime/Component/Main/Vessel.cs index e8d48d15..353b8698 100644 --- a/Runtime/Component/Main/Vessel.cs +++ b/Runtime/Component/Main/Vessel.cs @@ -46,13 +46,16 @@ namespace XCharts [SerializeField] private bool m_Show = true; [SerializeField] private Shape m_Shape = Shape.Circle; [SerializeField] private float m_ShapeWidth = 5f; - [SerializeField] private float m_Gap = 10f; + [SerializeField] private float m_Gap = 5f; [SerializeField] private Color32 m_Color; [SerializeField] private Color32 m_BackgroundColor; [SerializeField] private bool m_AutoColor = true; [SerializeField] private float[] m_Center = new float[2] { 0.5f, 0.5f }; - [SerializeField] private float m_Radius = 0.5f; + [SerializeField] private float m_Radius = 0.35f; [SerializeField] [Range(0.5f, 10f)] private float m_Smoothness = 1f; + [SerializeField] private float m_Width = 0.5f; + [SerializeField] private float m_Height = 0.7f; + [SerializeField] private float[] m_CornerRadius = new float[] { 0, 0, 0, 0 }; /// /// Whether to show the vessel. @@ -108,7 +111,7 @@ namespace XCharts } /// /// The radius of vessel. - /// When value between 0 and 1 represents a percentage relative to the chart. + /// When value between 0 and 1 represents a percentage relative to the chart. /// 半径。 /// [default: 0.35f] /// @@ -118,6 +121,28 @@ namespace XCharts set { if (PropertyUtil.SetStruct(ref m_Radius, value)) SetAllDirty(); } } /// + /// The width of vessel. + /// When value between 0 and 1 represents a percentage relative to the chart. + /// 容器的宽。shape为Rect时有效。 + /// [default: 0.35f] + /// + public float width + { + get { return m_Width; } + set { if (PropertyUtil.SetStruct(ref m_Width, value)) SetAllDirty(); } + } + /// + /// The height of vessel. + /// When value between 0 and 1 represents a percentage relative to the chart. + /// 容器的高。shape为Rect时有效。 + /// [default: 0.35f] + /// + public float height + { + get { return m_Height; } + set { if (PropertyUtil.SetStruct(ref m_Height, value)) SetAllDirty(); } + } + /// /// The smoothness of wave. /// 水波平滑度。 /// [default: 1f] @@ -156,6 +181,15 @@ namespace XCharts get { return m_AutoColor; } set { if (PropertyUtil.SetStruct(ref m_AutoColor, value)) SetVerticesDirty(); } } + /// + /// The radius of rounded corner. Its unit is px. Use array to respectively specify the 4 corner radiuses((clockwise upper left, upper right, bottom right and bottom left)). + /// 容器的圆角半径。用数组分别指定4个圆角半径(顺时针左上,右上,右下,左下)。shape为Rect时有效。 + /// + public float[] cornerRadius + { + get { return m_CornerRadius; } + set { if (PropertyUtil.SetClass(ref m_CornerRadius, value, true)) SetVerticesDirty(); } + } public int index { get; internal set; } /// /// the runtime center position of vessel. @@ -172,6 +206,8 @@ namespace XCharts /// 运行时内半径。扣除厚度和间隙后的实际半径。 /// public float runtimeInnerRadius { get; internal set; } + public float runtimeWidth { get; set; } + public float runtimeHeight { get; set; } public static Vessel defaultVessel { get @@ -181,8 +217,10 @@ namespace XCharts m_Show = true, m_Shape = Shape.Circle, m_ShapeWidth = 5, - m_Gap = 10, + m_Gap = 5, m_Radius = 0.35f, + m_Width = 0.5f, + m_Height = 0.7f, m_AutoColor = true, m_Color = new Color32(70, 70, 240, 255), m_Smoothness = 1 diff --git a/Runtime/Internal/DrawSerieLiquid.cs b/Runtime/Internal/DrawSerieLiquid.cs index 1995345f..8a5c3f19 100644 --- a/Runtime/Internal/DrawSerieLiquid.cs +++ b/Runtime/Internal/DrawSerieLiquid.cs @@ -113,9 +113,24 @@ namespace XCharts { if (vessel.backgroundColor.a != 0) { - var cenPos = vessel.runtimeCenterPos; - var radius = vessel.runtimeRadius; - UGL.DrawCricle(vh, cenPos, vessel.runtimeInnerRadius, vessel.backgroundColor, chart.settings.cicleSmoothness); + switch (vessel.shape) + { + case Vessel.Shape.Circle: + var cenPos = vessel.runtimeCenterPos; + var radius = vessel.runtimeRadius; + UGL.DrawCricle(vh, cenPos, vessel.runtimeInnerRadius + vessel.gap, vessel.backgroundColor, + chart.settings.cicleSmoothness); + UGL.DrawDoughnut(vh, cenPos, vessel.runtimeInnerRadius, vessel.runtimeInnerRadius + vessel.gap, + vessel.backgroundColor, Color.clear, chart.settings.cicleSmoothness); + break; + case Vessel.Shape.Rect: + UGL.DrawRectangle(vh, vessel.runtimeCenterPos, vessel.runtimeWidth / 2, vessel.runtimeHeight / 2, + vessel.backgroundColor); + break; + default: + break; + } + } } } @@ -125,7 +140,18 @@ namespace XCharts var vessel = chart.GetVessel(serie.vesselIndex); if (vessel != null) { - DrawCirleVessel(vh, vessel); + switch (vessel.shape) + { + case Vessel.Shape.Circle: + DrawCirleVessel(vh, vessel); + break; + case Vessel.Shape.Rect: + DrawRectVessel(vh, vessel); + break; + default: + DrawCirleVessel(vh, vessel); + break; + } } } @@ -135,7 +161,26 @@ namespace XCharts var radius = vessel.runtimeRadius; var serie = SeriesHelper.GetSerieByVesselIndex(chart.series, vessel.index); var vesselColor = VesselHelper.GetColor(vessel, serie, chart.theme, chart.m_LegendRealShowName); - UGL.DrawDoughnut(vh, cenPos, radius - vessel.shapeWidth, radius, vesselColor, Color.clear, chart.settings.cicleSmoothness); + if (vessel.gap != 0) + { + UGL.DrawDoughnut(vh, cenPos, vessel.runtimeInnerRadius, vessel.runtimeInnerRadius + vessel.gap, + vessel.backgroundColor, Color.clear, chart.settings.cicleSmoothness); + } + UGL.DrawDoughnut(vh, cenPos, radius - vessel.shapeWidth, radius, vesselColor, Color.clear, + chart.settings.cicleSmoothness); + } + + private void DrawRectVessel(VertexHelper vh, Vessel vessel) + { + var serie = SeriesHelper.GetSerieByVesselIndex(chart.series, vessel.index); + var vesselColor = VesselHelper.GetColor(vessel, serie, chart.theme, chart.m_LegendRealShowName); + if (vessel.gap != 0) + { + UGL.DrawBorder(vh, vessel.runtimeCenterPos, vessel.runtimeWidth, + vessel.runtimeHeight, vessel.gap, vessel.backgroundColor, 0, vessel.cornerRadius); + } + UGL.DrawBorder(vh, vessel.runtimeCenterPos, vessel.runtimeWidth + 2 * vessel.gap, + vessel.runtimeHeight + 2 * vessel.gap, vessel.shapeWidth, vesselColor, 0, vessel.cornerRadius); } private void DrawLiquid(VertexHelper vh, Serie serie) @@ -144,6 +189,22 @@ namespace XCharts if (serie.animation.HasFadeOut()) return; var vessel = chart.GetVessel(serie.vesselIndex); if (vessel == null) return; + switch (vessel.shape) + { + case Vessel.Shape.Circle: + DrawCirleLiquid(vh, serie, vessel); + break; + case Vessel.Shape.Rect: + DrawRectLiquid(vh, serie, vessel); + break; + default: + DrawCirleLiquid(vh, serie, vessel); + break; + } + } + + private void DrawCirleLiquid(VertexHelper vh, Serie serie, Vessel vessel) + { var cenPos = vessel.runtimeCenterPos; var radius = vessel.runtimeInnerRadius; var serieData = serie.GetSerieData(0); @@ -160,7 +221,7 @@ namespace XCharts serieData.labelPosition = cenPos; m_UpdateLabelText = true; } - if (value == 0) return; + if (value <= 0) return; var colorIndex = chart.m_LegendRealShowName.IndexOf(serie.name); var realHig = (value - serie.min) / (serie.max - serie.min) * radius * 2; @@ -255,5 +316,120 @@ namespace XCharts chart.RefreshPainter(serie); } } + + private void DrawRectLiquid(VertexHelper vh, Serie serie, Vessel vessel) + { + var cenPos = vessel.runtimeCenterPos; + var serieData = serie.GetSerieData(0); + if (serieData == null) return; + var dataChangeDuration = serie.animation.GetUpdateAnimationDuration(); + var value = serieData.GetCurrData(1, dataChangeDuration); + if (serie.runtimeCheckValue != value) + { + serie.runtimeCheckValue = value; + m_UpdateLabelText = true; + } + if (serieData.labelPosition != cenPos) + { + serieData.labelPosition = cenPos; + m_UpdateLabelText = true; + } + if (value <= 0) return; + var colorIndex = chart.m_LegendRealShowName.IndexOf(serie.name); + + var realHig = (value - serie.min) / (serie.max - serie.min) * vessel.runtimeHeight; + serie.animation.InitProgress(1, 0, realHig); + var hig = serie.animation.IsFinish() ? realHig : serie.animation.GetCurrDetail(); + var color = SerieHelper.GetItemColor(serie, serieData, chart.theme, colorIndex, false); + var toColor = SerieHelper.GetItemToColor(serie, serieData, chart.theme, colorIndex, false); + var isNeedGradient = !ChartHelper.IsValueEqualsColor(color, toColor); + var isFull = hig >= vessel.runtimeHeight; + if (hig >= vessel.runtimeHeight) hig = vessel.runtimeHeight; + if (isFull && !isNeedGradient) + { + UGL.DrawRectangle(vh, cenPos, vessel.runtimeWidth / 2, vessel.runtimeHeight / 2, toColor); + } + else + { + var startY = cenPos.y - vessel.runtimeHeight / 2 + hig; + var waveStartPos = new Vector3(cenPos.x - vessel.runtimeWidth / 2, startY); + var waveEndPos = new Vector3(cenPos.x + vessel.runtimeWidth / 2, startY); + var startX = waveStartPos.x; + var endX = waveEndPos.x; + + var step = vessel.smoothness; + if (step < 0.5f) step = 0.5f; + var lup = waveStartPos; + var ldp = new Vector3(startX, vessel.runtimeCenterPos.y - vessel.runtimeHeight / 2); + var nup = Vector3.zero; + var ndp = Vector3.zero; + var angle = 0f; + var isStarted = false; + var isEnded = false; + var waveHeight = isFull ? 0 : serie.waveHeight; + serie.runtimeWaveSpeed += serie.waveSpeed * Time.deltaTime; + while (startX < endX) + { + startX += step; + if (startX > endX) startX = endX; + if (startX > waveStartPos.x && !isStarted) + { + startX = waveStartPos.x; + isStarted = true; + } + if (startX > waveEndPos.x && !isEnded) + { + startX = waveEndPos.x; + isEnded = true; + } + var py = Mathf.Sqrt(Mathf.Pow(vessel.runtimeHeight, 2) - Mathf.Pow(Mathf.Abs(cenPos.x - startX), 2)); + if (startX < waveStartPos.x || startX > waveEndPos.x) + { + nup = new Vector3(startX, cenPos.y + py); + } + else + { + var py2 = waveHeight * Mathf.Sin(1 / serie.waveLength * angle + serie.runtimeWaveSpeed + serie.waveOffset); + var nupY = waveStartPos.y + py2; + nup = new Vector3(startX, nupY); + angle += step; + } + + ndp = new Vector3(startX, cenPos.y - vessel.runtimeHeight / 2); + if (nup.y > cenPos.y + vessel.runtimeHeight / 2) + { + nup.y = cenPos.y + vessel.runtimeHeight / 2; + } + if (nup.y < cenPos.y - vessel.runtimeHeight / 2) + { + nup.y = cenPos.y - vessel.runtimeHeight / 2; + } + if (!ChartHelper.IsValueEqualsColor(color, toColor)) + { + var colorMin = cenPos.y - vessel.runtimeHeight; + var colorMax = startY + serie.waveHeight; + var tcolor1 = Color32.Lerp(color, toColor, 1 - (lup.y - colorMin) / (colorMax - colorMin)); + var tcolor2 = Color32.Lerp(color, toColor, 1 - (ldp.y - colorMin) / (colorMax - colorMin)); + UGL.DrawQuadrilateral(vh, lup, nup, ndp, ldp, tcolor1, tcolor2); + } + else + { + UGL.DrawQuadrilateral(vh, lup, nup, ndp, ldp, color); + } + lup = nup; + ldp = ndp; + } + } + if (serie.waveSpeed != 0 && Application.isPlaying && !isFull) + { + chart.RefreshPainter(serie); + } + if (!serie.animation.IsFinish()) + { + serie.animation.CheckProgress(realHig); + chart.m_IsPlayingAnimation = true; + chart.RefreshPainter(serie); + } + } } } \ No newline at end of file diff --git a/Runtime/Internal/Helper/VesselHelper.cs b/Runtime/Internal/Helper/VesselHelper.cs index cc0e5ce3..4ad261d6 100644 --- a/Runtime/Internal/Helper/VesselHelper.cs +++ b/Runtime/Internal/Helper/VesselHelper.cs @@ -34,6 +34,8 @@ namespace XCharts vessel.runtimeCenterPos = chartPosition + new Vector3(centerX, centerY); vessel.runtimeRadius = ChartHelper.GetRuntimeRelativeOrAbsoluteValue(vessel.radius, checkWidth); vessel.runtimeInnerRadius = vessel.runtimeRadius - vessel.shapeWidth - vessel.gap; + vessel.runtimeWidth = ChartHelper.GetRuntimeRelativeOrAbsoluteValue(vessel.width, checkWidth) - 2 * vessel.gap; + vessel.runtimeHeight = ChartHelper.GetRuntimeRelativeOrAbsoluteValue(vessel.height, chartHeight) - 2 * vessel.gap; } } } \ No newline at end of file