1

Back here. Is there any way to improve the quality of the Arc?
I'm using e.Graphics.SmoothingMode = SmoothingMode.AntiAlias

This is the piece of code that creates the arc:

using (GraphicsPath gp = new GraphicsPath())
{
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    gp.Reset();
    gp.AddPie(_OuterRectangle, (float)_Properties.Origin, (float)_Properties.GaugeType);
    gp.Reverse();

    gp.AddPie(_InnerRectangle, (float)_Properties.Origin, (float)_Properties.GaugeType);
    gp.Reverse();
    pArea.SetClip(gp);

    using (Pen oPen = new Pen(this.ForeColor, 2f))
    {
       e.Graphics.DrawPath(oPen, gp);
    }
    e.Graphics.SetClip(ClientRectangle);
}

enter image description here

Thanks in advance.

EDIT:
I've did what LarsTech proposed and now the quality is perfect, but I'm not having the figure I need:

enter image description here

  • OuterRectangle: is the ClientRectangle area, that I'm manipulating it to make Width and Height the same lenght;
  • InnerRectangle: is 2/3ths of the ClientRectangle area, ergo, of the OuterRectangle;
  • Properties.Origin: is the angle where the arc starts. I have it in an enumerator as Cardinal Points, where North is 270, East is 0,
    and so. In case of the figure, is SouthWest, 135 degrees;

  • Properties.GaugeType: is another enumerator that says if is Complete = 360, Half = 180, Quarter = 90, so with that I can determine the sweep angle. In case of the figure is ThreeQuarter, 270 degrees.

Jimi
  • 29,621
  • 8
  • 43
  • 61
