objective-ccore-graphicsboxing

How to form CGPoint array in Objective-C?


I want to get this structure:

CGPoint addLines1[] =
{
    CGPointMake(30.0, 150.0),
    CGPointMake(41.67, 145.19),
    CGPointMake(53.33, 103.25),
    CGPointMake(65.0, 131.67),
    CGPointMake(76.67, 106.11),
    CGPointMake(88.33, 110.20),
    CGPointMake(100.0, 111.54),
    CGPointMake(111.67, 112.13),
    CGPointMake(123.33, 115.66),
    CGPointMake(135.0, 123.7),
    CGPointMake(146.67, 125.53),
    CGPointMake(158.33, 115.1),
    CGPointMake(170.0, 69.38),
    CGPointMake(181.67, 112.47),
    CGPointMake(193.33, 65.1),
    CGPointMake(205.0, 103.33),
    CGPointMake(216.67, 92.6),
    CGPointMake(228.33, 54.76),
    CGPointMake(240.0, 79.66),
    CGPointMake(251.67, 53.81),
    CGPointMake(263.33, 56.81),
    CGPointMake(275.0, 88.19),
    CGPointMake(286.67, 74.81),
    CGPointMake(298.33, 28.1),
    CGPointMake(310, 20.0),
};

In order to make some calculations and draw data.

I have CGPoint *lines = appDelegate.averageResponseTimePoints;

How to make array addLines[] from *lines?


Solution

  • C arrays are not really arrays at run time, where they are just pointers to a contiguous block of objects of the same type. When you see items[n], that's just syntactic sugar for *(items+n).

    In your example addLines[1] would be *(lines+1) and addLines[0] would be *(lines+0), which is *lines. So, addLines is just lines without the pointer dereference. *lines is the first item in the array and lines is the whole array.

    Arrays have some differences to pointers at compile time. For example, sizeof(addLines) would give you the size of the whole array.

    Array-ness is lost as soon as you pass the array somewhere where it's size might be variable, but you can still use the subscript operator. For example:

    #include <Foundation/Foundation.h>
    
    #define showsize( expr ) ( printf(#expr " = %zd\n", ( expr ) ) )
    
    CGPoint *
    pass_back(CGPoint points[4])
    {
        showsize(sizeof(points));
        return points;
    }
    
    int
    main(void)
    {
        CGPoint square[] = {CGPointMake(-1.0,  1.0),
                            CGPointMake( 1.0,  1.0),
                            CGPointMake( 1.0, -1.0),
                            CGPointMake(-1.0, -1.0)};
        CGPoint* returned;
        int i;
    
        showsize(sizeof(CGPoint));
        showsize(sizeof(CGPoint*));
        showsize(sizeof(square));
        returned = pass_back(square);
        showsize(sizeof(returned));
    
        for (i = 0; i < 4; ++i) {
            printf("returned[%d] = {%0.1f, %0.1f}\n", i, (float) returned[i].x,
                                                     (float) returned[i].y);
        }
    
        return 0;
    }
    

    This outputs the following on my Mac:

    sizeof(CGPoint) = 8
    sizeof(CGPoint*) = 4
    sizeof(square) = 32
    sizeof(points) = 4
    sizeof(returned) = 4
    returned[0] = {-1.0, 1.0}
    returned[1] = {1.0, 1.0}
    returned[2] = {1.0, -1.0}
    returned[3] = {-1.0, -1.0}
    

    Here, square is the size of four CGPoints, but once sent to the pass_back function, it's only the size of a pointer, because that's what it is. When the pointer comes back (and named returned) it can still be used like an array.

    Note the magic number 4 in the loop. The pointer doesn't know the length of the array it's pointing to.

    Arrays cannot be reassigned with the = operator. If you really must populate addLines with the points from lines, you can do that with something like the following:

    memcpy(addLines, lines, sizeof(CGPoint) * numberOfPoints);
    

    You'll have to get numberOfPoints from somewhere, and addLines will have to be large enough to handle those points. That's okay if the number of points is a constant, but it would be bad if the number of points can vary at run time, especially if the points come from the outside world (think arbitrary code execution).

    I'd change averageResponseTimePoints to return an NSArray rather than a C-style array. You'll need to encapsulate the CGPoints in objects - either your own object or NSValues.

    Here's an example of how you could write averageResponseTimePoints:

    - (NSArray*) averageResponseTimePoints
    {
        NSMutableArray* result = [[[NSMutableArray alloc] init] autorelease];
    
        for (int i = 0; i < numberOfPoints; ++i) {
            NSValue* point = [NSValue value:points+i
                               withObjCType:@encode(CGPoint)];
            [result addObject:point];
        }
    
        return result;
    }
    

    If your code runs with CocoaTouch, you can use this to create the point value instead:

    NSValue* point = [NSValue valueWithCGPoint:points[i]];
    

    To get the CGPoints out of the array, you could write something like this:

    for (NSValue* value in result) {
        NSPoint pt;
        [value getValue:&pt];
        NSLog(@"%f %f", pt.x, pt.y);
    }
    

    Or with CocoaTouch:

    CGPoint pt = [value CGPointValue];