text labels drawn in CGRect form an elliptical arc when images drawn using the same coordinates form a circular arc
Asked Answered
B

2

0

I am new to Core Graphics and trying to understand why text labels I draw in CGRect form an elliptical arc when images I draw using the same coordinates form a circular arc.

The original code by Arthur Knopper creates circles wherever the screen is touched. By removing the touches method, I have been able to generate a series of small circles (dots) along a circular arc (uber circle). Each dot is centred on the perimeter of the uber circle (as shown below).

16 dots on über circle

In order to label each dot I use the same point coordinates I used for placing the dot. However text labels form an elliptical arc even though dots form a circular arc (as shown below). Labels are also hidden by the dots when dots are filled. The reason for this is a complete mystery.

As a novice I am probably missing something basic in Core Graphics. If anyone could explain what that is and what I need to do to make both arcs circular and place labels on top of the dots I’d be most grateful.

Thanks.

label arc and dot arc

Here is the code.

    circleView.h

    NSMutableArray  *totalCircles;

    int            dotCount, limit;

    float         uberX, uberY, uberRadius, uberAngle, labelX,
                    labelY,dotRadius, dotsFilled, sectors, x, y;

    CGPoint         dotPosition;
    CGRect          boxBoundary;
    CGContextRef    context;
}

- (void)demo;

@end

And ...

-@implementation iOSCircleView

- (id)initWithFrame:(CGRect)frame   {
    self = [super initWithFrame:frame];
    if (self) {

        // Initialization code
        totalCircles            = [[NSMutableArray alloc] init];

        // Set background color
        self.backgroundColor    = [UIColor whiteColor];
    }
    return self;
}   // frame a view for drawing



- (void)drawRect:(CGRect)rect       {
     [self demo];
}


- (void)demo                        {
        context          = UIGraphicsGetCurrentContext();
        CGContextSetLineWidth(context, 0.5);

        uberX           =   120;
        uberY           =   160;
        uberRadius      =   30;
        sectors         =   16;
        uberAngle       =   (2.0 * PI) / sectors;

        dotRadius       =   20;
        dotsFilled      =   FALSE;

        for (dotCount   = 1; dotCount <= sectors; dotCount++)
        {
            // Create a new iOSCircle Object
            iOSCircle *newCircle    = [[iOSCircle alloc] init];
            newCircle.circleRadius  = dotRadius;
            [self setSectorDotCoordinates];        // make new point for each dot
            dotPosition             = CGPointMake(x,y);         // create each dot

            NSLog(@"Circle%i: %@", dotCount, NSStringFromCGPoint(dotPosition));

            [self autoLabel];                      // text hides behind the dots

            newCircle.circleCentre  = dotPosition; // place each dot on the frame
            [totalCircles addObject:newCircle];
            [self setNeedsDisplay];
        }

        CGContextSetShadowWithColor(context, CGSizeMake(-3 , 2), 4.0, [UIColor clearColor].CGColor);
        dotCount = 1;
        for (iOSCircle *circle in totalCircles) {
            CGContextAddArc(context, circle.circleCentre.x, circle.circleCentre.y, circle.circleRadius, 0.0, M_PI * 2.0, YES);               // draw the circles

            NSLog(@"Dot %i Filled %i ", dotCount, dotsFilled);
            switch (dotsFilled) {
                case 1:
                    CGContextSetFillColorWithColor(context, [[UIColor cyanColor] CGColor]);
                    //CGContextDrawPath(context, kCGPathFillStroke);
                    break;
                    default:
                    //CGContextStrokePath(context);    // draw dot outline
                    break;
            }
            dotCount++;
        }
    }   //  draw circular dots in circular patterns

- (void)setSectorDotCoordinates     {

    x           = uberX + (uberRadius * cos(uberAngle *dotCount) * 2);
    y           = uberY + (uberRadius * sin(uberAngle *dotCount) * 2);    
}   // calculate dot coordinates along a circular arc

- (void)autoLabel {
    CGContextSetStrokeColorWithColor(context, [[UIColor blackColor] CGColor]);
    boxBoundary = CGRectMake(x-dotRadius, y-dotRadius, x+dotRadius, y+dotRadius);
    [[NSString stringWithFormat:@"%i",dotCount] drawInRect:boxBoundary withFont:[UIFont systemFontOfSize:24] lineBreakMode:NSLineBreakByCharWrapping alignment:NSTextAlignmentCenter];
}
Butacaine answered 27/1, 2015 at 7:32 Comment(1)
Please check your code formattingWolfhound
S
1

