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 { /// /// Collection of GaugeCircularScales /// public class GaugeCircularScaleCollection : GenericCollection { } [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 /// /// Gets or sets the Scale pivot point, specified as a percentage /// [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 /// /// Gets or sets the Radius of Scale, specified as a percentage /// [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 /// /// Gets and sets the angle measured from the x-axis to the starting point of the scale /// [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 /// /// Get and sets the angle measured from the StartAngle to the ending point of the scale. /// [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 /// /// Converts Degrees to Radians /// /// Degrees /// Radians 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 /// /// Gets the angle (in radians) of the given point on the scale. /// /// /// Angle, in radians public double GetRadiansFromPoint(Point pt) { double radians = GetPointRadians(pt); return (radians); } #endregion #region GetDegreesFromPoint /// /// Gets the angle (in degrees) of the given point on the scale. /// /// /// Angle, in degrees 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 } }