locked
How to paint arc with path gradient using GDI+ (C#, WinForms) RRS feed

  • Question

  • Here is the result I am trying to achieve:

    Here is my attempt to create this using PathGradientBrush:

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
    
        double outerRadius = 120;
        double innerRadius = 110;
        PointF DistanceFromCenter(PointF center, double radius, double angle)        
        {
            double angleInRadians = angle * Math.PI / 180;
            return new PointF((float)(center.X + radius * (Math.Cos(angleInRadians))),
                              (float)(center.Y + radius * (Math.Sin(angleInRadians))));
        }
        protected override void OnPaint(PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            GraphicsPath path = new GraphicsPath();
            Point centerPoint = new Point(this.Width / 2, this.Width / 2);
            path.AddLine(this.DistanceFromCenter(centerPoint, innerRadius, 0), this.DistanceFromCenter(centerPoint, outerRadius, 0));
            path.AddArc(new RectangleF(centerPoint.X - (float)outerRadius, centerPoint.Y - (float)outerRadius, (float)outerRadius * 2, (float)outerRadius * 2), 0, -180);
            path.AddLine(this.DistanceFromCenter(centerPoint, outerRadius, -180), this.DistanceFromCenter(centerPoint, innerRadius, -180));
            path.AddArc(new RectangleF(centerPoint.X - (float)innerRadius, centerPoint.Y - (float)innerRadius, (float)innerRadius * 2, (float)innerRadius * 2), (float)0, -(float)180);
    
            PathGradientBrush pthGrBrush = new PathGradientBrush(path);
    
            // Set the color at the center of the path to red.
            pthGrBrush.CenterColor = Color.FromArgb(255, 255, 0, 0);
    
            // Set the colors of the points in the array.
            Color[] colors = {
           Color.FromArgb(255, 0, 0, 0),
           Color.FromArgb(255, 0, 255, 0),
           Color.FromArgb(255, 0, 0, 255), 
           Color.FromArgb(255, 255, 255, 255),
           Color.FromArgb(255, 0, 0, 0),
           Color.FromArgb(255, 0, 255, 0),
           Color.FromArgb(255, 0, 0, 255),
           Color.FromArgb(255, 255, 255, 255),
           Color.FromArgb(255, 0, 0, 0),  
           Color.FromArgb(255, 0, 255, 0)};
    
            pthGrBrush.SurroundColors = colors;
    
            // Fill the path with the path gradient brush.
            g.FillPath(pthGrBrush, path);
        }
    }
    

    Unfortunately, the result is not what I need:

    Friday, August 22, 2014 5:55 AM