Change the boxBoundary in autoLabel, CGRectMake creates a rectangle with one point coordinates and width and height, not two points:

(void)autoLabel { 
 CGContextSetStrokeColorWithColor(context, [[UIColor blackColor] CGColor]);
 boxBoundary = CGRectMake(x-dotRadius, y-dotRadius, dotRadius*2, dotRadius*2); 
 [[NSString stringWithFormat:@"%i",dotCount] drawInRect:boxBoundary 
                                             withFont:[UIFont systemFontOfSize:24] 
                                        lineBreakMode:NSLineBreakByCharWrapping alignment:NSTextAlignmentCenter]; 
}

In your code the "boxes" containing the texts where bigger and bigger when you where going to the right. (the width and height were not fixed)

Sharpie answered 27/1, 2015 at 7:55 Comment(5)
Of course my misintepretation meant that the sides of the boxes are no longer equal when they move. That's clearly the cause of the problem. Thanks.Butacaine
actually I should accept this answer because images and labels are aligned. The problem now is how to keep the label on top and not hidden by the imageButacaine
The text is behind the dots because you are drawing them in that order. Draw first all the circles, and then draw the text.Sharpie
see my update. Apple’s CGReference is comprehensive but there are many rabbit holes and I wonder which one will I go down next ? Can you please tell me the kind of context reference I should construct - CGContextAddArc, CGContextAddArcToPoint, CGContextAddCurveToPoint etc. to improve on my answer below. I hope to render several views using this drawing process and in the light of [David Rönnqvist’s answer to another post][2] - can you advise me whether should I be even use CGrect at all ? Thanks. [2]: #7991586Butacaine
my answer below was deleted. The improved code with several issues fixed is given as background to a related question. This can be found at #28262830Butacaine
B
0

My updated code show labels that match the drawing order but text is still hidden when dots are filled.

Here

I suspect I need to construct a path to write text in front of the dots and it’s already apparent that something like CGPathMoveToPoint is needed to start drawing from the 12 O'clock position.

Here’s the updated code. The first part draws and renders the dots

    context          = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, 0.5);

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

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

    dotRadius       =   20;
    dotsFilled      =   FALSE;

    textOffset      =   4;      // add to y to centre the label

    for (dotCount   = 1; dotCount <= 4 /*sectors*/; dotCount++)
    {
        // Create a new iOSCircle Object
        iOSCircle *newCircle    = [[iOSCircle alloc] init];
        newCircle.circleRadius  = dotRadius;
        [self setSectorDotCoordinates];                     // create a new point for each dot
        dotPosition             = CGPointMake(x,y);         // create each dot

        NSLog(@"Circle%i: %@", dotCount, NSStringFromCGPoint(dotPosition));

        newCircle.circleCentre  = dotPosition;              // place each dot on the frame
        [totalCircles addObject:newCircle];
        [self setNeedsDisplay];
    }

    CGContextSetShadowWithColor(context, CGSizeMake(-3 , 2), 4.0, [UIColor clearColor].CGColor);
    dotCount = 1;
    for (iOSCircle *circle in totalCircles) {

        CGContextAddArc(context, circle.circleCentre.x, circle.circleCentre.y, circle.circleRadius, 0.0, M_PI * 2.0, YES);
        // draw the circles

        NSLog(@"Dot %i Filled %i ", dotCount, dotsFilled);
        switch (dotsFilled) {
            case 1:
                CGContextSetFillColorWithColor(context, [[UIColor cyanColor] CGColor]);
                CGContextDrawPath(context, kCGPathFillStroke);
                break;
                default:
                CGContextStrokePath(context);      // draw dot outline
                break;
        }

        [self setSectorDotCoordinates];             // find point coordinates for each dot

        dotCount++;
    }

The code that draws the labels follow immediately afterwards.

    // draw labels

for (dotCount   = 1; dotCount <= sectors; dotCount++)
{
    // Create a new iOSCircle Object
    iOSCircle *newCircle    = [[iOSCircle alloc] init];
    newCircle.circleRadius  = dotRadius;
    [self setSectorDotCoordinates];                 // find point coordinates for each dot

    dotPosition             = CGPointMake(x,y);     // use point coordinates for label
    [self autoLabel];                               // THIS SHOWS TEXT BEHIND THE DOTS
}
Butacaine answered 29/1, 2015 at 0:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.