Question

I hope there will be an easy answer, as often times, something stripped out of Compact Framework has a way of being performed in a seemingly roundabout manner, but works just as well as the full framework (or can be made more efficient).

Simply put, I wish to be able to do a function similar to System.Drawing.Graphics.DrawArc(...) in Compact Framework 2.0.

It is for a UserControl's OnPaint override, where an arc is being drawn inside an ellipse I already filled.

Essentially (close pseudo code, please ignore imperfections in parameters):

FillEllipse(ellipseFillBrush, largeEllipseRegion);
DrawArc(arcPen, innerEllipseRegion, startAngle, endAngle); //not available in CF

I am only drawing arcs in 90 degree spaces, so the bottom right corner of the ellipse's arc, or the top left. If the answer for ANY angle is really roundabout, difficult, or inefficient, while there's an easy solution for just doing just a corner of an ellipse, I'm fine with the latter, though the former would help anyone else who has a similar question.

Was it helpful?

Solution

I use this code, then use FillPolygon or DrawPolygon with the output points:

private Point[] CreateArc(float StartAngle, float SweepAngle, int PointsInArc, int Radius, int xOffset, int yOffset, int LineWidth)
{
    if(PointsInArc < 0)
        PointsInArc = 0;

    if(PointsInArc > 360)
        PointsInArc = 360;

    Point[] points = new Point[PointsInArc * 2];
    int xo;
    int yo;
    int xi;
    int yi;
    float degs;
    double rads;

    for(int p = 0 ; p < PointsInArc ; p++)
    {
        degs = StartAngle + ((SweepAngle / PointsInArc) * p);

        rads = (degs * (Math.PI / 180));

        xo = (int)(Radius * Math.Sin(rads));
        yo = (int)(Radius * Math.Cos(rads));
        xi = (int)((Radius - LineWidth) * Math.Sin(rads));
        yi = (int)((Radius - LineWidth) * Math.Cos(rads));

        xo += (Radius + xOffset);
        yo = Radius - yo + yOffset;
        xi += (Radius + xOffset);
        yi = Radius - yi + yOffset;

        points[p] = new Point(xo, yo);
        points[(PointsInArc * 2) - (p + 1)] = new Point(xi, yi);
    }

    return points;
}

OTHER TIPS

I had this exactly this problem and me and my team solved that creating a extension method for compact framework graphics class;

I hope I could help someone, cuz I spent a lot of work to get this nice solution

Mauricio de Sousa Coelho

Embedded Software Engineer

public static class GraphicsExtension
{
    // Implements the native Graphics.DrawArc as an extension
    public static void DrawArc(this Graphics g, Pen pen, float x, float y, float width, float height, float startAngle, float sweepAngle)
    {
        //Configures the number of degrees for each line in the arc
        int degreesForNewLine = 5;

        //Calculates the number of points in the arc based on the degrees for new line configuration
        int pointsInArc = Convert.ToInt32(Math.Ceiling(sweepAngle / degreesForNewLine)) + 1;

        //Minimum points for an arc is 3
        pointsInArc = pointsInArc < 3 ? 3 : pointsInArc;

        float centerX = (x + width) / 2;
        float centerY = (y + height) / 2;

        Point previousPoint = GetEllipsePoint(x, y, width, height, startAngle);

        //Floating point precision error occurs here
        double angleStep = sweepAngle / pointsInArc;

        Point nextPoint;
        for (int i = 1; i < pointsInArc; i++)
        {
            //Increments angle and gets the ellipsis associated to the incremented angle
            nextPoint = GetEllipsePoint(x, y, width, height, (float)(startAngle + angleStep * i));

            //Connects the two points with a straight line
            g.DrawLine(pen, previousPoint.X, previousPoint.Y, nextPoint.X, nextPoint.Y);

            previousPoint = nextPoint;
        }

        //Garantees connection with the last point so that acumulated errors cannot
        //cause discontinuities on the drawing
        nextPoint = GetEllipsePoint(x, y, width, height, startAngle + sweepAngle);
        g.DrawLine(pen, previousPoint.X, previousPoint.Y, nextPoint.X, nextPoint.Y);
    }

    // Retrieves a point of an ellipse with equation:
    private static Point GetEllipsePoint(float x, float y, float width, float height, float angle)
    {
        return new Point(Convert.ToInt32(((Math.Cos(ToRadians(angle)) * width + 2 * x + width) / 2)), Convert.ToInt32(((Math.Sin(ToRadians(angle)) * height + 2 * y + height) / 2)));
    }

    // Converts an angle in degrees to the same angle in radians.
    private static float ToRadians(float angleInDegrees)
    {
        return (float)(angleInDegrees * Math.PI / 180);
    }
}

Following up from @ctacke's response, which created an arc-shaped polygon for a circle (height == width), I edited it further and created a function for creating a Point array for a curved line, as opposed to a polygon, and for any ellipse.

Note: StartAngle here is NOON position, 90 degrees is the 3 o'clock position, so StartAngle=0 and SweepAngle=90 makes an arc from noon to 3 o'clock position.
The original DrawArc method has the 3 o'clock as 0 degrees, and 90 degrees is the 6 o'clock position. Just a note in replacing DrawArc with CreateArc followed by DrawLines with the resulting Point[] array.
I'd play with this further to change that, but why break something that's working?

private Point[] CreateArc(float StartAngle, float SweepAngle, int PointsInArc, int ellipseWidth, int ellipseHeight, int xOffset, int yOffset)
{
    if (PointsInArc < 0)
        PointsInArc = 0;

    if (PointsInArc > 360)
        PointsInArc = 360;

    Point[] points = new Point[PointsInArc];
    int xo;
    int yo;
    float degs;
    double rads;

    //could have WidthRadius and HeightRadius be parameters, but easier
    // for maintenance to have the diameters sent in instead, matching closer
    // to DrawEllipse and similar methods
    double radiusW = (double)ellipseWidth / 2.0;
    double radiusH = (double)ellipseHeight / 2.0;

    for (int p = 0; p < PointsInArc; p++)
    {
        degs = StartAngle + ((SweepAngle / PointsInArc) * p);

        rads = (degs * (Math.PI / 180));

        xo = (int)Math.Round(radiusW * Math.Sin(rads), 0);
        yo = (int)Math.Round(radiusH * Math.Cos(rads), 0);

        xo += (int)Math.Round(radiusW, 0) + xOffset;
        yo = (int)Math.Round(radiusH, 0) - yo + yOffset;

        points[p] = new Point(xo, yo);
    }

    return points;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top