diff --git a/Bin/SunnyUI.Demo.exe b/Bin/SunnyUI.Demo.exe index 6e42662f..37d861fe 100644 Binary files a/Bin/SunnyUI.Demo.exe and b/Bin/SunnyUI.Demo.exe differ diff --git a/Bin/SunnyUI.dll b/Bin/SunnyUI.dll index 129759dd..4edfd172 100644 Binary files a/Bin/SunnyUI.dll and b/Bin/SunnyUI.dll differ diff --git a/Bin/SunnyUI.pdb b/Bin/SunnyUI.pdb index 2de7fe51..e48cd7b0 100644 Binary files a/Bin/SunnyUI.pdb and b/Bin/SunnyUI.pdb differ diff --git a/SunnyUI.Demo/Charts/FLineChart.Designer.cs b/SunnyUI.Demo/Charts/FLineChart.Designer.cs index d06e2037..e5d30ffe 100644 --- a/SunnyUI.Demo/Charts/FLineChart.Designer.cs +++ b/SunnyUI.Demo/Charts/FLineChart.Designer.cs @@ -33,7 +33,7 @@ this.uiImageButton2 = new Sunny.UI.UIImageButton(); this.uiImageButton1 = new Sunny.UI.UIImageButton(); this.uiLine1 = new Sunny.UI.UILine(); - this.uiLineChart1 = new Sunny.UI.UILineChart(); + this.LineChart = new Sunny.UI.UILineChart(); this.PagePanel.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.uiImageButton3)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.uiImageButton2)).BeginInit(); @@ -47,7 +47,7 @@ this.PagePanel.Controls.Add(this.uiImageButton2); this.PagePanel.Controls.Add(this.uiImageButton1); this.PagePanel.Controls.Add(this.uiLine1); - this.PagePanel.Controls.Add(this.uiLineChart1); + this.PagePanel.Controls.Add(this.LineChart); this.PagePanel.Size = new System.Drawing.Size(800, 539); // // uiSymbolButton1 @@ -62,6 +62,7 @@ this.uiSymbolButton1.Symbol = 61952; this.uiSymbolButton1.TabIndex = 34; this.uiSymbolButton1.Text = "数据"; + this.uiSymbolButton1.Click += new System.EventHandler(this.uiSymbolButton1_Click); // // uiImageButton3 // @@ -113,18 +114,19 @@ this.uiLine1.Text = "UIBarChart"; this.uiLine1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // - // uiLineChart1 + // LineChart // - this.uiLineChart1.FillColor = System.Drawing.Color.FromArgb(((int)(((byte)(244)))), ((int)(((byte)(244)))), ((int)(((byte)(244))))); - this.uiLineChart1.Font = new System.Drawing.Font("微软雅黑", 12F); - this.uiLineChart1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(54)))), ((int)(((byte)(54)))), ((int)(((byte)(54))))); - this.uiLineChart1.Location = new System.Drawing.Point(30, 48); - this.uiLineChart1.MinimumSize = new System.Drawing.Size(1, 1); - this.uiLineChart1.Name = "uiLineChart1"; - this.uiLineChart1.Option = null; - this.uiLineChart1.Size = new System.Drawing.Size(670, 400); - this.uiLineChart1.TabIndex = 35; - this.uiLineChart1.Text = "uiLineChart1"; + this.LineChart.FillColor = System.Drawing.Color.FromArgb(((int)(((byte)(244)))), ((int)(((byte)(244)))), ((int)(((byte)(244))))); + this.LineChart.Font = new System.Drawing.Font("微软雅黑", 12F); + this.LineChart.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(54)))), ((int)(((byte)(54)))), ((int)(((byte)(54))))); + this.LineChart.Location = new System.Drawing.Point(30, 48); + this.LineChart.MinimumSize = new System.Drawing.Size(1, 1); + this.LineChart.Name = "LineChart"; + this.LineChart.Option = null; + this.LineChart.Size = new System.Drawing.Size(670, 400); + this.LineChart.TabIndex = 35; + this.LineChart.Text = "uiLineChart1"; + this.LineChart.PointValue += new Sunny.UI.UILineChart.OnPointValue(this.LineChart_PointValue); // // FLineChart // @@ -149,6 +151,6 @@ private UIImageButton uiImageButton2; private UIImageButton uiImageButton1; private UILine uiLine1; - private UILineChart uiLineChart1; + private UILineChart LineChart; } } \ No newline at end of file diff --git a/SunnyUI.Demo/Charts/FLineChart.cs b/SunnyUI.Demo/Charts/FLineChart.cs index 2bf080ab..90066bae 100644 --- a/SunnyUI.Demo/Charts/FLineChart.cs +++ b/SunnyUI.Demo/Charts/FLineChart.cs @@ -1,4 +1,8 @@ -namespace Sunny.UI.Demo.Charts +using System; +using System.Drawing; +using System.Text; + +namespace Sunny.UI.Demo.Charts { public partial class FLineChart : UITitlePage { @@ -6,5 +10,63 @@ { InitializeComponent(); } + + private void uiSymbolButton1_Click(object sender, System.EventArgs e) + { + UILineOption option = new UILineOption(); + option.Title = new UITitle(); + option.Title.Text = "SunnyUI"; + option.Title.SubText = "LineChart"; + + option.XAxisType = UIAxisType.Time; + + var series = option.AddSeries(new UILineSeries("Line1")); + DateTime dt = new DateTime(2020, 10, 4); + series.Add(dt.AddHours(0), 1.2); + series.Add(dt.AddHours(0.1), 2.2); + series.Add(dt.AddHours(0.2), 3.2); + series.Add(dt.AddHours(0.3), 4.2); + series.Add(dt.AddHours(0.4), 3.2); + series.Add(dt.AddHours(0.5), 2.2); + series.Symbol = UILinePointSymbol.Square; + series.SymbolSize = 4; + series.SymbolLineWidth = 2; + series.SymbolColor = Color.Red; + + series = option.AddSeries(new UILineSeries("Line2")); + series.Add(dt.AddHours(0.3), 3.3); + series.Add(dt.AddHours(0.4), 2.3); + series.Add(dt.AddHours(0.5), 2.3); + series.Add(dt.AddHours(0.6), 1.3); + series.Add(dt.AddHours(0.7), 2.3); + series.Add(dt.AddHours(0.8), 4.3); + series.Symbol = UILinePointSymbol.Star; + series.SymbolSize = 4; + series.SymbolLineWidth = 2; + series.SymbolColor = Color.Red; + series.Smooth = true; + + // option.XAxis.Min = new DateTimeInt64(dt.AddDays(-1)); + // option.XAxis.Max = new DateTimeInt64(dt.AddDays(1)); + // option.XAxis.MaxAuto = false; + // option.XAxis.MinAuto = false; + + option.XAxis.Name = "数值"; + option.YAxis.Name = "数值"; + + LineChart.SetOption(option); + } + + private void LineChart_PointValue(object sender, System.Collections.Generic.List points) + { + StringBuilder sb = new StringBuilder(); + foreach (var point in points) + { + sb.Append(point.Name + ", " + point.Index + ", " + point.X + ", " + point.Y); + sb.Append('\n'); + } + + Console.WriteLine(sb.ToString()); + } } } diff --git a/SunnyUI/Charts/UIBarChart.cs b/SunnyUI/Charts/UIBarChart.cs index 1cf3d2af..b7d833ef 100644 --- a/SunnyUI/Charts/UIBarChart.cs +++ b/SunnyUI/Charts/UIBarChart.cs @@ -62,8 +62,11 @@ namespace Sunny.UI double max = double.MinValue; foreach (var series in BarOption.Series) { - min = Math.Min(min, series.Data.Min()); - max = Math.Max(max, series.Data.Max()); + if (series.Data.Count > 0) + { + min = Math.Min(min, series.Data.Min()); + max = Math.Max(max, series.Data.Max()); + } } if (min > 0 && max > 0 && !BarOption.YAxis.Scale) min = 0; diff --git a/SunnyUI/Charts/UIBarChartOption.cs b/SunnyUI/Charts/UIBarChartOption.cs index b5343016..d22bb981 100644 --- a/SunnyUI/Charts/UIBarChartOption.cs +++ b/SunnyUI/Charts/UIBarChartOption.cs @@ -159,15 +159,31 @@ namespace Sunny.UI public event DoFormatter Formatter; - public string GetLabel(double value, int index) + public string GetLabel(double value, int index, UIAxisType axisType = UIAxisType.Value) { - return Formatter != null ? Formatter?.Invoke(value, index) : value.ToString("F" + DecimalCount); + switch (axisType) + { + case UIAxisType.Value: + return Formatter != null ? Formatter?.Invoke(value, index) : value.ToString("F" + DecimalCount); + case UIAxisType.Time: + DateTimeInt64 dt = new DateTimeInt64((long)value); + return Formatter != null ? Formatter?.Invoke(dt, index) : (DateTimeFormat.IsNullOrEmpty() ? dt.ToString() : dt.ToString(DateTimeFormat)); + case UIAxisType.Category: + return Formatter != null ? Formatter?.Invoke(value, index) : value.ToString("F0"); + } + + return value.ToString("F2"); } /// /// 小数位个数,Formatter不为空时以Formatter为准 /// public int DecimalCount { get; set; } = 0; + + /// + /// 日期格式化字符串,Formatter不为空时以Formatter为准 + /// + public string DateTimeFormat { get; set; } = "HH:mm"; } public class UIAxisTick diff --git a/SunnyUI/Charts/UILineChart.cs b/SunnyUI/Charts/UILineChart.cs index dbf6a54a..88103548 100644 --- a/SunnyUI/Charts/UILineChart.cs +++ b/SunnyUI/Charts/UILineChart.cs @@ -1,9 +1,10 @@ -using Sunny.UI.Charts; -using System; +using System; +using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Linq; +using System.Windows.Forms; namespace Sunny.UI { @@ -42,13 +43,13 @@ namespace Sunny.UI foreach (var series in LineOption.Series.Values) { - series.Points.Clear(); + series.ClearPoints(); for (int i = 0; i < series.XData.Count; i++) { - float x = (float)(series.XData[i] - XAxisStart) * 1.0f * DrawSize.Width / (XAxisEnd - XAxisStart); - float y = (float)(series.YData[i] - YAxisStart) * 1.0f * DrawSize.Height / (YAxisEnd - YAxisStart); - series.Points.Add(new PointF(DrawOrigin.X + x, DrawOrigin.Y - y)); + float x = (float)((series.XData[i] - XAxisStart * XAxisInterval) * 1.0f * DrawSize.Width / XAxisInterval / (XAxisEnd - XAxisStart)); + float y = (float)((series.YData[i] - YAxisStart * YAxisInterval) * 1.0f * DrawSize.Height / YAxisInterval / (YAxisEnd - YAxisStart)); + series.AddPoint(new PointF(DrawOrigin.X + x, DrawOrigin.Y - y)); } } @@ -62,8 +63,11 @@ namespace Sunny.UI double max = double.MinValue; foreach (var series in LineOption.Series.Values) { - min = Math.Min(min, series.YData.Min()); - max = Math.Max(max, series.YData.Max()); + if (series.DataCount > 0) + { + min = Math.Min(min, series.YData.Min()); + max = Math.Max(max, series.YData.Max()); + } } if (min > 0 && max > 0 && !LineOption.YAxis.Scale) min = 0; @@ -93,8 +97,8 @@ namespace Sunny.UI max = Math.Max(max, series.XData.Max()); } - if (min > 0 && max > 0 && !LineOption.XAxis.Scale) min = 0; - if (min < 0 && max < 0 && !LineOption.XAxis.Scale) max = 0; + if (min > 0 && max > 0 && !LineOption.XAxis.Scale && LineOption.XAxisType == UIAxisType.Value) min = 0; + if (min < 0 && max < 0 && !LineOption.XAxis.Scale && LineOption.XAxisType == UIAxisType.Value) max = 0; if (!LineOption.XAxis.MaxAuto) max = LineOption.XAxis.Max; if (!LineOption.XAxis.MinAuto) min = LineOption.XAxis.Min; @@ -104,12 +108,23 @@ namespace Sunny.UI min = 0; } - UIChartHelper.CalcDegreeScale(min, max, LineOption.XAxis.SplitNumber, - out int startX, out int endX, out double intervalX); + if (LineOption.XAxisType == UIAxisType.Value || LineOption.XAxisType == UIAxisType.Category) + { + UIChartHelper.CalcDegreeScale(min, max, LineOption.XAxis.SplitNumber, + out int startX, out int endX, out double intervalX); + XAxisStart = startX; + XAxisEnd = endX; + XAxisInterval = intervalX; + } - XAxisStart = startX; - XAxisEnd = endX; - XAxisInterval = intervalX; + if (LineOption.XAxisType == UIAxisType.Time) + { + UIChartHelper.CalcDateTimeDegreeScale(min, max, LineOption.XAxis.SplitNumber, + out int startX, out int endX, out double intervalX); + XAxisStart = startX; + XAxisEnd = endX; + XAxisInterval = intervalX; + } } [Browsable(false)] @@ -131,15 +146,31 @@ namespace Sunny.UI option.Title.Text = "SunnyUI"; option.Title.SubText = "LineChart"; - option.AddSeries(new UILineSeries("Line")); - option.AddData("Line", 0, 1); - option.AddData("Line", 1, 2); - option.AddData("Line", 2, 3); - option.AddData("Line", 3, 4); - option.AddData("Line", 4, 3); - option.AddData("Line", 5, 2); + var series = option.AddSeries(new UILineSeries("Line1")); + series.Add(0, 1.2); + series.Add(1.1, 2.2); + series.Add(2.2, 3.2); + series.Add(3.3, 4.2); + series.Add(4.4, 3.2); + series.Add(5.5, 2.2); + series.Symbol = UILinePointSymbol.Square; + series.SymbolSize = 4; + series.SymbolLineWidth = 1; + series.SymbolColor = Color.Red; - option.XAxis.Name = "日期"; + series = option.AddSeries(new UILineSeries("Line2")); + series.Add(0.3, 3.3); + series.Add(1.3, 2.3); + series.Add(2.3, 2.3); + series.Add(3.3, 1.3); + series.Add(4.3, 2.3); + series.Add(5.3, 4.3); + series.Symbol = UILinePointSymbol.Plus; + series.SymbolSize = 4; + series.SymbolLineWidth = 1; + series.SymbolColor = Color.Red; + + option.XAxis.Name = "数值"; option.YAxis.Name = "数值"; emptyOption = option; @@ -204,7 +235,7 @@ namespace Sunny.UI float wmax = 0; for (int i = XAxisStart; i <= XAxisEnd; i++) { - string label = LineOption.XAxis.AxisLabel.GetLabel(i * XAxisInterval, idx); + string label = LineOption.XAxis.AxisLabel.GetLabel(i * XAxisInterval, idx, LineOption.XAxisType); SizeF sf = g.MeasureString(label, SubFont); wmax = Math.Max(wmax, sf.Width); g.DrawString(label, SubFont, ChartStyle.ForeColor, start - sf.Width / 2.0f, @@ -277,10 +308,80 @@ namespace Sunny.UI Color color = series.Color; if (!series.CustomColor) color = ChartStyle.GetColor(idx); - if (series.Smooth) - g.DrawCurve(color, series.Points.ToArray(), true); - else - g.DrawLines(series.Color, series.Points.ToArray(), true); + using (Pen pen = new Pen(color, series.Width)) + { + g.SetHighQuality(); + if (series.Smooth) + g.DrawCurve(pen, series.Points.ToArray()); + else + g.DrawLines(pen, series.Points.ToArray()); + g.SetDefaultQuality(); + } + + + if (series.Symbol != UILinePointSymbol.None) + { + using (Brush br = new SolidBrush(ChartStyle.BackColor)) + using (Pen pn = new Pen(series.SymbolColor, series.SymbolLineWidth)) + { + foreach (var p in series.Points) + { + switch (series.Symbol) + { + case UILinePointSymbol.Square: + g.FillRectangle(br, p.X - series.SymbolSize, p.Y - series.SymbolSize, series.SymbolSize * 2, series.SymbolSize * 2); + g.DrawRectangle(pn, p.X - series.SymbolSize, p.Y - series.SymbolSize, series.SymbolSize * 2, series.SymbolSize * 2); + break; + case UILinePointSymbol.Diamond: + { + PointF pt1 = new PointF(p.X - series.SymbolSize, p.Y); + PointF pt2 = new PointF(p.X, p.Y - series.SymbolSize); + PointF pt3 = new PointF(p.X + series.SymbolSize, p.Y); + PointF pt4 = new PointF(p.X, p.Y + series.SymbolSize); + PointF[] pts = { pt1, pt2, pt3, pt4, pt1 }; + g.SetHighQuality(); + GraphicsPath path = pts.Path(); + g.FillPath(br, path); + g.DrawPath(pn, path); + path.Dispose(); + } + break; + case UILinePointSymbol.Triangle: + { + PointF pt1 = new PointF(p.X, p.Y - series.SymbolSize); + PointF pt2 = new PointF(p.X - series.SymbolSize * 0.866f, p.Y + series.SymbolSize * 0.5f); + PointF pt3 = new PointF(p.X + series.SymbolSize * 0.866f, p.Y + series.SymbolSize * 0.5f); + PointF[] pts = { pt1, pt2, pt3, pt1 }; + g.SetHighQuality(); + GraphicsPath path = pts.Path(); + g.FillPath(br, path); + g.DrawPath(pn, path); + path.Dispose(); + } + break; + case UILinePointSymbol.Circle: + g.SetHighQuality(); + g.FillEllipse(br, p.X - series.SymbolSize, p.Y - series.SymbolSize, series.SymbolSize * 2, series.SymbolSize * 2); + g.DrawEllipse(pn, p.X - series.SymbolSize, p.Y - series.SymbolSize, series.SymbolSize * 2, series.SymbolSize * 2); + break; + case UILinePointSymbol.Plus: + g.DrawLine(pn, p.X - series.SymbolSize, p.Y, p.X + series.SymbolSize, p.Y); + g.DrawLine(pn, p.X, p.Y - series.SymbolSize, p.X, p.Y + series.SymbolSize); + break; + case UILinePointSymbol.Star: + g.SetHighQuality(); + g.DrawLine(pn, p.X, p.Y - series.SymbolSize, p.X, p.Y + series.SymbolSize); + g.DrawLine(pn, p.X - series.SymbolSize * 0.866f, p.Y + series.SymbolSize * 0.5f, + p.X + series.SymbolSize * 0.866f, p.Y - series.SymbolSize * 0.5f); + g.DrawLine(pn, p.X - series.SymbolSize * 0.866f, p.Y - series.SymbolSize * 0.5f, + p.X + series.SymbolSize * 0.866f, p.Y + series.SymbolSize * 0.5f); + break; + } + } + } + + g.SetDefaultQuality(); + } idx++; } @@ -309,5 +410,67 @@ namespace Sunny.UI g.DrawString(line.Name, SubFont, line.Color, Width - sf.Width - 4 - LineOption.Grid.Right, pos - 2 - sf.Height); } } + + private readonly List selectPoints = new List(); + private readonly List selectPointsTemp = new List(); + + protected override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + if (!NeedDraw) return; + + selectPointsTemp.Clear(); + foreach (var series in LineOption.Series.Values) + { + if (series.GetNearestPoint(e.Location, 4, out double x, out double y, out int index)) + { + UILineSelectPoint point = new UILineSelectPoint(); + point.Name = series.Name; + point.Index = index; + point.X = x; + point.Y = y; + selectPointsTemp.Add(point); + } + } + + bool isNew = false; + if (selectPointsTemp.Count != selectPoints.Count) + { + isNew = true; + } + else + { + Dictionary points = selectPoints.ToDictionary(p => p.Name); + foreach (var point in selectPointsTemp) + { + if (!points.ContainsKey(point.Name)) + { + isNew = true; + break; + } + + if (points[point.Name].Index != point.Index) + { + isNew = true; + break; + } + } + } + + if (isNew) + { + selectPoints.Clear(); + foreach (var point in selectPointsTemp) + { + selectPoints.Add(point); + } + + PointValue?.Invoke(this, selectPoints); + } + } + + public delegate void OnPointValue(object sender, List points); + + public event OnPointValue PointValue; } } diff --git a/SunnyUI/Charts/UILineChartOption.cs b/SunnyUI/Charts/UILineChartOption.cs index acfe7586..4b80d5c5 100644 --- a/SunnyUI/Charts/UILineChartOption.cs +++ b/SunnyUI/Charts/UILineChartOption.cs @@ -3,7 +3,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Drawing; -namespace Sunny.UI.Charts +namespace Sunny.UI { public sealed class UILineOption : UIOption, IDisposable { @@ -29,10 +29,20 @@ namespace Sunny.UI.Charts public readonly List XAxisScaleLines = new List(); public readonly List YAxisScaleLines = new List(); - public void AddSeries(UILineSeries series) + + public UILineSeries AddSeries(UILineSeries series) { - if (series.Name.IsNullOrEmpty()) return; + if (series.Name.IsNullOrEmpty()) return null; Series.TryAdd(series.Name, series); + return series; + } + + public UILineSeries AddSeries(string name) + { + if (name.IsNullOrEmpty()) return null; + UILineSeries series = new UILineSeries(name); + Series.TryAdd(series.Name, series); + return series; } public void AddData(string name, double x, double y) @@ -150,11 +160,13 @@ namespace Sunny.UI.Charts { public string Name { get; private set; } - public float Width { get; set; } = 1; + public float Width { get; set; } = 2; public Color Color { get; set; } public UILinePointSymbol Symbol { get; set; } = UILinePointSymbol.None; - public int SymbolSize { get; set; } = 1; + public int SymbolSize { get; set; } = 4; + + public int SymbolLineWidth { get; set; } = 1; public Color SymbolColor { get; set; } @@ -181,13 +193,47 @@ namespace Sunny.UI.Charts public readonly List Points = new List(); + private readonly List PointsX = new List(); + + private readonly List PointsY = new List(); + public int DataCount => XData.Count; + public bool GetNearestPoint(Point p, int offset, out double x, out double y, out int index) + { + index = PointsX.BinarySearchNearIndex(p.X); + if (p.X >= PointsX[index] - offset && p.X <= PointsX[index] + offset && + p.Y >= PointsY[index] - offset && p.Y <= PointsY[index] + offset) + { + x = XData[index]; + y = YData[index]; + return true; + } + + x = 0; + y = 0; + return false; + } + public void Clear() { XData.Clear(); YData.Clear(); + ClearPoints(); + } + + public void ClearPoints() + { Points.Clear(); + PointsX.Clear(); + PointsY.Clear(); + } + + public void AddPoint(PointF point) + { + Points.Add(point); + PointsX.Add(point.X); + PointsY.Add(point.Y); } public void Add(double x, double y) @@ -221,4 +267,15 @@ namespace Sunny.UI.Charts Plus, Star } + + public struct UILineSelectPoint + { + public string Name { get; set; } + + public int Index { get; set; } + + public double X { get; set; } + + public double Y { get; set; } + } } diff --git a/SunnyUI/Charts/UIOption.cs b/SunnyUI/Charts/UIOption.cs index fe0611c1..e7cb2998 100644 --- a/SunnyUI/Charts/UIOption.cs +++ b/SunnyUI/Charts/UIOption.cs @@ -238,6 +238,67 @@ namespace Sunny.UI degree_end = end2; } + /// + /// 计算刻度 + /// 起始值必须小于结束值 + /// + /// 起始值 + /// 结束值 + /// 期望刻度数量,实际数接近此数 + /// 刻度起始值,须乘以间隔使用 + /// 刻度结束值,须乘以间隔使用 + /// 刻度间隔 + public static void CalcDateTimeDegreeScale(double start, double end, int expect_num, + out int degree_start, out int degree_end, out double degree_gap) + { + if (start >= end) + { + throw new Exception("起始值必须小于结束值"); + } + + double differ = end - start; + double differ_gap = differ / (expect_num - 1); //35, 4.6, 0.27 + + double exponent = Math.Log10(differ_gap) - 1; //0.54, -0.34, -1.57 + int _exponent = (int)exponent; //0, 0=>-1, -1=>-2 + if (exponent < 0 && Math.Abs(exponent) > 1e-8) + { + _exponent--; + } + + int step = (int)(differ_gap / Math.Pow(10, _exponent)); //35, 46, 27 + int[] fix_steps = new int[] { 10, 20, 30, 60, 120 }; + int fix_step = 10; //25, 50, 25 + for (int i = fix_steps.Length - 1; i >= 1; i--) + { + if (step > (fix_steps[i] + fix_steps[i - 1]) / 2) + { + fix_step = fix_steps[i]; + break; + } + } + + degree_gap = fix_step * Math.Pow(10, _exponent); //25, 5, 0.25 + + double start1 = start / degree_gap; + int start2 = (int)start1; + if (start1 < 0 && Math.Abs(start1 - start2) > 1e-8) + { + start2--; + } + + degree_start = start2; + + double end1 = end / degree_gap; + int end2 = (int)end1; + if (end1 >= 0 && Math.Abs(end1 - end2) > 1e-8) + { + end2++; + } + + degree_end = end2; + } + /// /// 计算刻度 /// 起始值必须小于结束值