How do I rotate an image drawn directly to an NSView with DrawRect in an OSX app?

874 views Asked by At

I'm writing an OSX app in Xcode using Objective-C. I have a window, with an NSView inside it, and that NSView is supposed to use data from an NSMutableArray containing NSNumbers to draw corresponding images on a grid such that images are drawn at 0,0; 32,0; 64,0 . . . 0,32; 32,32; etc. Accordingly the array's count is the grid's W*H, in this case 21*21 or 441.

You left click to "place" an image, which really just means updating the array based on where you clicked and then calling setNeedsDisplay:YES so it redraws itself to reflect the updated array. So far, I can get it to draw images based on the array properly.

When you right click, though, it is supposed to rotate the image in the particular grid slot by a certain amount. The only problem I am having here is figuring out how to actually draw the rotated images, in their proper locations. They should rotate about their center points, which would be the relative coordinates of 16,16 (all images are 32x32 pixels in size). As it is, my code is:

- (void)drawRect:(NSRect)dirtyRect
{
    [super drawRect:dirtyRect];

    //Black background
    [[NSColor blackColor] setFill];
    NSRectFill(dirtyRect);

    // Drawing code here.
    NSRect rectToDraw = CGRectMake(0,0,32,32);
    NSInteger imageIndex = 0;
    NSImage *imageToDraw = nil;
    for (int i = 0; i < [objDataArray count]; i++) {
        //Loop through data array and draw each image where it should be
        if ([objDataArray objectAtIndex:i]==[NSNull null]) continue; //Don't draw anything in this slot

        //Math to determine where to draw based on the current for loop index
        //0 = 0,0; 1 = 32,0 . . . 20 = 640,0; 21 = 0,32; etc. (grid is 21x21)
        rectToDraw.origin.x = (i % 21)*32;
        rectToDraw.origin.y = floor(i/21)*32;

        //Get the data at this index in the data array
        imageIndex = [((NSNumber*)[objDataArray objectAtIndex:i]) integerValue];

        //Use the retrieved number to get a corresponding image
        imageToDraw = (NSImage*)[objImagesArray objectAtIndex:imageIndex];

        //Draw that image at the proper location
        [imageToDraw drawInRect:rectToDraw];
    }
}

So say that the amount of rotation in degrees is specified by the variable rotationAmount. How do I change the drawInRect line (the last line before the closing braces) so that the image draws at the proper location specified by rectToDraw, but rotated by rotationAmount degrees about its center?

Thanks.

1

There are 1 answers

1
Ken Thomases On BEST ANSWER

You don't draw the image rotated, as such. You transform the coordinate space and then draw the image.

[NSGraphicsContext saveGraphicsState];

NSAffineTransform* xform = [NSAffineTransform transform];

// Translate the image's center to the view origin so rotation occurs around it.
[xform translateXBy:-NSMidX(rectToDraw) yBy:-NSMidY(rectToDraw)];
[xform rotateByDegrees:rotationAmount];
[xform concat];

[imageToDraw drawInRect:NSOffsetRect(rectToDraw, -NSMidX(rectToDraw), -NSMidY(rectToDraw))];

[NSGraphicsContext restoreGraphicsState];

There's some chance that I have the transform backward. I always forget which way it goes (if it's transforming the view or the content). If your image goes off into never-never land, change the signs of the translation.