[feature][radar] support smooth line

This commit is contained in:
monitor1394
2022-07-17 20:57:38 +08:00
parent aee49322b7
commit eedafa7011
10 changed files with 120 additions and 43 deletions

View File

@@ -24,7 +24,7 @@ namespace XCharts.Editor
PropertyField("m_AxisName"); PropertyField("m_AxisName");
PropertyField("m_SplitLine"); PropertyField("m_SplitLine");
PropertyField("m_SplitArea"); PropertyField("m_SplitArea");
PropertyField("m_IndicatorList"); PropertyListField("m_IndicatorList");
--EditorGUI.indentLevel; --EditorGUI.indentLevel;
} }
} }

View File

@@ -9,6 +9,7 @@ namespace XCharts.Editor
{ {
PropertyField("m_RadarType"); PropertyField("m_RadarType");
PropertyField("m_RadarIndex"); PropertyField("m_RadarIndex");
PropertyField("m_Smooth");
PropertyField("m_Symbol"); PropertyField("m_Symbol");
PropertyField("m_LineStyle"); PropertyField("m_LineStyle");

View File

@@ -32,7 +32,7 @@ namespace XCharts.Runtime
if (!polar.context.isPointerEnter) if (!polar.context.isPointerEnter)
{ {
axis.context.pointerValue = double.PositiveInfinity; axis.context.pointerValue = double.NaN;
return; return;
} }

View File

@@ -180,7 +180,7 @@ namespace XCharts.Runtime
private void SetTooltipIndicatorLabel(Tooltip tooltip, Axis axis, ChartLabel label) private void SetTooltipIndicatorLabel(Tooltip tooltip, Axis axis, ChartLabel label)
{ {
if (label == null) return; if (label == null) return;
if (double.IsPositiveInfinity(axis.context.pointerValue)) return; if (double.IsNaN(axis.context.pointerValue)) return;
label.SetActive(true); label.SetActive(true);
label.SetTextActive(true); label.SetTextActive(true);
label.SetPosition(axis.context.pointerLabelPosition); label.SetPosition(axis.context.pointerLabelPosition);

View File

@@ -2,7 +2,7 @@ using System;
namespace XCharts.Runtime namespace XCharts.Runtime
{ {
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] [AttributeUsage(AttributeTargets.All, AllowMultiple = false)]
public class Since : Attribute public class Since : Attribute
{ {
public readonly string version; public readonly string version;

View File

@@ -21,8 +21,7 @@ namespace XCharts.Runtime
DrawParallelSerie(vh, serie); DrawParallelSerie(vh, serie);
} }
private void UpdateSerieContext() private void UpdateSerieContext() { }
{ }
private void DrawParallelSerie(VertexHelper vh, Parallel serie) private void DrawParallelSerie(VertexHelper vh, Parallel serie)
{ {
@@ -116,7 +115,10 @@ namespace XCharts.Runtime
lp = pos; lp = pos;
} }
if (isSmooth) if (isSmooth)
UGL.DrawCurves(vh, m_Points, lineWidth, lineColor, chart.settings.lineSmoothness, currProgress, isHorizonal); UGL.DrawCurves(vh, m_Points, lineWidth, lineColor,
chart.settings.lineSmoothStyle,
chart.settings.lineSmoothness,
UGL.Direction.XAxis, currProgress, isHorizonal);
else else
UGL.DrawLine(vh, m_Points, lineWidth, lineColor, isSmooth); UGL.DrawLine(vh, m_Points, lineWidth, lineColor, isSmooth);
} }

View File

@@ -11,9 +11,21 @@ namespace XCharts.Runtime
[SerieDataExtraField()] [SerieDataExtraField()]
public class Radar : Serie, INeedSerieContainer public class Radar : Serie, INeedSerieContainer
{ {
[SerializeField][Since("3.2.0")] private bool m_Smooth = false;
/// <summary>
/// Whether use smooth curve.
/// |是否平滑曲线。平滑曲线时不支持区域填充颜色。
/// </summary>
public bool smooth
{
get { return m_Smooth; }
set { if (PropertyUtil.SetStruct(ref m_Smooth, value)) { SetVerticesDirty(); } }
}
public int containerIndex { get; internal set; } public int containerIndex { get; internal set; }
public int containterInstanceId { get; internal set; } public int containterInstanceId { get; internal set; }
public override bool useDataNameForColor { get { return true; } } public override bool useDataNameForColor { get { return radarType == RadarType.Multiple; } }
public override bool multiDimensionLabel { get { return radarType == RadarType.Multiple; } } public override bool multiDimensionLabel { get { return radarType == RadarType.Multiple; } }
public static Serie AddDefaultSerie(BaseChart chart, string serieName) public static Serie AddDefaultSerie(BaseChart chart, string serieName)

View File

@@ -262,11 +262,11 @@ namespace XCharts.Runtime
{ {
toPoint = new Vector3(centerPos.x + radius * Mathf.Sin(currAngle), toPoint = new Vector3(centerPos.x + radius * Mathf.Sin(currAngle),
centerPos.y + radius * Mathf.Cos(currAngle)); centerPos.y + radius * Mathf.Cos(currAngle));
if (areaStyle != null && areaStyle.show) if (areaStyle != null && areaStyle.show && !serie.smooth)
{ {
UGL.DrawTriangle(vh, startPoint, toPoint, centerPos, areaColor, areaColor, areaToColor); UGL.DrawTriangle(vh, startPoint, toPoint, centerPos, areaColor, areaColor, areaToColor);
} }
if (lineStyle.show) if (lineStyle.show && !serie.smooth)
{ {
ChartDrawer.DrawLineStyle(vh, lineStyle.type, lineWidth, startPoint, toPoint, lineColor); ChartDrawer.DrawLineStyle(vh, lineStyle.type, lineWidth, startPoint, toPoint, lineColor);
} }
@@ -274,14 +274,24 @@ namespace XCharts.Runtime
} }
serieData.context.dataPoints.Add(startPoint); serieData.context.dataPoints.Add(startPoint);
} }
if (areaStyle != null && areaStyle.show) if (areaStyle != null && areaStyle.show && !serie.smooth)
{ {
UGL.DrawTriangle(vh, startPoint, firstPoint, centerPos, areaColor, areaColor, areaToColor); UGL.DrawTriangle(vh, startPoint, firstPoint, centerPos, areaColor, areaColor, areaToColor);
} }
if (lineStyle.show) if (lineStyle.show && !serie.smooth)
{ {
ChartDrawer.DrawLineStyle(vh, lineStyle.type, lineWidth, startPoint, firstPoint, lineColor); ChartDrawer.DrawLineStyle(vh, lineStyle.type, lineWidth, startPoint, firstPoint, lineColor);
} }
if (serie.smooth)
{
UGL.DrawCurves(vh, serieData.context.dataPoints, lineWidth, lineColor,
chart.settings.lineSmoothStyle,
chart.settings.lineSmoothness,
UGL.Direction.Random,
float.NaN, true);
}
if (symbol.show && symbol.type != SymbolType.None) if (symbol.show && symbol.type != SymbolType.None)
{ {
for (int m = 0; m < serieData.context.dataPoints.Count; m++) for (int m = 0; m < serieData.context.dataPoints.Count; m++)
@@ -394,11 +404,11 @@ namespace XCharts.Runtime
{ {
toPoint = new Vector3(p.x + radius * Mathf.Sin(currAngle), toPoint = new Vector3(p.x + radius * Mathf.Sin(currAngle),
p.y + radius * Mathf.Cos(currAngle)); p.y + radius * Mathf.Cos(currAngle));
if (areaStyle != null && areaStyle.show) if (areaStyle != null && areaStyle.show && !serie.smooth)
{ {
UGL.DrawTriangle(vh, startPoint, toPoint, p, areaColor, areaColor, areaToColor); UGL.DrawTriangle(vh, startPoint, toPoint, p, areaColor, areaColor, areaToColor);
} }
if (lineStyle.show) if (lineStyle.show && !serie.smooth)
{ {
if (radar.connectCenter) if (radar.connectCenter)
ChartDrawer.DrawLineStyle(vh, lineStyle, startPoint, centerPos, ChartDrawer.DrawLineStyle(vh, lineStyle, startPoint, centerPos,
@@ -409,14 +419,15 @@ namespace XCharts.Runtime
startPoint = toPoint; startPoint = toPoint;
lastColor = lineColor; lastColor = lineColor;
} }
serie.context.dataPoints.Add(startPoint);
serieData.context.position = startPoint; serieData.context.position = startPoint;
serieData.context.labelPosition = startPoint; serieData.context.labelPosition = startPoint;
if (areaStyle != null && areaStyle.show && j == endIndex) if (areaStyle != null && areaStyle.show && j == endIndex && !serie.smooth)
{ {
UGL.DrawTriangle(vh, startPoint, firstPoint, centerPos, areaColor, areaColor, areaToColor); UGL.DrawTriangle(vh, startPoint, firstPoint, centerPos, areaColor, areaColor, areaToColor);
} }
if (lineStyle.show && j == endIndex) if (lineStyle.show && j == endIndex && !serie.smooth)
{ {
if (radar.connectCenter) if (radar.connectCenter)
ChartDrawer.DrawLineStyle(vh, lineStyle, startPoint, centerPos, ChartDrawer.DrawLineStyle(vh, lineStyle, startPoint, centerPos,
@@ -425,6 +436,16 @@ namespace XCharts.Runtime
LineStyle.Type.Solid, lineColor, radar.lineGradient ? firstColor : lineColor); LineStyle.Type.Solid, lineColor, radar.lineGradient ? firstColor : lineColor);
} }
} }
if (serie.smooth)
{
var lineWidth = serie.lineStyle.GetWidth(chart.theme.serie.lineWidth);
var lineColor = SerieHelper.GetLineColor(serie, null, chart.theme, serie.context.colorIndex, false);
UGL.DrawCurves(vh, serie.context.dataPoints, lineWidth, lineColor,
chart.settings.lineSmoothStyle,
chart.settings.lineSmoothness,
UGL.Direction.Random,
float.NaN, true);
}
if (serie.symbol.show && serie.symbol.type != SymbolType.None) if (serie.symbol.show && serie.symbol.type != SymbolType.None)
{ {
for (int j = 0; j < serie.data.Count; j++) for (int j = 0; j < serie.data.Count; j++)

View File

@@ -10,6 +10,24 @@ namespace XUGL
/// </summary> /// </summary>
public static class UGL public static class UGL
{ {
/// <summary>
/// 曲线方向
/// </summary>
public enum Direction
{
/// <summary>
/// 沿X轴方向
/// </summary>
XAxis,
/// <summary>
/// 沿Y轴方向
/// </summary>
YAxis,
/// <summary>
/// 随机无序的。如一个闭合的环状曲线。
/// </summary>
Random
}
private static readonly Color32 s_ClearColor32 = new Color32(0, 0, 0, 0); private static readonly Color32 s_ClearColor32 = new Color32(0, 0, 0, 0);
private static readonly Vector2 s_ZeroVector2 = Vector2.zero; private static readonly Vector2 s_ZeroVector2 = Vector2.zero;
private static UIVertex[] s_Vertex = new UIVertex[4]; private static UIVertex[] s_Vertex = new UIVertex[4];
@@ -134,7 +152,7 @@ namespace XUGL
} }
else if (smooth) else if (smooth)
{ {
DrawCurves(vh, points, width, color, 2); DrawCurves(vh, points, width, color, 2, 2, Direction.XAxis, float.NaN, closepath);
} }
else else
{ {
@@ -1752,42 +1770,67 @@ namespace XUGL
/// <param name="lineWidth">曲线宽</param> /// <param name="lineWidth">曲线宽</param>
/// <param name="lineColor">曲线颜色</param> /// <param name="lineColor">曲线颜色</param>
public static void DrawCurves(VertexHelper vh, Vector3 sp, Vector3 ep, Vector3 cp1, Vector3 cp2, public static void DrawCurves(VertexHelper vh, Vector3 sp, Vector3 ep, Vector3 cp1, Vector3 cp2,
float lineWidth, Color32 lineColor, float smoothness) float lineWidth, Color32 lineColor, float smoothness, Direction dire = Direction.XAxis)
{ {
var dist = Vector3.Distance(sp, ep); var dist = Vector3.Distance(sp, ep);
var segment = (int) (dist / (smoothness <= 0 ? 2f : smoothness)); var segment = (int) (dist / (smoothness <= 0 ? 2f : smoothness));
UGLHelper.GetBezierList2(ref s_CurvesPosList, sp, ep, segment, cp1, cp2); UGLHelper.GetBezierList2(ref s_CurvesPosList, sp, ep, segment, cp1, cp2);
DrawCurvesInternal(vh, s_CurvesPosList, lineWidth, lineColor); DrawCurvesInternal(vh, s_CurvesPosList, lineWidth, lineColor, dire);
} }
/// <summary>
/// 画贝塞尔曲线
/// </summary>
/// <param name="vh"></param>
/// <param name="points">坐标点列表</param>
/// <param name="width">曲线宽</param>
/// <param name="color">曲线颜色</param>
/// <param name="smoothStyle">曲线样式</param>
/// <param name="smoothness">平滑度</param>
/// <param name="dire">曲线方向</param>
/// <param name="currProgress">当前绘制进度</param>
/// <param name="closed">曲线是否闭合</param>
public static void DrawCurves(VertexHelper vh, List<Vector3> points, float width, Color32 color, public static void DrawCurves(VertexHelper vh, List<Vector3> points, float width, Color32 color,
float smoothness, float currProgress = float.PositiveInfinity, bool isYAxis = false) float smoothStyle, float smoothness, Direction dire, float currProgress = float.NaN,
bool closed = false)
{ {
for (int i = 0; i < points.Count - 1; i++) var count = points.Count;
var size = (closed?count : count - 1);
if (closed)
dire = Direction.Random;
for (int i = 0; i < size; i++)
{ {
var sp = points[i]; var sp = points[i];
var ep = points[i + 1]; var ep = closed?(i == size - 1 ? points[0] : points[i + 1]) : points[i + 1];
var lsp = i > 0 ? points[i - 1] : sp; var lsp = i > 0 ? points[i - 1] : (closed?points[count - 1] : sp);
var nep = i < points.Count - 2 ? points[i + 2] : ep; var nep = i < points.Count - 2 ? points[i + 2] : (closed?points[(i + 2) % count] : ep);
var smoothness2 = smoothness; var smoothness2 = smoothness;
if (currProgress != float.PositiveInfinity) if (currProgress != float.NaN)
{ {
if (isYAxis) switch (dire)
smoothness2 = ep.y <= currProgress ? smoothness : smoothness * 0.5f; {
else case Direction.XAxis:
smoothness2 = ep.x <= currProgress ? smoothness : smoothness * 0.5f; smoothness2 = ep.x <= currProgress ? smoothness : smoothness * 0.5f;
break;
case Direction.YAxis:
smoothness2 = ep.y <= currProgress ? smoothness : smoothness * 0.5f;
break;
case Direction.Random:
smoothness2 = smoothness * 0.5f;
break;
} }
if (isYAxis) }
UGLHelper.GetBezierListVertical(ref s_CurvesPosList, sp, ep, smoothness2); if (dire == Direction.YAxis)
UGLHelper.GetBezierListVertical(ref s_CurvesPosList, sp, ep, smoothness2, smoothStyle);
else else
UGLHelper.GetBezierList(ref s_CurvesPosList, sp, ep, lsp, nep, smoothness2); UGLHelper.GetBezierList(ref s_CurvesPosList, sp, ep, lsp, nep, smoothness2, smoothStyle);
DrawCurvesInternal(vh, s_CurvesPosList, width, color, currProgress, isYAxis); DrawCurvesInternal(vh, s_CurvesPosList, width, color, dire, currProgress);
} }
} }
private static void DrawCurvesInternal(VertexHelper vh, List<Vector3> curvesPosList, float lineWidth, private static void DrawCurvesInternal(VertexHelper vh, List<Vector3> curvesPosList, float lineWidth,
Color32 lineColor, float currProgress = float.PositiveInfinity, bool isYAxis = false) Color32 lineColor, Direction dire, float currProgress = float.NaN)
{ {
if (curvesPosList.Count > 1) if (curvesPosList.Count > 1)
{ {
@@ -1805,11 +1848,11 @@ namespace XUGL
for (int i = 1; i < curvesPosList.Count; i++) for (int i = 1; i < curvesPosList.Count; i++)
{ {
to = curvesPosList[i]; to = curvesPosList[i];
if (currProgress != float.PositiveInfinity) if (currProgress != float.NaN)
{ {
if (isYAxis && to.y > currProgress) if (dire == Direction.YAxis && to.y > currProgress)
break; break;
if (!isYAxis && to.x > currProgress) if (dire == Direction.XAxis && to.x > currProgress)
break; break;
} }

View File

@@ -110,22 +110,21 @@ namespace XUGL
public static void GetBezierList(ref List<Vector3> posList, Vector3 sp, Vector3 ep, public static void GetBezierList(ref List<Vector3> posList, Vector3 sp, Vector3 ep,
Vector3 lsp, Vector3 nep, float smoothness = 2f, float k = 2.0f, bool limit = false) Vector3 lsp, Vector3 nep, float smoothness = 2f, float k = 2.0f, bool limit = false)
{ {
float dist = Mathf.Abs(sp.x - ep.x); var dist = Vector3.Distance(sp, ep);
Vector3 cp1, cp2; Vector3 cp1, cp2;
var dir = (ep - sp).normalized; var dir = (ep - sp).normalized;
var diff = dist / k; var diff = dist / k;
if (lsp == sp) if (lsp == sp)
{ {
cp1 = sp + dist / k * dir * 1; cp1 = sp + (nep - ep).normalized * diff;
if (limit)
cp1.y = sp.y; cp1.y = sp.y;
cp1 = sp;
} }
else else
{ {
cp1 = sp + (ep - lsp).normalized * diff; cp1 = sp + (ep - lsp).normalized * diff;
if (limit) if (limit)
cp1.y = sp.y; cp1.y = sp.y;
} }
if (nep == ep) if (nep == ep)
{ {
@@ -137,7 +136,6 @@ namespace XUGL
if (limit) if (limit)
cp2.y = ep.y; cp2.y = ep.y;
} }
dist = Vector3.Distance(sp, ep);
int segment = (int) (dist / (smoothness <= 0 ? 2f : smoothness)); int segment = (int) (dist / (smoothness <= 0 ? 2f : smoothness));
if (segment < 1) segment = (int) (dist / 0.5f); if (segment < 1) segment = (int) (dist / 0.5f);
if (segment < 4) segment = 4; if (segment < 4) segment = 4;