views:

594

answers:

3

I've got an NSImage being drawn on a subclass of NSView. In a previous question, I was helped to draw it upright in the upper left corner. Now, I want to be able to rotate the image. I have a button that increments a rotation variable %4, and then I multiply this by -90 to find the rotation angle. I then use an NSAffineTransform to rotate the image, and translate it back onto the screen. However, it doesn't seem to be working the way I expect it to. I have two problems.

1) When I rotate, the portion of the image that is in an area that wasn't in the previous frame gets drawn correctly. However, the potion that was previously there remains as the original image. This means that after several rotations, there's a square of the original upright image and then a rectangle below or to the left of rotate image.

2) When I re-size the window, the image redraws (as it should) in the original upright orientation (as it should not).

Basically, I'm coming to the conclusion that NSAffineTransform doesn't work the way I think it does. Is there some other way to rotate (and translate) the image? thanks

large code chunk: (code between "WORKS" and "end works" is working code to just draw the image. It came from my previous question).

[OLD CODE DELETED, replaced lower with new code]

thanks

EDIT: A little more research finds that instead of doing an NSAffineTtransform I can rotate the view. This seems to work better. However, I can't get the translation to work quite right. new code below (original code deleted to save space)

- (void)drawRect:(NSRect)rect
{

//WORKS
    NSRect frame;
    frame.origin = NSZeroPoint;
    frame.size = [image size];
// end works

    float deltaX, deltaY, height, width;

// if the rotate button was just clicked, we need to rotate by 90 deg, otherwise not
    double rotateDeg = justRot ? -90.0 : 0;

    justRot = NO;

// rotate

    deltaX = 0;
    deltaY = 0;

// translate to account for rotation

    height = [image size].height;
    width = [image size].width;

    switch (rotation)
    {
     case 0:
      NSLog(@"No rotation ");
      break;
     case 1: 
      deltaX += width;
      break;
     case 2:
      deltaX += width;
      deltaY +=  height; 
      break;
     case 3:
      deltaY += height;
      break;
    }


    NSPoint orig;
    if (rotation != 0)
    {
     orig.x = -100;
     orig.y = -100;
    }
    [self rotateByAngle: rotateDeg];
    NSLog(@"orig %f %f", orig.x, orig.y);
//  WORKS
    [self setFrame: frame];
    [image drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1];
// end works
    [self translateOriginToPoint: orig];    
}
+1  A: 

I'm guessing that you'd like to rotate the image about it's center. If so you need to translate the origin of the affine transform to the center of the image, then rotate, then translate back. Now if you have artifacts of the previos position left over, it's probably because you didn't call -[NSView setNeedsDisplayInRect:] with the correct rectangles. Remember that you need to invalidate both the images previous and new positions.

Update: I just noticed that you're changing the view's frame from within drawRect:. That's too late to change the frame. Instead, draw the image in the correct location without changing the view's frame.

Jon Hess
Well, rather than translate the image, then translate it back, it's easier to just translate the rotated image...I'm doing a straight forward "setNeedsDisplay" without rect, so it should invalidate the whole thing, no? Changing the views frame is just so that it's the right shape. that way when I change the size of the window it knows where to create scroll bars...
Brian Postow
I don't think I said it strongly enough. I don't think changing a view's frame during draw rect is supported. The best supporting evidence I can find is here: http://developer.apple.com/releasenotes/Cocoa/AppKit.html. See the notes about the view will draw method. The go out of their way to say that changing the frame during viewWillDraw is supported, which to me implies that changing it during drawRect: is not supported.
Jon Hess
hrm, well, it seems to work for the purposes of changing the SHAPE of the frame, so that the scroll bars come up correctly... but I could put that into the rotate method instead... it doesn't seem to help any...
Brian Postow
Changing the frame during -drawRect: is definitely not supported. -drawRect: can be sent anytime, and you don't want it to have any side effects.
NSResponder
+2  A: 

Ok, for history, in case anyone else has this question here's the answer I've come up with:

The frame rotation stays in the drawRect, everything else moves to the rotate method:

-(void)rotate
{
    float deltaX, deltaY, height, width;

    rotation = (rotation +1) % 4 ;

    deltaX = 0;
    deltaY = 0;

// translate to account for rotation

    height = [image size].height;
    width = [image size].width;

    switch (rotation)
    {
     case 0:
      NSLog(@"No rotation ");
      deltaY -= width;
     break;
     case 1: 
      deltaY -= height;
      break;
     case 2:
      deltaX += height-width;
      deltaY -= height ; 
      break;
     case 3:
      deltaX += height-width;
      deltaY -= width;
      break;
    }

    NSPoint orig;
    orig.x = deltaX;
    orig.y = deltaY;

    [self rotateByAngle: 90.0];
    [self translateOriginToPoint: orig];

    [self setNeedsDisplay:YES];
}
Brian Postow
A: 

Have a look at my old Transformed Image sample code at: http://developer.apple.com/mac/library/samplecode/Transformed%5FImage/index.html

NSResponder