What is causing this edge condition with CGContextAddArc?

你离开我真会死。 提交于 2020-01-04 15:16:30

问题


I created a view by drawing an arc of dots around the perimeter of a circle using iOS Core Graphics. Dot centres are calculated, stored in an array and retrieved using CGContextAddArc before being rendered to screen. I have every confidence in methods used to draw perimeter dots which may be [A] outlined, [B] filled with one colour, [C] alternately filled and outlined, and [D] filled with a sequence of five colours as shown.

However if centre dots are added to the array the properties of the last dots drawn on the perimeter change.

The simplest way to demonstrate this condition is to draw perimeter dots outlined with the addition of [E] the small centred dot [F] the large centred dot and [G] both centred dots; in each case, both small and large centred dots are expected to be filled green. With [H] there is also a problem when both centred dots are outlined not filled.

I have been learning Core Graphics for a month and need help to identify the edge condition that causes this. I would really welcome the insights of a more experienced core graphics programmer. Thanks.

Here is the implementation code; firstly, the method to initialise view settings.

- (void)ViewSettings_WaitView {
    sectors         = 80;   // number of dots on perimeter
    limit           = sectors;

    uberRadius      = 52;   // radius of large circle perimeter
    dotRadius       = 4;    // radius of dots on large circle perimeter
    dotsFilled      = FALSE;    // fill every with colour or outline
    oneColour       = FALSE;    // every colour or one colour
    alternateDots   = FALSE;    // alternately filled and outlined

    ringDot         = 64;   // 64:show 0:hide
    ringDotFilled   = TRUE; // fill or outlined    
    centreDot       = 26;       // 26:show 0:hide
    centreDotFilled = FALSE;    // fill or outlined

    [self centreReference];     // set up arc drawing to start from 12 o'clock position
    [self selectZone];          // emulate 1-of-5 colours selected in GlobalView
} 

And here is the code that draws these images

- (void)drawCircle                  {
    context          = UIGraphicsGetCurrentContext();          // Get the Graphics Context
    CGContextSetLineWidth(context, 0.5);                       // Set the circle outerline-width

    dotPosition                    = CGPointMake(uberX,uberY); // centre point for ring dot and centre dot

    // create ring dot (larger centre dot)

    if (ringDot     != 0) {
        iOSCircle *newCircle       = [[iOSCircle alloc] init]; // Create a new iOSCircle Object
        newCircle.circleRadius     = ringDot;                  // ringDot radius
        newCircle.circleCentre     = dotPosition;              // place ringDot on the frame
        [totalCircles addObject:newCircle];                    // add to the circle Array
        [self setNeedsDisplay];                                // update the view

        NSLog(@"ringDot added:%@ radius: %f", NSStringFromCGPoint(dotPosition), ringDot);
    }

    // create centre dot (smaller centre dot)

    if (centreDot    != 0) {
        iOSCircle *newCircle       = [[iOSCircle alloc] init]; // Create a new iOSCircle Object
        newCircle.circleRadius     = centreDot;                // ringDot radius
        newCircle.circleCentre     = dotPosition;              // place ringDot on the frame
        [totalCircles addObject:newCircle];                    // add to the circle Array
        [self setNeedsDisplay];                                // update the view

        NSLog(@"centreDot added:%@ radius: %f", NSStringFromCGPoint(dotPosition), centreDot);
    }

    // create sector dots (on perimeter of the arc)

    for (dotCount   = 1; dotCount < limit+1; dotCount++)
    {
        iOSCircle *newCircle    = [[iOSCircle alloc] init];    // Create a new iOSCircle Object
        newCircle.circleRadius  = dotRadius;

            [self newCentre];                                  // create a new x and y point for each sector dot

        dotPosition             = CGPointMake(x,y);            // create each sector dot
        newCircle.circleCentre  = dotPosition;                 // place each dot on the frame
            [totalCircles addObject:newCircle];                // add to the circle Array
            [self setNeedsDisplay];                            // update the view

        NSLog(@"Dot %i %@", dotCount, NSStringFromCGPoint(dotPosition));
    }

    dotCount = 1;
    for (iOSCircle *circle in totalCircles) {                  // Loop through array and retrieve dot dimensions
        CGContextAddArc(context, circle.circleCentre.x, circle.circleCentre.y, circle.circleRadius, 0.0, M_PI * 2.0, YES);

            [self renderSectorDots];                           // render dots to view

    dotCount++;

    }    
    if (ringDot    != 0) {
        NSLog(@"add ringDot %@ radius: %f", NSStringFromCGPoint(dotPosition), ringDot);

        [self renderRingDot];
    }
    if (centreDot    != 0) {
        NSLog(@"add centreDot %@ radius: %f", NSStringFromCGPoint(dotPosition), centreDot);

        [self renderCentreDot];
    }
} 

