diff --git a/Bin/net40/SunnyUI.Demo.exe b/Bin/net40/SunnyUI.Demo.exe index cbb808ec..4a5b4d78 100644 Binary files a/Bin/net40/SunnyUI.Demo.exe and b/Bin/net40/SunnyUI.Demo.exe differ diff --git a/Bin/net40/SunnyUI.dll b/Bin/net40/SunnyUI.dll index c602cd94..2dbbb230 100644 Binary files a/Bin/net40/SunnyUI.dll and b/Bin/net40/SunnyUI.dll differ diff --git a/SunnyUI.Demo/Charts/FLineChart.Designer.cs b/SunnyUI.Demo/Charts/FLineChart.Designer.cs index a138301b..a21e756b 100644 --- a/SunnyUI.Demo/Charts/FLineChart.Designer.cs +++ b/SunnyUI.Demo/Charts/FLineChart.Designer.cs @@ -37,6 +37,7 @@ this.uiSymbolButton2 = new Sunny.UI.UISymbolButton(); this.timer1 = new System.Windows.Forms.Timer(this.components); this.uiCheckBox1 = new Sunny.UI.UICheckBox(); + this.uiSymbolButton3 = new Sunny.UI.UISymbolButton(); ((System.ComponentModel.ISupportInitialize)(this.uiImageButton3)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.uiImageButton2)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.uiImageButton1)).BeginInit(); @@ -103,10 +104,12 @@ 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.LegendFont = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); this.LineChart.Location = new System.Drawing.Point(30, 55); this.LineChart.MinimumSize = new System.Drawing.Size(1, 1); this.LineChart.Name = "LineChart"; this.LineChart.Size = new System.Drawing.Size(670, 430); + this.LineChart.SubFont = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); this.LineChart.TabIndex = 35; this.LineChart.Text = "uiLineChart1"; this.LineChart.PointValue += new Sunny.UI.UILineChart.OnPointValue(this.LineChart_PointValue); @@ -142,11 +145,27 @@ this.uiCheckBox1.Text = "显示点"; this.uiCheckBox1.CheckedChanged += new System.EventHandler(this.uiCheckBox1_CheckedChanged); // + // uiSymbolButton3 + // + this.uiSymbolButton3.Cursor = System.Windows.Forms.Cursors.Hand; + this.uiSymbolButton3.Font = new System.Drawing.Font("微软雅黑", 12F); + this.uiSymbolButton3.Location = new System.Drawing.Point(665, 505); + this.uiSymbolButton3.MinimumSize = new System.Drawing.Size(1, 1); + this.uiSymbolButton3.Name = "uiSymbolButton3"; + this.uiSymbolButton3.Padding = new System.Windows.Forms.Padding(28, 0, 0, 0); + this.uiSymbolButton3.Size = new System.Drawing.Size(100, 27); + this.uiSymbolButton3.Symbol = 61952; + this.uiSymbolButton3.TabIndex = 38; + this.uiSymbolButton3.Text = "数据"; + this.uiSymbolButton3.Visible = false; + this.uiSymbolButton3.Click += new System.EventHandler(this.uiSymbolButton3_Click); + // // FLineChart // this.AllowShowTitle = true; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; this.ClientSize = new System.Drawing.Size(800, 574); + this.Controls.Add(this.uiSymbolButton3); this.Controls.Add(this.uiCheckBox1); this.Controls.Add(this.uiSymbolButton2); this.Controls.Add(this.uiSymbolButton1); @@ -176,5 +195,6 @@ private UISymbolButton uiSymbolButton2; private System.Windows.Forms.Timer timer1; private UICheckBox uiCheckBox1; + private UISymbolButton uiSymbolButton3; } } \ No newline at end of file diff --git a/SunnyUI.Demo/Charts/FLineChart.cs b/SunnyUI.Demo/Charts/FLineChart.cs index df7d42bb..a699141e 100644 --- a/SunnyUI.Demo/Charts/FLineChart.cs +++ b/SunnyUI.Demo/Charts/FLineChart.cs @@ -144,5 +144,38 @@ namespace Sunny.UI.Demo LineChart.Refresh(); } + + private void uiSymbolButton3_Click(object sender, EventArgs e) + { + timer1.Stop(); + + UILineOption option = new UILineOption(); + option.ToolTip.Visible = true; + option.Title = new UITitle(); + option.Title.Text = "SunnyUI"; + option.Title.SubText = "LineChart"; + + var series = option.AddSeries(new UILineSeries("Line1")); + series.Add(0, 1.2); + series.Add(1, 2.2); + series.Add(2, double.NaN); + series.Add(3, 4.2); + series.Add(4, 3.2); + series.Add(5, 2.2); + series.Symbol = UILinePointSymbol.Square; + series.SymbolSize = 4; + series.SymbolLineWidth = 2; + series.SymbolColor = Color.Red; + series.ContainsNan = true; + + option.XAxis.Name = "日期"; + option.YAxis.Name = "数值"; + option.XAxis.AxisLabel.DateTimeFormat = "yyyy-MM-dd HH:mm"; + option.XAxis.AxisLabel.AutoFormat = false; + option.YAxis.AxisLabel.DecimalCount = 1; + option.YAxis.AxisLabel.AutoFormat = false; + + LineChart.SetOption(option); + } } } diff --git a/SunnyUI/Charts/UILineChart.cs b/SunnyUI/Charts/UILineChart.cs index 7abab2ba..bb376f6e 100644 --- a/SunnyUI/Charts/UILineChart.cs +++ b/SunnyUI/Charts/UILineChart.cs @@ -48,6 +48,7 @@ namespace Sunny.UI protected Point DrawOrigin; protected Size DrawSize; + private Rectangle DrawRect; protected override void CalcData() { @@ -57,6 +58,8 @@ namespace Sunny.UI DrawOrigin = new Point(Option.Grid.Left, Height - Option.Grid.Bottom); DrawSize = new Size(Width - Option.Grid.Left - Option.Grid.Right, Height - Option.Grid.Top - Option.Grid.Bottom); + DrawRect = new Rectangle(Option.Grid.Left, Option.Grid.Top, DrawSize.Width, DrawSize.Height); + if (DrawSize.Width <= 0 || DrawSize.Height <= 0) return; CalcAxises(); @@ -307,15 +310,32 @@ namespace Sunny.UI return; } + if (series.Points.Count == 2) + { + g.DrawTwoPoints(color, series.Points[0], series.Points[1], DrawRect); + return; + } + if (series.ShowLine || series.Symbol == UILinePointSymbol.None) { using (Pen pen = new Pen(color, series.Width)) { g.SetHighQuality(); - if (series.Smooth) - g.DrawCurve(pen, series.Points.ToArray()); + if (series.ContainsNan) + { + for (int i = 0; i < series.Points.Count - 1; i++) + { + g.DrawTwoPoints(pen, series.Points[i], series.Points[i + 1], DrawRect); + } + } else - g.DrawLines(pen, series.Points.ToArray()); + { + if (series.Smooth) + g.DrawCurve(pen, series.Points.ToArray()); + else + g.DrawLines(pen, series.Points.ToArray()); + } + g.SetDefaultQuality(); } } @@ -444,6 +464,7 @@ namespace Sunny.UI { if (p.X <= Option.Grid.Left || p.X >= Width - Option.Grid.Right) continue; if (p.Y <= Option.Grid.Top || p.Y >= Height - Option.Grid.Bottom) continue; + if (double.IsNaN(p.X) || double.IsNaN(p.Y)) continue; switch (series.Symbol) { diff --git a/SunnyUI/Charts/UILineChartOption.cs b/SunnyUI/Charts/UILineChartOption.cs index c55c1c9a..d7202873 100644 --- a/SunnyUI/Charts/UILineChartOption.cs +++ b/SunnyUI/Charts/UILineChartOption.cs @@ -207,8 +207,20 @@ namespace Sunny.UI { if (series.DataCount > 0) { - min = Math.Min(min, series.YData.Min()); - max = Math.Max(max, series.YData.Max()); + if (series.ContainsNan) + { + foreach (var d in series.YData) + { + if (d.IsNan() || d.IsInfinity()) continue; + min = Math.Min(min, d); + max = Math.Max(max, d); + } + } + else + { + min = Math.Min(min, series.YData.Min()); + max = Math.Max(max, series.YData.Max()); + } } } } @@ -225,12 +237,25 @@ namespace Sunny.UI { min = double.MaxValue; max = double.MinValue; + foreach (var series in Series.Values) { if (series.DataCount > 0) { - min = Math.Min(min, series.XData.Min()); - max = Math.Max(max, series.XData.Max()); + if (series.ContainsNan) + { + foreach (var d in series.XData) + { + if (d.IsNan() || d.IsInfinity()) continue; + min = Math.Min(min, d); + max = Math.Max(max, d); + } + } + else + { + min = Math.Min(min, series.XData.Min()); + max = Math.Max(max, series.XData.Max()); + } } } } @@ -263,6 +288,8 @@ namespace Sunny.UI public bool ShowLine { get; set; } = true; + public bool ContainsNan { get; set; } + public UILineSeries(string name) { Name = name; @@ -359,6 +386,7 @@ namespace Sunny.UI public void Add(double x, double y) { XData.Add(x); + if (double.IsInfinity(y)) y = double.NaN; YData.Add(y); } @@ -366,6 +394,7 @@ namespace Sunny.UI { DateTimeInt64 t = new DateTimeInt64(x); XData.Add(t); + if (double.IsInfinity(y)) y = double.NaN; YData.Add(y); } @@ -373,6 +402,7 @@ namespace Sunny.UI { int cnt = XData.Count; XData.Add(cnt); + if (double.IsInfinity(y)) y = double.NaN; YData.Add(y); } } diff --git a/SunnyUI/Charts/UIScale.cs b/SunnyUI/Charts/UIScale.cs index bd612fb6..241c599f 100644 --- a/SunnyUI/Charts/UIScale.cs +++ b/SunnyUI/Charts/UIScale.cs @@ -31,13 +31,9 @@ namespace Sunny.UI protected bool _minAuto = true; protected bool _maxAuto = true; - protected double _minGrace = 0.1; - protected double _maxGrace = 0.1; protected double _min, _max; - protected bool _formatAuto = true; protected string _format; - protected bool _magAuto = true; protected int _mag; protected static double TargetSteps = 7.0; @@ -76,7 +72,10 @@ namespace Sunny.UI float[] result = new float[labels.Length]; for (int i = 0; i < labels.Length; i++) { - result[i] = CalcXPixel(labels[i], origin, width); + if (labels[i].IsInfinity() || labels[i].IsNan()) + result[i] = float.NaN; + else + result[i] = CalcXPixel(labels[i], origin, width); } return result; @@ -88,7 +87,10 @@ namespace Sunny.UI float[] result = new float[labels.Length]; for (int i = 0; i < labels.Length; i++) { - result[i] = CalcYPixel(labels[i], origin, height); + if (labels[i].IsInfinity() || labels[i].IsNan()) + result[i] = float.NaN; + else + result[i] = CalcYPixel(labels[i], origin, height); } return result; @@ -96,38 +98,22 @@ namespace Sunny.UI public int Mag { - get { return _mag; } - set { _mag = value; _magAuto = false; } + get => _mag; + set { _mag = value; MagAuto = false; } } - public bool MagAuto - { - get { return _magAuto; } - set { _magAuto = value; } - } + public bool MagAuto { get; set; } = true; - public double MinGrace - { - get { return _minGrace; } - set { _minGrace = value; } - } + public double MinGrace { get; set; } = 0.1; - public double MaxGrace - { - get { return _maxGrace; } - set { _maxGrace = value; } - } + public double MaxGrace { get; set; } = 0.1; - public bool FormatAuto - { - get { return _formatAuto; } - set { _formatAuto = value; } - } + public bool FormatAuto { get; set; } = true; public string Format { - get { return _format; } - set { _format = value; _formatAuto = false; } + get => _format; + set { _format = value; FormatAuto = false; } } public virtual double Min @@ -171,14 +157,14 @@ namespace Sunny.UI if (_minAuto) { _min = minVal; - if (_min < 0 || minVal - _minGrace * range >= 0.0) - _min = minVal - _minGrace * range; + if (_min < 0 || minVal - MinGrace * range >= 0.0) + _min = minVal - MinGrace * range; } if (_maxAuto) { _max = maxVal; - if (_max > 0 || maxVal + _maxGrace * range <= 0.0) - _max = maxVal + _maxGrace * range; + if (_max > 0 || maxVal + MaxGrace * range <= 0.0) + _max = maxVal + MaxGrace * range; } if (_max.Equals(_min) && _maxAuto && _minAuto) @@ -247,7 +233,7 @@ namespace Sunny.UI private void SetScaleMag() { - if (_magAuto) + if (MagAuto) { double minMag = Math.Floor(Math.Log10(Math.Abs(_min))); double maxMag = Math.Floor(Math.Log10(Math.Abs(_max))); @@ -256,7 +242,7 @@ namespace Sunny.UI _mag = (int)(Math.Floor(mag / 3.0) * 3.0); } - if (_formatAuto) + if (FormatAuto) { int numDec = 0 - (int)(Math.Floor(Math.Log10(Step)) - _mag); if (numDec < 0) numDec = 0; @@ -433,37 +419,37 @@ namespace Sunny.UI if (span.TotalDays > 1825) // 5 years { _scaleLevel = UIDateScaleLevel.Year; - if (_formatAuto) _format = "yyyy"; + if (FormatAuto) _format = "yyyy"; tempStep = Math.Ceiling(tempStep / 365.0); } else if (span.TotalDays > 730) // 2 years { _scaleLevel = UIDateScaleLevel.Year; - if (_formatAuto) _format = "yyyy-MM"; + if (FormatAuto) _format = "yyyy-MM"; tempStep = Math.Ceiling(tempStep / 365.0); } else if (span.TotalDays > 300) // 10 months { _scaleLevel = UIDateScaleLevel.Month; - if (_formatAuto) _format = "yyyy-MM"; + if (FormatAuto) _format = "yyyy-MM"; tempStep = Math.Ceiling(tempStep / 30.0); } else if (span.TotalDays > 10) // 10 days { _scaleLevel = UIDateScaleLevel.Day; - if (_formatAuto) _format = "yyyy-MM-dd"; + if (FormatAuto) _format = "yyyy-MM-dd"; tempStep = Math.Ceiling(tempStep); } else if (span.TotalDays > 3) // 3 days { _scaleLevel = UIDateScaleLevel.Day; - if (_formatAuto) _format = "yyyy-MM-dd HH:mm"; + if (FormatAuto) _format = "yyyy-MM-dd HH:mm"; tempStep = Math.Ceiling(tempStep); } else if (span.TotalHours > 10) // 10 hours { _scaleLevel = UIDateScaleLevel.Hour; - if (_formatAuto) _format = "HH:mm"; + if (FormatAuto) _format = "HH:mm"; tempStep = Math.Ceiling(tempStep * 24.0); if (tempStep > 12.0) tempStep = 24.0; @@ -475,13 +461,13 @@ namespace Sunny.UI else if (span.TotalHours > 3) // 3 hours { _scaleLevel = UIDateScaleLevel.Hour; - if (_formatAuto) _format = "HH:mm"; + if (FormatAuto) _format = "HH:mm"; tempStep = Math.Ceiling(tempStep * 24.0); } else if (span.TotalMinutes > 10) // 10 Minutes { _scaleLevel = UIDateScaleLevel.Minute; - if (_formatAuto) _format = "HH:mm"; + if (FormatAuto) _format = "HH:mm"; tempStep = Math.Ceiling(tempStep * 1440.0); // make sure the minute step size is 1, 5, 15, or 30 minutes if (tempStep > 15.0) tempStep = 30.0; @@ -492,13 +478,13 @@ namespace Sunny.UI else if (span.TotalMinutes > 3) // 3 Minutes { _scaleLevel = UIDateScaleLevel.Minute; - if (_formatAuto) _format = "mm:ss"; + if (FormatAuto) _format = "mm:ss"; tempStep = Math.Ceiling(tempStep * 1440.0); } else if (span.TotalSeconds > 3) // 3 Seconds { _scaleLevel = UIDateScaleLevel.Second; - if (_formatAuto) _format = "mm:ss"; + if (FormatAuto) _format = "mm:ss"; tempStep = Math.Ceiling(tempStep * 86400.0); // make sure the second step size is 1, 5, 15, or 30 seconds @@ -510,7 +496,7 @@ namespace Sunny.UI else { _scaleLevel = UIDateScaleLevel.Millisecond; - if (_formatAuto) _format = "ss.fff"; + if (FormatAuto) _format = "ss.fff"; tempStep = CalcStepSize(span.TotalMilliseconds, targetSteps); } diff --git a/SunnyUI/Common/UGraphics.cs b/SunnyUI/Common/UGraphics.cs index 60eeab9f..da7627e1 100644 --- a/SunnyUI/Common/UGraphics.cs +++ b/SunnyUI/Common/UGraphics.cs @@ -934,6 +934,7 @@ namespace Sunny.UI public static void DrawTwoPoints(this Graphics g, Pen pen, PointF pf1, PointF pf2, Rectangle rect, bool smooth = true) { + if (pf1.X.IsNan() || pf1.Y.IsNan() || pf2.X.IsNan() || pf2.Y.IsNan()) return; bool haveLargePixel = Math.Abs(pf1.X - pf2.X) >= rect.Width * 100 || Math.Abs(pf1.Y - pf2.Y) >= rect.Height * 100; //两点都在区域内 diff --git a/SunnyUI/Common/UOther.cs b/SunnyUI/Common/UOther.cs new file mode 100644 index 00000000..4f4846d6 --- /dev/null +++ b/SunnyUI/Common/UOther.cs @@ -0,0 +1,25 @@ +namespace Sunny.UI +{ + public static class UIOther + { + public static bool IsNan(this double d) + { + return double.IsNaN(d); + } + + public static bool IsNan(this float d) + { + return float.IsNaN(d); + } + + public static bool IsInfinity(this double d) + { + return double.IsInfinity(d); + } + + public static bool IsInfinity(this float d) + { + return float.IsInfinity(d); + } + } +}