Fill Octagon in C#

2019-06-01 05:53发布

问题:

I have created a method that draws an octagon, and it works well, as long as the size is 200 or higher

public static void FillOctagon(PaintEventArgs e, Color color, int x, int y, int width, int height)
{
     e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

     var points = new []
     {
          new Point(((x + width) / 2) - (width / 4), y), //side 1
          new Point(x, ((y + height) / 2) - (height / 4)), //side 2
          new Point(x, ((y + height) / 2) + (height / 4)), //side 3
          new Point(((x + width) / 2) - (width / 4), y + height), //side 4
          new Point((x + width) - (width / 4), y + height), //side 5
          new Point(x + width, ((y + height) / 2) + (height / 4)), //side 6
          new Point(x + width, ((y + height) / 2) - (height / 4)), //side 7
          new Point((x + width) - (width / 4), y) //side 8
     };

     using (var br = new SolidBrush(color))
     {
          using (var gpath = new GraphicsPath())
          {
              gpath.AddPolygon(points);
              e.Graphics.FillPath(br, gpath);
          }
     }
}

protected override void OnPaint(PaintEventArgs e)
{
     base.OnPaint(e);
     FillOctagon(e, Color.DodgerBlue, 20, 20, 50, 50);
}

Well, my problem is that if the size is less than 200 or if width is different from height and vice versa, the figure is deformed. My goal is to create a self-adapting figure, that retains its shape when the width and height is less than 200 or that width is different from height

This is what happens if, for example, I set the size to 50x50:

回答1:

I'll give you a different approach. Think about an octagon as an eight-sided blocky circle.

From the origin of a circle, you can calculate a point along the edge of a cicle given the angle t (in radians) and radius r using trigonometry.

x = r cos t
y = r sin t

You could apply this method the calculate the points of an octagon (or an equilateral shape with any number of sides) but it won't be able to deform (stretch). In order for it to deform, the formula changes slightly (where a is the horizontal radius and b is the vertical radius).

x = a cos t
y = b sin t

Here's what that might look like in code - I've modified your code in this instance.

public static void FillEquilateralPolygon(PaintEventArgs e, int sides, Color color, double x, double y, double width, double height)
{
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

    double a = width / 2;
    double b = height / 2;

    var points = new List<Point>();

    for (int pn = 0; pn < sides; pn++)
    {
        double angle = (360.0 / sides * pn) * Math.PI / 180;
        double px = a * Math.Cos(angle);
        double py = b * Math.Sin(angle);
        var point = new Point((int) (px + x), (int) (py + y));
        points.Add(point);
    }

    using (var br = new SolidBrush(color))
    {
        using (var gpath = new GraphicsPath())
        {
            gpath.AddPolygon(points.ToArray());
            e.Graphics.FillPath(br, gpath);
        }
    }
}

Now you can call this method, passing in 8 sides, and render an octogon that can deform.

FillEquilateralPolygon(e, 8, Color.Red, 201, 101, 201, 101);

If you don't want it to deform, just use the radius of the smallest side instead of replacing r with a and b.