And the methods for rendering centre dot (ring dot is similar)

- (void)renderCentreDot {
    switch (centreDotFilled) {
        case 1:
            colourIndex = selectedZone;
            [self whatColour];
            break;
        default:
            [self dotOutline];
            break;
    }
} 

- (void)whatColour {
    switch (colourIndex) {
        case 1:
            // Fill the circle with cyan
            [self paintCyan];
            break;
        case 2:
            // Fill the circle with red
            [self paintRed];
            break;
        case 3:
            // Fill the circle with yellow
            [self paintYellow];
            break;
        case 4:
            // Fill the circle with magenta
            [self paintMagenta];
            break;
        case 5:
            // Fill the circle with green
            [self paintGreen];
            break;
        default:
            break;
    }
}

- (void)dotOutline {
    CGContextStrokePath(context);                     // draw outerline only
} 

- (void)paintGreen {
    CGContextSetFillColorWithColor(context, [[UIColor greenColor] CGColor]);
    CGContextDrawPath(context, kCGPathFillStroke);    // fill with outerline-colour
    }

And finally the method for calculating new centres for the sector dots

- (void)newCentre  {
    dotAngle    = dotAngle + uberAngle;
    x           = uberX + (uberRadius * 2 * cos(dotAngle));
    y           = uberY + (uberRadius * 2 * sin(dotAngle));    
//    NSLog(@"%i %f %f %f", dotCount, dotAngle, endAngle, uberAngle);
}

and a method to initialise centre parameters and start drawing from 12 o'clock position

- (void)centreReference { 

    uberX     = 160; 
    uberY     = 240; 
    uberAngle = (2.0 * PI) / sectors; 

    dotAngle  = PI * -0.5;             // start drawing 0.5 PI radians before 3 o'clock
    endAngle  = PI * 1.5;              // stop drawing 1.5 PI radians after 3 o'clock 

    NSLog(@"%f %f %f %f", uberX, ringY, uberRadius, uberAngle); 

}


回答1:


You have 80 outer circles and 0, 1, or 2 inner circles. All of these circles are stored in a single array called totalCircles, and the inner circles are stored at the front of the array. So if you have two inner circles, totalCircles has 82 elements.

The problem is that you're only drawing the first 80 circles: two inner circles and 78 outer circles.

Then you attempt to fill in one of the inner circles, but instead fill in the 79th outer circle.

In other words, you're losing track of which circles are which.


Try structuring your code in the following way:

You already have a "iOSCircle" object, which is good. This circle object should also contain its color and have the ability to draw itself:

@interface Circle : NSObject
@property (nonatomic) CGPoint centre;
@property (nonatomic) CGFloat radius;
@property (nonatomic, strong) UIColor* color;
- (void)draw;
@end

The -draw method draws the circle, optionally filling the color if it is set:

- (void)draw
{
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGContextAddArc(ctx, self.centre.x, self.centre.y, etc...)
    if (self.color)
    {
        [self.color setFill];
        CGContextDrawPath(ctx, kCGPathFillStroke);
    }
    else
    {
        CGContextStrokePath(ctx);
    }
}

You should have a method that generates an array of circle objects. This will create the 80 outer circles, and optionally the inner circles. This will also assign a color to each circle.

Your main drawing method then as simple as this:

- (void)drawCircle
{
    NSArray* circles = [self generateCircles];
    for (Circle* circle in circles)
    {
        [circle draw];
    }
}


来源:https://stackoverflow.com/questions/28378461/what-is-causing-this-edge-condition-with-cgcontextaddarc

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!