Nane
  • 318
  • 1
  • 8
  • 18
  • 1
    Looks like you are clipping with that graphics path. Can't clip regions like that and still have anti-alias work. – LarsTech Jan 08 '19 at 23:31
  • @LarsTech. Thank you for your comment. Any advice? – Nane Jan 08 '19 at 23:48
  • Get rid of the clipping? – LarsTech Jan 08 '19 at 23:56
  • @LarsTech. See my edited question. Anyway, Thank you very much! – Nane Jan 09 '19 at 00:15
  • Can you update the code you are using now? It would help if you gave us the values you are using. The rectangles, _Properties.Origin, GaugeType, etc. – LarsTech Jan 09 '19 at 00:21
  • What is pArea?? – LarsTech Jan 09 '19 at 00:22
  • pArea is just e.Graphics passed as parameter. I'm updating the question quickly, but googling, seems that I need to do AddLine for both ends to close the figure. – Nane Jan 09 '19 at 00:26
  • It's confusing, because you also have e.Graphics. – LarsTech Jan 09 '19 at 00:27
  • Edited the question. Thanks @LarsTech – Nane Jan 09 '19 at 00:53
  • 3
    Without clipping, you could just use the `_InnerRectangle`, inflate it by (-1, -1) and fill it (`e.Graphics.FillEllipse()`) with the background color of the canvas. This will eliminate the Pie lines without compromising the antialiasing (and avoiding further calculations). – Jimi Jan 09 '19 at 01:24
  • @LarsTech. But the _InnerRectangle is the one that defines the inner arc of the figure. I'm filling the color in other method, that calculates the angle according to the percentage. The one that I published, is for the Draw only, not the fill. But now is drawing those lines up to the center of the figure (both, the draw and the fill). I'm not doing a CloseFigure. – Nane Jan 09 '19 at 01:38
  • 2
    If you're referring to my comment (LarsTech didn't mention the `_InnerRectangle`), what I mean is that you can use it, in the code you have here, to fill the center of the arcs designed by `AddPie` and delete the lines that `AddPie` is generating: `_InnerRectangle.Inflate(-1, -1); e.Graphics.FillEllipse([Brush from canvas.backcolor], _InnerRectangle);`. Then, if you want to use it somewhere else for other reasons, that's unrelated. – Jimi Jan 09 '19 at 01:53
  • @Jimi. Ohhh. I see it now. Thank you! I'll try it tonight and let you know. And yes, I was referring to your comment. Sorry about that. And thanks again – Nane Jan 09 '19 at 10:49
  • Sure. If you want to test what I was referring to, I can post a sample code to PasteBin or Google Drive. – Jimi Jan 09 '19 at 10:52
  • @Jimi. That would be great if you don't mind. Thank you!!! – Nane Jan 09 '19 at 11:01
  • 1
    @Jimi I reopened the question (didn't quite agree with the duplicate), so if you want to post it as an answer, go ahead. – LarsTech Jan 09 '19 at 15:22
  • @LarsTech and Jimi. Thank you for your time and effort to answer my doubt. With both answers I will be able to solve my problem. I´m really grateful to both. – Nane Jan 09 '19 at 16:57
  • 1
    Since the question was re-opened, I can post here that code sample. It wasn't actually meant as an answer, but if you think it *fits* , I will move it here. – Jimi Jan 09 '19 at 17:13
  • 1
    @LarsTech Well, the duplicate was *in context*, but not exactly an answer to this question. If you want to post an answer yourself, I can post the code I used as a *proof of concept* after, no problem. – Jimi Jan 09 '19 at 17:13

1 Answers1

2

The problem:
When clipping a region of the current Graphics (Graphics.SetClip method), the resulting drawing loses quality, because the antialiasing effect generated by Graphics.SmoothingMode = SmoothingMode.AntiAlias is lost.

A possible solution is to avoid clipping the region defined by the GraphicsPath used to design the arcs (GraphicsPath.AddPie method); this, however, leaves the lines of the Pie visible, compromising the shape.

Another solution is to draw an ellipsis in the center of the arcs using the background color of the Canvas. Since the arcs are drawn using two rectangles, we can use the inner rectagle, inflate it (Rectangle.Inflate method) as needed (a fraction - Pen.Width / 2 - of the Pen size used for the ouline, usually).

Rectangle inflate

This allows to delete the artifacts generated by the GraphicsPath shapes and to draw some other graphics content in the center of the shapes.

For example, using different Brushes:

LinearGradienBrush - HatchBrush - TextureBrush

  LinearGradientBrush           HatchBrush               TextureBrush

Of course there are other methods to achieve the same result. We could draw the Arcs using the GraphicsPath.AddArc method, extract or calculate the first and last points of the Arcs and use them to draw two lines (GraphicsPath.AddLine) that will close the figures.

But, since we want to draw different graphics objects in the center of the arcs, these objects will cover the center area anyway.

How to use this code:

  • In a Form, add a TrackBar (named tbarSpeed, here)
  • Add a PictureBox (named Canvas), with Size (200, 200).
  • Wire up the TrackBar tbarSpeed_Scroll event and the Panel Canvas_Paint event.

using System.Drawing;
using System.Drawing.Drawing2D;


float GaugeValue = 88.0f;
float GaugeSweepAngle = 270.0f;
float GaugeStartAngle = 135.0F;

private void Canvas_Paint(object sender, PaintEventArgs e)
{
    var canvas = sender as Control;
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    var outerRectangle = new Rectangle(10, 10, 180, 180);
    var innerRectangle = new Rectangle(30, 30, 140, 140);
    var blendRectangle = new Rectangle(10, 10, 180, 160);
    var innerCenter = new PointF(outerRectangle.Left + (outerRectangle.Width / 2),
                                    outerRectangle.Top + (outerRectangle.Height / 2));
    float gaugeLength = (outerRectangle.Width / 2) - 2;

    using (var path = new GraphicsPath())
    {
        path.AddPie(outerRectangle, GaugeStartAngle, GaugeSweepAngle);
        path.AddPie(innerRectangle, GaugeStartAngle, GaugeSweepAngle);
        innerRectangle.Inflate(-1, -1);

        using (var pen = new Pen(Color.White, 3f))
        using (var backgroundbrush = new SolidBrush(canvas.BackColor))
        using (var gradientBrush = new LinearGradientBrush(blendRectangle,
               Color.Green, Color.Red, LinearGradientMode.ForwardDiagonal))
        {
            var blend = new Blend()
            {
                Factors = new[] { 0.0f, 0.0f, 0.1f, 0.3f, 0.7f, 1.0f },
                Positions = new[] { 0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0f }
            };

            gradientBrush.Blend = blend;
            e.Graphics.FillPath(gradientBrush, path);
            e.Graphics.DrawPath(pen, path);

            e.Graphics.FillEllipse(backgroundbrush, innerRectangle);

            using (var format = new StringFormat())
            {
                format.Alignment = StringAlignment.Center;
                format.LineAlignment = StringAlignment.Center;
                innerRectangle.Location = new Point(innerRectangle.X, innerRectangle.Y + canvas.Font.Height);
                e.Graphics.DrawString(GaugeValue.ToString() + "%", canvas.Font, Brushes.White, innerRectangle, format);
            }

            using (var mx = new Matrix())
            {
                mx.RotateAt(GaugeStartAngle + 90 + (GaugeValue * (GaugeSweepAngle / 100)), innerCenter);
                e.Graphics.Transform = mx;
                e.Graphics.DrawLine(pen, innerCenter, new PointF(innerCenter.X, innerCenter.Y - gaugeLength));
                e.Graphics.ResetTransform();
            }
        }
    }
}

private void tbarSpeed_Scroll(object sender, EventArgs e)
{
    GaugeValue = tbarSpeed.Value;
    Canvas.Invalidate();
}

Gauge control

Sample code on PasteBin

Jimi
  • 29,621
  • 8
  • 43
  • 61