591 lines
16 KiB
C#

using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using DevComponents.Instrumentation.Primitives;
namespace DevComponents.Instrumentation
{
/// <summary>
/// Collection of GaugeCircularScales
/// </summary>
public class GaugeCircularScaleCollection : GenericCollection<GaugeCircularScale>
{
}
[TypeConverter(typeof(GaugeScaleConvertor))]
public class GaugeCircularScale : GaugeScale
{
#region Private variables
private float _StartAngle;
private float _SweepAngle;
private float _Radius;
private PointF _PivotPoint;
private int _SquareSize;
#endregion
public GaugeCircularScale(GaugeControl gaugeControl)
: base(gaugeControl)
{
InitGaugeScale();
}
public GaugeCircularScale()
{
InitGaugeScale();
}
#region InitGaugeScale
private void InitGaugeScale()
{
Style = GaugeScaleStyle.Circular;
_PivotPoint = new PointF(.5f, .5f);
Radius = .38f;
StartAngle = 110;
SweepAngle = 320;
Width = .065f;
}
#endregion
#region Public properties
#region PivotPoint
/// <summary>
/// Gets or sets the Scale pivot point, specified as a percentage
/// </summary>
[Browsable(true), Category("Layout")]
[Editor("DevComponents.Instrumentation.Design.PivotPointEditor, DevComponents.Instrumentation.Design, Version=14.1.0.37, Culture=neutral, PublicKeyToken=76cb4c6eb576bca5", typeof(UITypeEditor))]
[Description("Indicates the Scale pivot point, specified as a percentage.")]
[TypeConverter(typeof(PointFConverter))]
public PointF PivotPoint
{
get { return (_PivotPoint); }
set
{
if (_PivotPoint.Equals(value) == false)
{
_PivotPoint = value;
OnGaugeItemChanged(true);
}
}
}
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
internal bool ShouldSerializePivotPoint()
{
return (_PivotPoint.X != .5f || _PivotPoint.Y != .5f);
}
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
internal void ResetPivotPoint()
{
_PivotPoint = new PointF(.5f, .5f);
}
#endregion
#region Radius
/// <summary>
/// Gets or sets the Radius of Scale, specified as a percentage
/// </summary>
[Browsable(true)]
[Category("Layout"), DefaultValue(.38f)]
[Editor("DevComponents.Instrumentation.Design.RadiusRangeValueEditor, DevComponents.Instrumentation.Design, Version=14.1.0.37, Culture=neutral, PublicKeyToken=76cb4c6eb576bca5", typeof(UITypeEditor))]
[Description("Indicates the Radius of Scale, specified as a percentage.")]
[NotifyParentProperty(true)]
public float Radius
{
get { return (_Radius); }
set
{
if (value < 0)
throw new ArgumentException("Value can not be less than zero.");
if (_Radius != value)
{
_Radius = value;
NeedLabelRecalcLayout = true;
OnGaugeItemChanged(true);
}
}
}
#endregion
#region StartAngle
/// <summary>
/// Gets and sets the angle measured from the x-axis to the starting point of the scale
/// </summary>
[Browsable(true)]
[Category("Layout"), DefaultValue(110f)]
[Description("Indicates the angle measured from the x-axis to the starting point of the scale.")]
[Editor("DevComponents.Instrumentation.Design.AngleRangeValueEditor, DevComponents.Instrumentation.Design, Version=14.1.0.37, Culture=neutral, PublicKeyToken=76cb4c6eb576bca5", typeof(UITypeEditor))]
public float StartAngle
{
get { return (_StartAngle); }
set
{
if (value < 0 || value > 360)
throw new ArgumentException("Value must be between 0 and 360 degrees.");
if (_StartAngle != value)
{
_StartAngle = value;
OnGaugeItemChanged(true);
}
}
}
#endregion
#region SweepAngle
/// <summary>
/// Get and sets the angle measured from the StartAngle to the ending point of the scale.
/// </summary>
[Browsable(true)]
[Category("Layout"), DefaultValue(320f)]
[Description("Indicates the angle measured from the StartAngle to the ending point of the scale.")]
[Editor("DevComponents.Instrumentation.Design.AngleRangeValueEditor, DevComponents.Instrumentation.Design, Version=14.1.0.37, Culture=neutral, PublicKeyToken=76cb4c6eb576bca5", typeof(UITypeEditor))]
public float SweepAngle
{
get { return (_SweepAngle); }
set
{
if (value < 0 || value > 360)
throw new ArgumentException("Value must be between -0 and +360.");
if (_SweepAngle != value)
{
_SweepAngle = value;
OnGaugeItemChanged(true);
}
}
}
#endregion
#endregion
#region Internal properties
#region AbsRadius
internal int AbsRadius
{
get { return (int)(SquareSize * _Radius); }
}
#endregion
#region AbsScaleWidth
internal int AbsScaleWidth
{
get { return (int)(AbsRadius * Width); }
}
#endregion
#region SquareSize
internal int SquareSize
{
get { return (_SquareSize); }
set { _SquareSize = value; }
}
#endregion
#endregion
#region RecalcLayout
public override void RecalcLayout()
{
if (NeedRecalcLayout == true)
{
RecalcMetrics();
NeedLabelRecalcLayout = true;
NeedSectionRecalcLayout = true;
NeedRangeRecalcLayout = true;
NeedTickMarkRecalcLayout = true;
NeedPointerRecalcLayout = true;
NeedPinRecalcLayout = true;
base.RecalcLayout();
}
}
#region RecalcMetrics
private void RecalcMetrics()
{
base.RecalcLayout();
bool autoCenter = GaugeControl.Frame.AutoCenter;
_SquareSize = GaugeControl.GetAbsSize(new SizeF(1, 1), true).Width;
Center = GaugeControl.GetAbsPoint(_PivotPoint, autoCenter);
int radius = (int)(_Radius * _SquareSize);
Rectangle r = new Rectangle();
r.Size = new Size(radius * 2, radius * 2);
r.Location = new Point(Center.X - r.Size.Width / 2, Center.Y - r.Size.Height / 2);
Bounds = r;
}
#endregion
#endregion
#region PaintBorder
protected override void PaintBorder(PaintEventArgs e)
{
Graphics g = e.Graphics;
if (BorderWidth > 0)
{
int radius = AbsRadius;
int n = (int) (radius*Width);
if (radius > 0 && n > 0)
{
using (GraphicsPath path = new GraphicsPath())
{
Rectangle r = Bounds;
r.Inflate(n/2, n/2);
path.AddArc(r, _StartAngle, _SweepAngle);
r.Inflate(-n, -n);
path.AddArc(r, _StartAngle + _SweepAngle, -_SweepAngle);
path.CloseAllFigures();
using (Pen pen = new Pen(BorderColor, BorderWidth))
g.DrawPath(pen, path);
}
}
}
}
#endregion
#region GetPoint
internal Point GetPoint(int radius, float angle)
{
Point pt = new Point();
// Normalize the angle and calculate some
// working vars
if (angle < 0)
angle += 360;
angle = angle % 360;
// Determine the angle quadrant, and then calculate
// the intersecting coordinate accordingly
double radians = GetRadians(angle % 90);
if (angle < 90)
{
pt.X = (int)(Math.Cos(radians) * radius);
pt.Y = (int)(Math.Sin(radians) * radius);
}
else if (angle < 180)
{
pt.X = -(int)(Math.Sin(radians) * radius);
pt.Y = (int)(Math.Cos(radians) * radius);
}
else if (angle < 270)
{
pt.X = -(int)(Math.Cos(radians) * radius);
pt.Y = -(int)(Math.Sin(radians) * radius);
}
else
{
pt.X = (int)(Math.Sin(radians) * radius);
pt.Y = -(int)(Math.Cos(radians) * radius);
}
pt.X += Center.X;
pt.Y += Center.Y;
return (pt);
}
#endregion
#region GetRadians
/// <summary>
/// Converts Degrees to Radians
/// </summary>
/// <param name="theta">Degrees</param>
/// <returns>Radians</returns>
private double GetRadians(float theta)
{
return (theta * Math.PI / 180);
}
#endregion
#region GetDegrees
internal double GetDegrees(double radians)
{
return (radians * 180 / Math.PI);
}
#endregion
#region GetRadiansFromPoint
/// <summary>
/// Gets the angle (in radians) of the given point on the scale.
/// </summary>
/// <param name="pt"></param>
/// <returns>Angle, in radians</returns>
public double GetRadiansFromPoint(Point pt)
{
double radians = GetPointRadians(pt);
return (radians);
}
#endregion
#region GetDegreesFromPoint
/// <summary>
/// Gets the angle (in degrees) of the given point on the scale.
/// </summary>
/// <param name="pt"></param>
/// <returns>Angle, in degrees</returns>
public double GetDegreesFromPoint(Point pt)
{
return (GetDegrees(GetRadiansFromPoint(pt)));
}
#endregion
#region GetPointRadians
internal double GetPointRadians(Point pt)
{
int dx = pt.X - Center.X;
int dy = pt.Y - Center.Y;
if (dx >= 0)
{
if (dy >= 0)
return (Math.Atan((double)dy / dx));
return (-Math.Atan((double)dx / dy) + Math.PI * 1.5);
}
if (dy >= 0)
return (-Math.Atan((double)dx / dy) + Math.PI / 2);
return (Math.Atan((double)dy / dx) + Math.PI);
}
#endregion
#region GetNearLabelRadius
internal int GetNearLabelRadius()
{
int scaleRadius = AbsRadius;
int scaleWidth = AbsScaleWidth;
int radius = scaleRadius - scaleWidth / 2;
radius = GetNearLabelRadius(MajorTickMarks, scaleRadius, radius);
radius = GetNearLabelRadius(MinorTickMarks, scaleRadius, radius);
return (radius);
}
private int GetNearLabelRadius(GaugeTickMark tickMarks, int scaleRadius, int radius)
{
if (tickMarks.Visible == true)
{
int tickMarkRadius = tickMarks.Radius;
if (tickMarks.Layout.Placement != DisplayPlacement.Near)
tickMarkRadius -= (int)(tickMarks.Layout.Length * scaleRadius);
if (tickMarkRadius < radius)
radius = tickMarkRadius;
}
return (radius);
}
#endregion
#region GetFarLabelRadius
internal int GetFarLabelRadius()
{
int scaleRadius = AbsRadius;
int scaleWidth = AbsScaleWidth;
int radius = scaleRadius + scaleWidth / 2;
radius = GetFarLabelRadius(MajorTickMarks, scaleRadius, radius);
radius = GetFarLabelRadius(MinorTickMarks, scaleRadius, radius);
return (radius);
}
private int GetFarLabelRadius(GaugeTickMark tickMarks, int scaleRadius, int radius)
{
if (tickMarks.Visible == true)
{
int tickMarkRadius = tickMarks.Radius;
if (tickMarks.Layout.Placement == DisplayPlacement.Near)
tickMarkRadius += (int)(tickMarks.Layout.Length * scaleRadius);
if (tickMarkRadius > radius)
radius = tickMarkRadius;
}
return (radius);
}
#endregion
#region CreateGradient
internal PathGradientBrush CreateGradient(Rectangle r,
float startAngle, float sweepAngle, GradientFillColor fillColor, int n)
{
r.Inflate(n, n);
const int count = 15;
int k = (sweepAngle > 0 ? -1 : 1);
float sa = startAngle + k;
float ea = sa + sweepAngle;
float n1 = (sweepAngle - k) / count;
PointF[] pts = new PointF[count + 1];
Color[] cls = new Color[count + 1];
Color c1 = fillColor.Start;
Color c2 = fillColor.End.IsEmpty == false ? fillColor.End : fillColor.Start;
// Calculate the RGB color deltas
float dr = (float)(c2.R - c1.R) / count;
float dg = (float)(c2.G - c1.G) / count;
float db = (float)(c2.B - c1.B) / count;
int radius = r.Width / 2;
for (int i = 0; i < count; i++)
{
pts[i] = GetPoint(radius, sa + i * n1);
Color c3 = Color.FromArgb(
(int)(c1.R + dr * i),
(int)(c1.G + dg * i),
(int)(c1.B + db * i));
cls[i] = c3;
}
float d = Math.Abs(sweepAngle);
float delta = (d < 180 ? (180 - d) : 2);
pts[count] = GetPoint(radius, ea + (sweepAngle < 0 ? -delta : delta));
cls[count] = c2;
PathGradientBrush pgb = new PathGradientBrush(pts);
pgb.CenterColor = Color.White;
pgb.CenterPoint = Center;
pgb.SurroundColors = cls;
pgb.FocusScales = new PointF(0f, 0f);
Blend blnd = new Blend();
blnd.Positions = new float[] { 0f, 1f };
blnd.Factors = new float[] { 1f, 1f };
pgb.Blend = blnd;
return (pgb);
}
#endregion
#region ICloneable Members
public override object Clone()
{
GaugeCircularScale copy = new GaugeCircularScale();
CopyToItem(copy);
return (copy);
}
#endregion
#region CopyToItem
public override void CopyToItem(GaugeItem copy)
{
GaugeCircularScale c = copy as GaugeCircularScale;
if (c != null)
{
base.CopyToItem(c);
c.PivotPoint = _PivotPoint;
c.Radius = _Radius;
c.StartAngle = _StartAngle;
c.SweepAngle = _SweepAngle;
}
}
#endregion
}
}