You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
681 lines
23 KiB
681 lines
23 KiB
using System;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
using System.Drawing;
|
|
using System.Drawing.Drawing2D;
|
|
using System.Linq;
|
|
|
|
namespace SSControls
|
|
{
|
|
public partial class SSChart : UserControl
|
|
{
|
|
private Timer RedrawTimer;
|
|
private Bitmap _backbuffer;
|
|
public bool redrawactive;
|
|
|
|
protected override void OnSizeChanged(EventArgs e)
|
|
{
|
|
if (_backbuffer != null)
|
|
{
|
|
_backbuffer.Dispose();
|
|
_backbuffer = null;
|
|
}
|
|
if (!_scaleonresize)
|
|
{
|
|
Page.Width = this.Width;
|
|
Page.Height = this.Height;
|
|
}
|
|
if (RedrawTimer == null)
|
|
{
|
|
RedrawTimer = new Timer();
|
|
RedrawTimer.Tick += RedrawTimer_Tick;
|
|
RedrawTimer.Interval = 100;
|
|
RedrawTimer.Start();
|
|
}
|
|
Invalidate();
|
|
base.OnSizeChanged(e);
|
|
}
|
|
|
|
protected override void OnPaint(PaintEventArgs e)
|
|
{
|
|
if (_backbuffer != null)
|
|
{
|
|
e.Graphics.DrawImageUnscaled(_backbuffer, 0, 0);
|
|
}
|
|
}
|
|
|
|
|
|
protected override void OnPaintBackground(PaintEventArgs e)
|
|
{
|
|
//MyBase.OnPaintBackground(e)
|
|
}
|
|
|
|
public class GMargins
|
|
{
|
|
private float _left = 50;
|
|
private float _right = 35;
|
|
private float _top = 30;
|
|
private float _bottom = 30;
|
|
|
|
public event ChangedEventHandler Changed;
|
|
public delegate void ChangedEventHandler();
|
|
|
|
public float Left
|
|
{
|
|
get { return _left; }
|
|
set
|
|
{
|
|
if (_left != value)
|
|
{
|
|
_left = value;
|
|
Changed?.Invoke();
|
|
}
|
|
}
|
|
}
|
|
|
|
public float Right
|
|
{
|
|
get { return _right; }
|
|
set
|
|
{
|
|
if (_right != value)
|
|
{
|
|
_right = value;
|
|
Changed?.Invoke();
|
|
}
|
|
}
|
|
}
|
|
|
|
public float Top
|
|
{
|
|
get { return _top; }
|
|
set
|
|
{
|
|
if (_top != value)
|
|
{
|
|
_top = value;
|
|
Changed?.Invoke();
|
|
}
|
|
}
|
|
}
|
|
|
|
public float Bottom
|
|
{
|
|
get { return _bottom; }
|
|
set
|
|
{
|
|
if (_bottom != value)
|
|
{
|
|
_bottom = value;
|
|
Changed?.Invoke();
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
public class GPage
|
|
{
|
|
private float _width = 400;
|
|
private float _height = 300;
|
|
public event ChangedEventHandler Changed;
|
|
public delegate void ChangedEventHandler();
|
|
public GMargins Margins = new GMargins();
|
|
|
|
public GPage()
|
|
{
|
|
Margins.Changed += Margins_Changed;
|
|
}
|
|
|
|
public float Width
|
|
{
|
|
get { return _width; }
|
|
set
|
|
{
|
|
if (_width != value)
|
|
{
|
|
if (value > Margins.Left + Margins.Right)
|
|
{
|
|
_width = value;
|
|
Changed?.Invoke();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public float Height
|
|
{
|
|
get { return _height; }
|
|
set
|
|
{
|
|
if (_height != value)
|
|
{
|
|
if (value > Margins.Top + Margins.Bottom)
|
|
{
|
|
_height = value;
|
|
Changed?.Invoke();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public float DrawWidth
|
|
{
|
|
get { return _width - Margins.Left - Margins.Right; }
|
|
}
|
|
|
|
public float DrawHeight
|
|
{
|
|
get { return _height - Margins.Top - Margins.Bottom; }
|
|
}
|
|
|
|
private void Margins_Changed()
|
|
{
|
|
Changed?.Invoke();
|
|
}
|
|
|
|
}
|
|
|
|
public class GScale
|
|
{
|
|
public enum GScaleTypes : byte
|
|
{
|
|
HorBottom = 0,
|
|
HorTop = 1,
|
|
VerLeft = 2,
|
|
VerRight = 3
|
|
}
|
|
|
|
|
|
private GScaleTypes _scaletype = GScaleTypes.HorBottom;
|
|
public GScaleTypes ScaleType
|
|
{
|
|
get { return _scaletype; }
|
|
set
|
|
{
|
|
switch (value)
|
|
{
|
|
case GScaleTypes.HorTop:
|
|
ValSFormat.Alignment = StringAlignment.Center;
|
|
ValSFormat.LineAlignment = StringAlignment.Far;
|
|
break;
|
|
case GScaleTypes.VerLeft:
|
|
ValSFormat.Alignment = StringAlignment.Far;
|
|
ValSFormat.LineAlignment = StringAlignment.Center;
|
|
break;
|
|
case GScaleTypes.VerRight:
|
|
ValSFormat.Alignment = StringAlignment.Near;
|
|
ValSFormat.LineAlignment = StringAlignment.Center;
|
|
break;
|
|
default:
|
|
ValSFormat.Alignment = StringAlignment.Center;
|
|
ValSFormat.LineAlignment = StringAlignment.Near;
|
|
break;
|
|
}
|
|
_scaletype = value;
|
|
}
|
|
}
|
|
|
|
public bool PaintOver = false;
|
|
public float ValFrom = 0;
|
|
public float ValTo = 100;
|
|
public float ValMinStep = 5;
|
|
public float ValMajStep = 10;
|
|
public float ValMinSize = 2;
|
|
public float ValMajSize = 4;
|
|
public float sX = 0;
|
|
public float sY = 0;
|
|
public Pen Pen = new Pen(Brushes.White);
|
|
public bool Visible = false;
|
|
public Font ValFont = (Font)SystemFonts.SmallCaptionFont.Clone();
|
|
public string ValFormat = "0.00";
|
|
public bool ValVisible = true;
|
|
public StringFormat ValSFormat = new StringFormat();
|
|
public float ValOffset = 0;
|
|
public Brush ValBrush = new SolidBrush(Color.Aquamarine);
|
|
public bool ShowMinLines = true;
|
|
public Pen MinLinesPen = new Pen(new SolidBrush(Color.FromArgb(0x60606060)));
|
|
public bool ShowMajLines = true;
|
|
public Pen MajLinesPen = new Pen(new SolidBrush(Color.FromArgb(unchecked((int)0x90909090))));
|
|
|
|
public GScale()
|
|
{
|
|
var _with1 = ValSFormat;
|
|
_with1.Alignment = StringAlignment.Center;
|
|
_with1.LineAlignment = StringAlignment.Near;
|
|
Pen.Width = (float)0.000001;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (Pen != null) Pen.Dispose();
|
|
if (ValFont != null) ValFont.Dispose();
|
|
if (ValSFormat != null) ValSFormat.Dispose();
|
|
if (ValBrush != null) ValBrush.Dispose();
|
|
if (MinLinesPen != null) MinLinesPen.Dispose();
|
|
if (MajLinesPen != null) MajLinesPen.Dispose();
|
|
Pen = null;
|
|
ValFont = null;
|
|
ValSFormat = null;
|
|
ValBrush = null;
|
|
MinLinesPen = null;
|
|
MajLinesPen = null;
|
|
}
|
|
|
|
public void Draw(ref Graphics g, ref GPage p)
|
|
{
|
|
float s = 0;
|
|
float s1 = 0;
|
|
float s2 = 0;
|
|
float eX = 0;
|
|
float eY = 0;
|
|
float majXOff = 0;
|
|
float majYoff = 0;
|
|
float minXoff = 0;
|
|
float minYoff = 0;
|
|
float vXoff = 0;
|
|
float vYoff = 0;
|
|
if (Visible)
|
|
{
|
|
switch (_scaletype)
|
|
{
|
|
case GScaleTypes.HorBottom:
|
|
eX = sX + p.DrawWidth;
|
|
eY = sY;
|
|
majXOff = 0;
|
|
majYoff = ValMajSize;
|
|
minXoff = 0;
|
|
minYoff = ValMinSize;
|
|
vXoff = 0;
|
|
vYoff = ValOffset + ValMajSize;
|
|
break;
|
|
case GScaleTypes.HorTop:
|
|
eX = sX + p.DrawWidth;
|
|
eY = sY;
|
|
majXOff = 0;
|
|
majYoff = -ValMajSize;
|
|
minXoff = 0;
|
|
minYoff = -ValMinSize;
|
|
vXoff = 0;
|
|
vYoff = -ValOffset - ValMajSize;
|
|
break;
|
|
case GScaleTypes.VerLeft:
|
|
eX = sX;
|
|
eY = sY - p.DrawHeight;
|
|
majXOff = -ValMajSize;
|
|
majYoff = 0;
|
|
minXoff = -ValMinSize;
|
|
minYoff = 0;
|
|
vXoff = -ValOffset - ValMajSize;
|
|
vYoff = 0;
|
|
break;
|
|
case GScaleTypes.VerRight:
|
|
eX = sX;
|
|
eY = sY - p.DrawHeight;
|
|
majXOff = ValMajSize;
|
|
majYoff = 0;
|
|
minXoff = ValMinSize;
|
|
minYoff = 0;
|
|
vXoff = ValOffset + ValMajSize;
|
|
vYoff = 0;
|
|
break;
|
|
}
|
|
if (ValFrom != ValTo)
|
|
{
|
|
g.DrawLine(Pen, sX, sY, eX, eY);
|
|
if (ValMinStep > 0)
|
|
{
|
|
for (s = ValFrom; s <= ValTo; s += ValMinStep)
|
|
{
|
|
s1 = sX + (((s - ValFrom) / (ValTo - ValFrom))) * (eX - sX);
|
|
s2 = sY + (((s - ValFrom) / (ValTo - ValFrom))) * (eY - sY);
|
|
g.DrawLine(Pen, s1, s2, s1 + minXoff, s2 + minYoff);
|
|
if (ShowMinLines)
|
|
{
|
|
switch (_scaletype)
|
|
{
|
|
case GScaleTypes.VerRight:
|
|
case GScaleTypes.VerLeft:
|
|
g.DrawLine(MinLinesPen, 0, s2, p.DrawWidth, s2);
|
|
break;
|
|
default:
|
|
g.DrawLine(MinLinesPen, s1, 0, s1, -p.DrawHeight);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (ValMajStep > 0)
|
|
{
|
|
for (s = ValFrom; s <= ValTo; s += ValMajStep)
|
|
{
|
|
s1 = sX + (((s - ValFrom) / (ValTo - ValFrom))) * (eX - sX);
|
|
s2 = sY + (((s - ValFrom) / (ValTo - ValFrom))) * (eY - sY);
|
|
g.DrawLine(Pen, s1, s2, s1 + majXOff, s2 + majYoff);
|
|
if (ShowMajLines)
|
|
{
|
|
switch (_scaletype)
|
|
{
|
|
case GScaleTypes.VerRight:
|
|
case GScaleTypes.VerLeft:
|
|
g.DrawLine(MajLinesPen, 0, s2, p.DrawWidth, s2);
|
|
break;
|
|
default:
|
|
g.DrawLine(MajLinesPen, s1, 0, s1, -p.DrawHeight);
|
|
break;
|
|
}
|
|
}
|
|
if (ValVisible)
|
|
{
|
|
g.DrawString(s.ToString(ValFormat), ValFont, ValBrush, s1 + vXoff, s2 + vYoff, ValSFormat);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public class GDrawing
|
|
{
|
|
public bool Visible = true;
|
|
public Boolean ShowPoints = false;
|
|
public Pen Pen = new Pen(Brushes.Red);
|
|
public GScale XScale;
|
|
public GScale Yscale;
|
|
public PointF[] Points;
|
|
public long PointsNum
|
|
{
|
|
get
|
|
{
|
|
if (Points == null)
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
return Points.Length;
|
|
}
|
|
catch
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
set
|
|
{
|
|
long oldnum = 0;
|
|
long i = 0;
|
|
oldnum = PointsNum;
|
|
if (oldnum != value)
|
|
{
|
|
if (value > 0)
|
|
{
|
|
Array.Resize(ref Points, (int)value);
|
|
for (i = oldnum; i < value; i++)
|
|
{
|
|
Points[i] = new PointF();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Points = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public void Draw(ref Graphics g, ref GPage p)
|
|
{
|
|
Matrix m = null;
|
|
float[] mtx = null;
|
|
m = g.Transform.Clone();
|
|
|
|
g.ScaleTransform(p.DrawWidth / (XScale.ValTo - XScale.ValFrom), -p.DrawHeight / (Yscale.ValTo - Yscale.ValFrom));
|
|
g.TranslateTransform(XScale.ValFrom, -Yscale.ValFrom);
|
|
|
|
Pen.ResetTransform();
|
|
mtx = g.Transform.Elements;
|
|
Pen.ScaleTransform(1 / mtx[0], 1 / mtx[3]);
|
|
Pen.TranslateTransform(0, 0);
|
|
if (Points != null) {
|
|
g.DrawLines(Pen, Points);
|
|
if (ShowPoints) {
|
|
var c = Pen.Color;
|
|
Pen.Color = Color.FromArgb(255 - Pen.Color.R, 255 - Pen.Color.G, 255 - Pen.Color.B);
|
|
g.DrawRectangles(Pen, Points.Select(x => new RectangleF(x.X, x.Y, (float)0.01,1)).ToArray());
|
|
Pen.Color = c;
|
|
}
|
|
}
|
|
g.Transform.Dispose();
|
|
g.Transform = m;
|
|
}
|
|
|
|
~GDrawing()
|
|
{
|
|
Pen.Dispose();
|
|
}
|
|
|
|
}
|
|
|
|
public GPage Page = new GPage();
|
|
private bool _scaleonresize = false;
|
|
private GScale[] _scales;
|
|
private GDrawing[] _drawings;
|
|
|
|
public bool ScaleOnResize
|
|
{
|
|
get { return _scaleonresize; }
|
|
set { _scaleonresize = value; }
|
|
}
|
|
|
|
|
|
public GScale Scales(int index)
|
|
{
|
|
return _scales[index];
|
|
}
|
|
|
|
public GDrawing Drawings(int index)
|
|
{
|
|
return _drawings[index];
|
|
}
|
|
|
|
public GDrawing[] Drawings1
|
|
{
|
|
get { return _drawings; }
|
|
}
|
|
|
|
public long ScaleNum
|
|
{
|
|
get
|
|
{
|
|
if (_scales == null)
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
return _scales.Length;
|
|
}
|
|
catch
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
set
|
|
{
|
|
long i = 0;
|
|
long oldnum = 0;
|
|
oldnum = ScaleNum;
|
|
if (oldnum != value)
|
|
{
|
|
if (value > 0)
|
|
{
|
|
for (i = value; i <= oldnum - 1; i++)
|
|
{
|
|
_scales[i] = null;
|
|
}
|
|
Array.Resize(ref _scales, (int)value);
|
|
for (i = oldnum; i <= value - 1; i++)
|
|
{
|
|
_scales[i] = new GScale();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i <= ScaleNum - 1; i++)
|
|
{
|
|
_scales[i] = null;
|
|
}
|
|
_scales = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public long DrawingsNum
|
|
{
|
|
get
|
|
{
|
|
if (_drawings == null)
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
return _drawings.Length;
|
|
}
|
|
catch
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
set
|
|
{
|
|
long i = 0;
|
|
long oldnum = 0;
|
|
oldnum = DrawingsNum;
|
|
if (oldnum != value)
|
|
{
|
|
if (value != 0)
|
|
{
|
|
for (i = value; i <= oldnum - 1; i++)
|
|
{
|
|
_drawings[i] = null;
|
|
}
|
|
Array.Resize(ref _drawings, (int)value);
|
|
for (i = oldnum; i <= value - 1; i++)
|
|
{
|
|
_drawings[i] = new GDrawing();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i <= oldnum - 1; i++)
|
|
{
|
|
_drawings[i] = null;
|
|
}
|
|
_drawings = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void _Redraw()
|
|
{
|
|
long i = 0;
|
|
redrawactive = true;
|
|
_backbuffer = null;
|
|
if (_backbuffer == null)
|
|
{
|
|
_backbuffer = new Bitmap(Math.Max(this.ClientSize.Width, 2), Math.Max(this.ClientSize.Height, 2));
|
|
Graphics g = Graphics.FromImage(_backbuffer);
|
|
|
|
g.ScaleTransform(Math.Max(this.Width, 2) / Page.Width, Math.Max(this.Height, 2) / Page.Height);
|
|
g.TranslateTransform(Page.Margins.Left, Page.Height - Page.Margins.Bottom);
|
|
g.Clear(BackColor);
|
|
|
|
g.SmoothingMode = SmoothingMode.HighSpeed;
|
|
for (i = 0; i <= ScaleNum - 1; i++)
|
|
{
|
|
if(!_scales[i].PaintOver) _scales[i].Draw(ref g, ref Page);
|
|
}
|
|
|
|
g.SmoothingMode = SmoothingMode.HighQuality;
|
|
foreach(var d in _drawings)
|
|
{
|
|
if (d.Visible) d.Draw(ref g, ref Page);
|
|
}
|
|
|
|
g.SmoothingMode = SmoothingMode.HighSpeed;
|
|
for (i = 0; i <= ScaleNum - 1; i++)
|
|
{
|
|
if (_scales[i].PaintOver) _scales[i].Draw(ref g, ref Page);
|
|
}
|
|
|
|
g.Dispose();
|
|
if (!this.IsDisposed)
|
|
{
|
|
g = this.CreateGraphics();
|
|
g.DrawImageUnscaled(_backbuffer, 0, 0);
|
|
g.Dispose();
|
|
}
|
|
}
|
|
Application.DoEvents();
|
|
redrawactive = false;
|
|
}
|
|
|
|
public SSChart()
|
|
{
|
|
long i = 0;
|
|
// This call is required by the designer.
|
|
InitializeComponent();
|
|
|
|
// Add any initialization after the InitializeComponent() call.
|
|
ScaleNum = 2;
|
|
_scales[0].ScaleType = GScale.GScaleTypes.HorBottom;
|
|
_scales[0].Visible = true;
|
|
_scales[0].sX = 0;
|
|
_scales[0].sY = 0;
|
|
_scales[0].Pen.Width = 1;
|
|
|
|
_scales[1].ScaleType = GScale.GScaleTypes.VerLeft;
|
|
_scales[1].Visible = true;
|
|
_scales[1].sX = 0;
|
|
_scales[1].sY = 0;
|
|
_scales[1].Pen.Width = 1;
|
|
|
|
DrawingsNum = 1;
|
|
_drawings[0].Pen.Width = 1;
|
|
_drawings[0].XScale = Scales(0);
|
|
_drawings[0].Yscale = Scales(0);
|
|
_drawings[0].PointsNum = 1000;
|
|
Random r = new Random();
|
|
for (i = 0; i <= _drawings[0].PointsNum - 1; i++)
|
|
{
|
|
_drawings[0].Points[i].X = Convert.ToSingle(i) / 10;
|
|
_drawings[0].Points[i].Y = (float)(r.Next(0, 50) + Math.Sin(i / 50) * 25 + 25);
|
|
}
|
|
|
|
}
|
|
|
|
private void RedrawTimer_Tick(object sender, EventArgs e)
|
|
{
|
|
RedrawTimer.Stop();
|
|
RedrawTimer.Tick -= RedrawTimer_Tick;
|
|
RedrawTimer.Dispose();
|
|
RedrawTimer = null;
|
|
_Redraw();
|
|
}
|
|
|
|
}
|
|
}
|