Answers

  • Hi,

    the problem(s) are the pathPoints of the GraphicsPath, see:

    bobpowell.net/pgb.aspx section Got them surrounded

    You could surround the Arc by a harmonic polygon (around a fictitious circle) and so set points to the places where the colors should be:

    [using  System.Drawing.Drawing2D; needed]

        public partial class Form1 : Form
        {
            PathGradientBrush _brush = null;
            private GraphicsPath _gPath = null;
            double outerRadius = 130;
            double innerRadius = 110;
            PointF centerPoint = new PointF();
    
            public Form1()
            {
                InitializeComponent();
                this.DoubleBuffered = true;
    
                centerPoint = new PointF(this.ClientSize.Width / 2f, this.ClientSize.Height / 2f);
                _gPath = GetPath();
    
                RectangleF r = _gPath.GetBounds();
                RectangleF r2 = new RectangleF(r.X, r.Y, r.Width, r.Height * 2);
    
                //pass colors for a full circle
                _brush = GetBrush(new Color[] { 
                    Color.Red, Color.Yellow, Color.Green, Color.Blue, Color.Red, 
                    Color.Yellow, Color.Green, Color.Blue, Color.Red }, r2,
                    new PointF(this.ClientSize.Width / 2f, this.ClientSize.Height / 2f));
            }
    
            PointF DistanceFromCenter(PointF center, double radius, double angle)
            {
                double angleInRadians = angle * Math.PI / 180;
                return new PointF((float)(center.X + radius * (Math.Cos(angleInRadians))),
                                  (float)(center.Y + radius * (Math.Sin(angleInRadians))));
            }
    
            private GraphicsPath GetPath()
            {
                GraphicsPath fPath = new GraphicsPath();
    
                fPath.AddLine(this.DistanceFromCenter(centerPoint, innerRadius, 0), this.DistanceFromCenter(centerPoint, outerRadius, 0));
                fPath.AddArc(new RectangleF(centerPoint.X - (float)outerRadius, centerPoint.Y - (float)outerRadius, (float)outerRadius * 2, (float)outerRadius * 2), 0, -180);
                fPath.AddLine(this.DistanceFromCenter(centerPoint, outerRadius, -180), this.DistanceFromCenter(centerPoint, innerRadius, -180));
                fPath.AddArc(new RectangleF(centerPoint.X - (float)innerRadius, centerPoint.Y - (float)innerRadius, (float)innerRadius * 2, (float)innerRadius * 2), (float)0, -(float)180);
    
                return fPath;
            }
    
            protected override void OnLayout(LayoutEventArgs levent)
            {
                base.OnLayout(levent);
    
                centerPoint = new PointF(this.ClientSize.Width / 2f, this.ClientSize.Height / 2f);
    
                if (_gPath != null)
                    _gPath.Dispose();
                _gPath = GetPath();
    
                RectangleF r = _gPath.GetBounds();
                RectangleF r2 = new RectangleF(r.X, r.Y, r.Width, r.Height * 2);
    
                if (_brush != null)
                    _brush.Dispose();
                _brush = GetBrush(new Color[] { 
                    Color.Red, Color.Yellow, Color.Green, Color.Blue, Color.Red, 
                    Color.Yellow, Color.Green, Color.Blue, Color.Red }, r2,
                    new PointF(this.ClientSize.Width / 2f, this.ClientSize.Height / 2f));
    
                this.Invalidate();
            }
    
            protected override void OnPaint(PaintEventArgs e)
            {
                base.OnPaint(e);
    
                e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    
                if (_brush != null)
                    e.Graphics.FillPath(_brush, _gPath);
            }
    
            protected override void OnClosing(CancelEventArgs e)
            {
                if (_brush != null)
                    _brush.Dispose();
                if (_gPath != null)
                    _gPath.Dispose();
                base.OnClosing(e);
            }
    
            private PathGradientBrush GetBrush(Color[] Colors, RectangleF r, PointF centerOfBrush)
            {
                //based on a newsgroup post by Bob Powell
    
                //put a polygon to the GraphicsPath, so that there are PathPoints at the places where the colors should be
                int i = Colors.Length - 1;
                PointF[] points = new PointF[i + 1];
    
                float a = 0f;
                int n = 0;
                float cx = r.Width / 2f;
                float cy = r.Height / 2f;
    
                //Faktorberechnung:
                //Winkel in gleichwinkligem Polygon: 180 * (n - 2) / n
                //Sin((180 * (n - 2) / n) / 2) * r = Höhe (Sinussatz)
                //r / Höhe = Faktor = 1 / Sin(Winkel)
    
                int w = (int)(Math.Floor((180.0 * (i - 2.0) / i) / 2.0));
                double wi = w * Math.PI / 180.0;
                double faktor = 1.0 / Math.Sin(wi);
    
                float radx = (float)(cx * faktor);
                float rady = (float)(cy * faktor);
    
                while (a <= Math.PI * 2)
                {
                    points[n] = new PointF((float)((cx + (Math.Cos(a) * radx))) + r.Left, (float)((cy + (Math.Sin(a) * rady))) + r.Top);
                    n += 1;
                    a += (float)(Math.PI * 2 / i);
                }
    
                points[i] = points[0];
                GraphicsPath gPath = new GraphicsPath();
                gPath.AddLines(points);
    
                PathGradientBrush fBrush = new PathGradientBrush(gPath);
    
                fBrush.CenterPoint = centerOfBrush;
    
                fBrush.CenterColor = Color.Transparent;
                fBrush.SurroundColors = new Color[] { Color.White };
    
                try
                {
                    fBrush.SurroundColors = Colors;
                }
                catch
                {
                    MessageBox.Show("Zu viele Farben.", "Hinweis", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }
    
                return fBrush;
            }
        }

    Regards,

      Thorsten


    Friday, August 22, 2014 10:56 PM

All replies

  • Hi,

    the problem(s) are the pathPoints of the GraphicsPath, see:

    bobpowell.net/pgb.aspx section Got them surrounded

    You could surround the Arc by a harmonic polygon (around a fictitious circle) and so set points to the places where the colors should be:

    [using  System.Drawing.Drawing2D; needed]

        public partial class Form1 : Form
        {
            PathGradientBrush _brush = null;
            private GraphicsPath _gPath = null;
            double outerRadius = 130;
            double innerRadius = 110;
            PointF centerPoint = new PointF();
    
            public Form1()
            {
                InitializeComponent();
                this.DoubleBuffered = true;
    
                centerPoint = new PointF(this.ClientSize.Width / 2f, this.ClientSize.Height / 2f);
                _gPath = GetPath();
    
                RectangleF r = _gPath.GetBounds();
                RectangleF r2 = new RectangleF(r.X, r.Y, r.Width, r.Height * 2);
    
                //pass colors for a full circle
                _brush = GetBrush(new Color[] { 
                    Color.Red, Color.Yellow, Color.Green, Color.Blue, Color.Red, 
                    Color.Yellow, Color.Green, Color.Blue, Color.Red }, r2,
                    new PointF(this.ClientSize.Width / 2f, this.ClientSize.Height / 2f));
            }
    
            PointF DistanceFromCenter(PointF center, double radius, double angle)
            {
                double angleInRadians = angle * Math.PI / 180;
                return new PointF((float)(center.X + radius * (Math.Cos(angleInRadians))),
                                  (float)(center.Y + radius * (Math.Sin(angleInRadians))));
            }
    
            private GraphicsPath GetPath()
            {
                GraphicsPath fPath = new GraphicsPath();
    
                fPath.AddLine(this.DistanceFromCenter(centerPoint, innerRadius, 0), this.DistanceFromCenter(centerPoint, outerRadius, 0));
                fPath.AddArc(new RectangleF(centerPoint.X - (float)outerRadius, centerPoint.Y - (float)outerRadius, (float)outerRadius * 2, (float)outerRadius * 2), 0, -180);
                fPath.AddLine(this.DistanceFromCenter(centerPoint, outerRadius, -180), this.DistanceFromCenter(centerPoint, innerRadius, -180));
                fPath.AddArc(new RectangleF(centerPoint.X - (float)innerRadius, centerPoint.Y - (float)innerRadius, (float)innerRadius * 2, (float)innerRadius * 2), (float)0, -(float)180);
    
                return fPath;
            }
    
            protected override void OnLayout(LayoutEventArgs levent)
            {
                base.OnLayout(levent);
    
                centerPoint = new PointF(this.ClientSize.Width / 2f, this.ClientSize.Height / 2f);
    
                if (_gPath != null)
                    _gPath.Dispose();
                _gPath = GetPath();
    
                RectangleF r = _gPath.GetBounds();
                RectangleF r2 = new RectangleF(r.X, r.Y, r.Width, r.Height * 2);
    
                if (_brush != null)
                    _brush.Dispose();
                _brush = GetBrush(new Color[] { 
                    Color.Red, Color.Yellow, Color.Green, Color.Blue, Color.Red, 
                    Color.Yellow, Color.Green, Color.Blue, Color.Red }, r2,
                    new PointF(this.ClientSize.Width / 2f, this.ClientSize.Height / 2f));
    
                this.Invalidate();
            }
    
            protected override void OnPaint(PaintEventArgs e)
            {
                base.OnPaint(e);
    
                e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    
                if (_brush != null)
                    e.Graphics.FillPath(_brush, _gPath);
            }
    
            protected override void OnClosing(CancelEventArgs e)
            {
                if (_brush != null)
                    _brush.Dispose();
                if (_gPath != null)
                    _gPath.Dispose();
                base.OnClosing(e);
            }
    
            private PathGradientBrush GetBrush(Color[] Colors, RectangleF r, PointF centerOfBrush)
            {
                //based on a newsgroup post by Bob Powell
    
                //put a polygon to the GraphicsPath, so that there are PathPoints at the places where the colors should be
                int i = Colors.Length - 1;
                PointF[] points = new PointF[i + 1];
    
                float a = 0f;
                int n = 0;
                float cx = r.Width / 2f;
                float cy = r.Height / 2f;
    
                //Faktorberechnung:
                //Winkel in gleichwinkligem Polygon: 180 * (n - 2) / n
                //Sin((180 * (n - 2) / n) / 2) * r = Höhe (Sinussatz)
                //r / Höhe = Faktor = 1 / Sin(Winkel)
    
                int w = (int)(Math.Floor((180.0 * (i - 2.0) / i) / 2.0));
                double wi = w * Math.PI / 180.0;
                double faktor = 1.0 / Math.Sin(wi);
    
                float radx = (float)(cx * faktor);
                float rady = (float)(cy * faktor);
    
                while (a <= Math.PI * 2)
                {
                    points[n] = new PointF((float)((cx + (Math.Cos(a) * radx))) + r.Left, (float)((cy + (Math.Sin(a) * rady))) + r.Top);
                    n += 1;
                    a += (float)(Math.PI * 2 / i);
                }
    
                points[i] = points[0];
                GraphicsPath gPath = new GraphicsPath();
                gPath.AddLines(points);
    
                PathGradientBrush fBrush = new PathGradientBrush(gPath);
    
                fBrush.CenterPoint = centerOfBrush;
    
                fBrush.CenterColor = Color.Transparent;
                fBrush.SurroundColors = new Color[] { Color.White };
    
                try
                {
                    fBrush.SurroundColors = Colors;
                }
                catch
                {
                    MessageBox.Show("Zu viele Farben.", "Hinweis", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }
    
                return fBrush;
            }
        }

    Regards,

      Thorsten


    Friday, August 22, 2014 10:56 PM
  • Thank you!
    Saturday, August 23, 2014 6:58 AM