using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Text; using DevComponents.DotNetBar.Charts.Style; namespace DevComponents.DotNetBar.Charts { internal class PointMarker : IDisposable { #region Private variables private List _Bitmaps = new List(); #endregion #region GetMarkerBitmap public Bitmap GetMarkerBitmap(Graphics g, PointMarkerType markerType, int markerPoints, Size size, int markerRotation, Background background, Color borderColor, int borderWidth) { if (markerType == PointMarkerType.None || markerType == PointMarkerType.NotSet) return (null); if (markerType == PointMarkerType.Cross || background == null || background.IsEmpty) { if (borderColor.IsEmpty) borderColor = Color.Black; } // Add margin to permit better antialiasing of image size.Width++; size.Height++; Bitmap bitmap = null; bitmap = FindBitmap(markerType, markerPoints, size, markerRotation, background, borderColor, borderWidth); if (bitmap == null) { size.Width = Math.Max(size.Width, 3); size.Height = Math.Max(size.Height, 3); Rectangle r = new Rectangle(Point.Empty, size); using (GraphicsPath path = GetMarkerPath(markerType, markerPoints, markerRotation, r, borderWidth)) { if (path != null) { bitmap = new Bitmap(size.Width, size.Height, g); using (Graphics gBmp = Graphics.FromImage(bitmap)) { gBmp.SmoothingMode = SmoothingMode.AntiAlias; FillMarkerPath(gBmp, path, r, markerType, background, borderColor, borderWidth); if (markerRotation != 0 && markerRotation != -1) { Bitmap bitmap2 = RotatePic(bitmap, markerRotation); bitmap.Dispose(); bitmap = bitmap2; } _Bitmaps.Add(new BitmapEntry(markerType, size, markerPoints, markerRotation, background, borderColor, borderWidth, bitmap)); } } } } return (bitmap); } #endregion #region FindBitmap public Bitmap FindBitmap(PointMarkerType markerType, int markerPoints, Size size, int markerRotation, Background background, Color borderColor, int borderWidth) { foreach (BitmapEntry entry in _Bitmaps) { if (entry.MarkerType == markerType && entry.MarkerPoints == markerPoints && entry.MarkerRotation == markerRotation && entry.MarkerSize.Equals(size) && entry.BorderWidth == borderWidth && entry.BorderColor.Equals(borderColor) && entry.Background.IsEqualTo(background)) { return (entry.Bitmap); } } return (null); } #endregion #region GetMarkerPath internal GraphicsPath GetMarkerPath( PointMarkerType markerType, int markerPoints, int markerRotation, Rectangle r, int borderWidth) { r.Inflate(-borderWidth, -borderWidth); if (r.Width > 0 && r.Height > 0) { switch (markerType) { case PointMarkerType.Ellipse: return (GetCirclePath(r)); case PointMarkerType.Cross: return (GetCrossPath(r, markerPoints)); case PointMarkerType.Diamond: return (GetDiamondPath(r)); case PointMarkerType.Rectangle: return (GetRectanglePath(r)); case PointMarkerType.Star: return (GetStarPath(r, markerPoints)); case PointMarkerType.Triangle: return (GetTrianglePath(r)); default: return (GetPolygonPath(r, markerPoints)); } } return (null); } #region GetCirclePath private GraphicsPath GetCirclePath(Rectangle r) { GraphicsPath path = new GraphicsPath(); path.AddEllipse(r); return (path); } #endregion #region GetCrossPath private GraphicsPath GetCrossPath(Rectangle r, int points) { GraphicsPath path = new GraphicsPath(); PointF[] pts = new PointF[2 * points]; double rx1 = r.Width / 2; double ry1 = r.Height / 2; if (rx1 < 2) rx1 = 2; if (ry1 < 2) ry1 = 2; double cx = r.X + rx1; double cy = r.Y + ry1; double theta = MathHelper.ToRadians(270); double dtheta = Math.PI / points; for (int i = 0; i < 2 * points; i += 2) { pts[i] = new PointF( (float)(cx + rx1 * Math.Cos(theta)), (float)(cy + ry1 * Math.Sin(theta))); pts[i + 1] = new PointF((float)cx, (float)cy); theta += (dtheta * 2); } path.AddPolygon(pts); path.CloseAllFigures(); return (path); } #endregion #region GetDiamondPath private GraphicsPath GetDiamondPath(Rectangle r) { GraphicsPath path = new GraphicsPath(); int dx = r.Width / 2; int dy = r.Height / 2; int mx = r.X + dx; int my = r.Y + dy; Point[] pts = { new Point(mx, my - dy), new Point(mx + dx, my), new Point(mx, my + dy), new Point(mx - dx, my), }; path.AddPolygon(pts); path.CloseAllFigures(); return (path); } #endregion #region GetPolygonPath private GraphicsPath GetPolygonPath(Rectangle r, int sides) { if (sides <= 4) return (GetRectanglePath(r)); GraphicsPath path = new GraphicsPath(); int radius = Math.Min(r.Width, r.Height); float dx = (float)radius / 2; float radians = (float)MathHelper.ToRadians(270); float delta = (float)MathHelper.ToRadians((float)360 / sides); Point[] pts = new Point[sides]; for (int i = 0; i < sides; i++) { pts[i] = new Point( (int)(dx * Math.Cos(radians) + dx + r.X), (int)(dx * Math.Sin(radians) + dx + r.Y)); radians += delta; } path.AddPolygon(pts); path.CloseAllFigures(); if (r.Width != r.Height) { PointF[] dp = { new PointF(0, 0), new PointF(r.Width, 0), new PointF(0, r.Height), }; path.Warp(dp, new RectangleF(0, 0, radius, radius)); } return (path); } #endregion #region GetRectanglePath private GraphicsPath GetRectanglePath(Rectangle r) { GraphicsPath path = new GraphicsPath(); path.AddRectangle(r); return (path); } #endregion #region GetStarPath private GraphicsPath GetStarPath(Rectangle r, int points) { if (points < 2) points = 2; GraphicsPath path = new GraphicsPath(); PointF[] pts = new PointF[2 * points]; double rx1 = r.Width / 2; double ry1 = r.Height / 2; if (rx1 < 2) rx1 = 2; if (ry1 < 2) ry1 = 2; double rx2 = rx1 / 2; double ry2 = ry1 / 2; double cx = r.X + rx1; double cy = r.Y + ry1; double theta = MathHelper.ToRadians(270); double dtheta = Math.PI / points; for (int i = 0; i < 2 * points; i += 2) { pts[i] = new PointF( (float)(cx + rx1 * Math.Cos(theta)), (float)(cy + ry1 * Math.Sin(theta))); theta += dtheta; pts[i + 1] = new PointF( (float)(cx + rx2 * Math.Cos(theta)), (float)(cy + ry2 * Math.Sin(theta))); theta += dtheta; } path.AddPolygon(pts); path.CloseAllFigures(); return (path); } #endregion #region GetTrianglePath private GraphicsPath GetTrianglePath(Rectangle r) { // Equal height and width will not be adjusted to make // an equalaterial triangle - thus rotation will skew image. GraphicsPath path = new GraphicsPath(); int dx = r.Width / 2; int dy = r.Height / 2; Point[] pts = { new Point(r.X + dx, r.Y), new Point(r.X , r.Y + dy * 2), new Point(r.X + dx * 2, r.Y + dy * 2), }; path.AddPolygon(pts); path.CloseAllFigures(); return (path); } #endregion #endregion #region FillMarkerPath internal void FillMarkerPath(Graphics g, GraphicsPath path, Rectangle r, PointMarkerType markerType, Background background, Color borderColor, int borderWidth) { if (markerType != PointMarkerType.Cross) { BackFillType fillType = GetMarkerFillType(markerType, background); using (Brush br = background.GetBrush(r, -1, fillType)) g.FillPath(br, path); } if (borderColor.IsEmpty == false && borderWidth > 0) { using (Pen pen = new Pen(borderColor, borderWidth)) g.DrawPath(pen, path); } } #region GetMarkerFillType private BackFillType GetMarkerFillType(PointMarkerType markerType, Background background) { if (background.Color2.IsEmpty == true) return (BackFillType.None); if (background.BackFillType == BackFillType.Auto) { switch (markerType) { case PointMarkerType.Ellipse: case PointMarkerType.Star: return (BackFillType.Center); default: return (BackFillType.VerticalCenter); } } return (background.BackFillType); } #endregion #endregion #region RotatePic private Bitmap RotatePic(Bitmap obmp, float angle) { float rad = (float)MathHelper.ToRadians(angle); double fW = Math.Abs((Math.Cos(rad) * obmp.Width)) + Math.Abs((Math.Sin(rad) * obmp.Height)); double fH = Math.Abs((Math.Sin(rad) * obmp.Width)) + Math.Abs((Math.Cos(rad) * obmp.Height)); Bitmap nbmp = new Bitmap((int)Math.Ceiling(fW), (int)Math.Ceiling(fH)); using (Graphics g = Graphics.FromImage(nbmp)) { g.SmoothingMode = SmoothingMode.AntiAlias; g.InterpolationMode = InterpolationMode.HighQualityBicubic; float hw = nbmp.Width / 2f; float hh = nbmp.Height / 2f; Matrix m = g.Transform; m.RotateAt(angle, new PointF(hw, hh), MatrixOrder.Append); g.Transform = m; g.DrawImage(obmp, new PointF((float)((nbmp.Width - obmp.Width) / 2), (float)((nbmp.Height - obmp.Height) / 2))); } return nbmp; } #endregion #region Clear public void Clear() { foreach (BitmapEntry entry in _Bitmaps) { entry.Background.Dispose(); entry.Bitmap.Dispose(); } _Bitmaps.Clear(); } #endregion #region IDisposable Members public void Dispose() { Clear(); } #endregion #region BitmapEntry private class BitmapEntry { public PointMarkerType MarkerType; public Size MarkerSize; public int MarkerPoints; public int MarkerRotation; public Background Background; public Color BorderColor; public int BorderWidth; public Bitmap Bitmap; public BitmapEntry(PointMarkerType markerType, Size markerSize, int markerPoints, int markerRotation, Background background, Color borderColor, int borderWidth, Bitmap bitmap) { MarkerType = markerType; MarkerSize = markerSize; MarkerPoints = markerPoints; MarkerRotation = markerRotation; Background = background.Copy(); BorderColor = borderColor; BorderWidth = borderWidth; Bitmap = bitmap; } } #endregion } #region enums #region PointMarkerType public enum PointMarkerType { /// /// Type not set. /// NotSet = -1, /// /// No Marker /// None, /// /// Cross /// Cross, /// /// Diamond /// Diamond, /// /// Ellipse /// Ellipse, /// /// Polygon (Pentagon, Hexagon, etc) /// Polygon, /// /// Rectangle /// Rectangle, /// /// Star /// Star, /// /// Triangle /// Triangle, } #endregion #endregion }