ESoft
ESoft

Reputation: 862

Rotate rectangle around center

I am playing with Brad Larsen's adaption of the trackball app.
I have two views at a 60 degree angle to each other and was wondering how I get the rotation to be in the center of this (non-closed) rectangle?

In the images below I would have liked the rotation to take place all within the blue lines.

enter image description here enter image description here enter image description here

Code (modified to only rotate around x axis):

#import "MyView.h"

//=====================================================
// Defines
//=====================================================

#define DEGREES_TO_RADIANS(degrees) \
    (degrees * (M_PI / 180.0f))

//=====================================================
// Public Interface
//=====================================================

@implementation MyView

- (void)awakeFromNib
{
    transformed = [CALayer layer];
    transformed.anchorPoint = CGPointMake(0.5f, 0.5f);

    transformed.frame = self.bounds;
    [self.layer addSublayer:transformed];

    CALayer *imageLayer = [CALayer layer];
    imageLayer.frame = CGRectMake(10.0f, 4.0f, self.bounds.size.width / 2.0f, self.bounds.size.height / 2.0f);
    imageLayer.transform = CATransform3DMakeRotation(DEGREES_TO_RADIANS(60.0f), 1.0f, 0.0f, 0.0f);
    imageLayer.contents = (id)[[UIImage imageNamed:@"IMG_0051.png"] CGImage];
    imageLayer.borderColor = [UIColor yellowColor].CGColor;
    imageLayer.borderWidth = 2.0f;
    [transformed addSublayer:imageLayer];

    imageLayer = [CALayer layer];
    imageLayer.frame = CGRectMake(10.0f, 120.0f, self.bounds.size.width / 2.0f, self.bounds.size.height / 2.0f);
    imageLayer.transform = CATransform3DMakeRotation(DEGREES_TO_RADIANS(-60.0f), 1.0f, 0.0f, 0.0f);
    imageLayer.contents = (id)[[UIImage imageNamed:@"IMG_0089.png"] CGImage];
    imageLayer.borderColor = [UIColor greenColor].CGColor;
    imageLayer.borderWidth = 2.0f;

    transformed.borderColor = [UIColor whiteColor].CGColor;
    transformed.borderWidth = 2.0f;
    [transformed addSublayer:imageLayer];

    UIView *line = [[UIView alloc] initWithFrame:CGRectMake(0, self.bounds.size.height / 2.0f, self.bounds.size.width, 2)];
    [line setBackgroundColor:[UIColor redColor]];
    [self addSubview:line];

    line = [[UIView alloc] initWithFrame:CGRectMake(0, self.bounds.size.height * (1.0f / 4.0f), self.bounds.size.width, 2)];
    [line setBackgroundColor:[UIColor blueColor]];
    [self addSubview:line];

    line = [[UIView alloc] initWithFrame:CGRectMake(0, self.bounds.size.height * (3.0f / 4.0f), self.bounds.size.width, 2)];
    [line setBackgroundColor:[UIColor blueColor]];
    [self addSubview:line];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    previousLocation = [[touches anyObject] locationInView:self];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    CGPoint location = [[touches anyObject] locationInView:self];
    //location = CGPointMake(previousLocation.x, location.y);

    CATransform3D currentTransform = transformed.sublayerTransform;

    //CGFloat displacementInX = location.x - previousLocation.x;
    CGFloat displacementInX = previousLocation.x - location.x;
    CGFloat displacementInY = previousLocation.y - location.y;

    CGFloat totalRotation = sqrt((displacementInX * displacementInX) + (displacementInY * displacementInY));

    CGFloat angle = DEGREES_TO_RADIANS(totalRotation);
    CGFloat x = ((displacementInX / totalRotation) * currentTransform.m12 + (displacementInY/totalRotation) * currentTransform.m11);

    CATransform3D rotationalTransform = CATransform3DRotate(currentTransform, angle, x, 0, 0);

    previousLocation = location;
    transformed.sublayerTransform = rotationalTransform;
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
}

- (void)dealloc {
  [super dealloc];
}

@end

Upvotes: 10

Views: 1194

Answers (3)

LK__
LK__

Reputation: 6723

You need to set the imageLayer.zPosition of each of the triangle sides to the distance from the center of the triangle (which is an equilateral triangle in your case).

If sideHeight = self.bounds.size.height / 2.0f;
Then distanceFromCenter = ((sideHeight / 2.0f) / sqrt(3));

Also, when setting the rotation on the sides, you need to move them into their required positions (in your code they are hardcoded).

Updated Code

- (void)awakeFromNib
{
    transformed = [CALayer layer];
    transformed.anchorPoint = CGPointMake(0.5f, 0.5f);

    transformed.frame = self.bounds;
    [self.layer addSublayer:transformed];

    CGFloat sideHeight = self.bounds.size.height / 2.0f;
    CGFloat distanceFromCenter = ((sideHeight / 2.0f) / sqrt(3));

    CGFloat sideC = sideHeight / 2.0f;
    CGFloat sideA = sideC / 2.0f;
    CGFloat sideB = (sqrt(3) * sideA);

    CALayer *imageLayer = [CALayer layer];
    imageLayer.frame = CGRectMake(10.0f, (self.bounds.size.height / 2.0f) - (sideHeight / 2.0f), self.bounds.size.width / 2.0f, sideHeight);
    imageLayer.transform = CATransform3DConcat(CATransform3DMakeRotation(-DEGREES_TO_RADIANS(60.0f), 1.0f, 0.0f, 0.0f),
                                               CATransform3DMakeTranslation(0, -sideA, -sideB));
    imageLayer.contents = (id)[[UIImage imageNamed:@"IMG_0051.png"] CGImage];
    imageLayer.borderColor = [UIColor yellowColor].CGColor;
    imageLayer.borderWidth = 2.0f;
    imageLayer.zPosition = distanceFromCenter;
    [transformed addSublayer:imageLayer];

    imageLayer = [CALayer layer];
    imageLayer.frame = CGRectMake(10.0f, (self.bounds.size.height / 2.0f) - (sideHeight / 2.0f), self.bounds.size.width / 2.0f, sideHeight);
    imageLayer.transform = CATransform3DConcat(CATransform3DMakeRotation(DEGREES_TO_RADIANS(60.0f), 1.0f, 0.0f, 0.0f),
                                               CATransform3DMakeTranslation(0, sideA, -sideB));
    imageLayer.contents = (id)[[UIImage imageNamed:@"IMG_0089.png"] CGImage];
    imageLayer.borderColor = [UIColor greenColor].CGColor;
    imageLayer.zPosition = distanceFromCenter;
    imageLayer.borderWidth = 2.0f;

    transformed.borderColor = [UIColor whiteColor].CGColor;
    transformed.borderWidth = 2.0f;
    [transformed addSublayer:imageLayer];

    UIView *line = [[UIView alloc] initWithFrame:CGRectMake(0, self.bounds.size.height / 2.0f, self.bounds.size.width, 2)];
    [line setBackgroundColor:[UIColor redColor]];
    [self addSubview:line];

    line = [[UIView alloc] initWithFrame:CGRectMake(0, self.bounds.size.height * (1.0f / 4.0f), self.bounds.size.width, 2)];
    [line setBackgroundColor:[UIColor blueColor]];
    [self addSubview:line];

    line = [[UIView alloc] initWithFrame:CGRectMake(0, self.bounds.size.height * (3.0f / 4.0f), self.bounds.size.width, 2)];
    [line setBackgroundColor:[UIColor blueColor]];
    [self addSubview:line];
}

Upvotes: 2

signal
signal

Reputation: 225

you should make your imageView superView to show the perspective :

CATransform3D tranfrom = CATransform3DIdentity;
tranfrom.m34 = -1.0 / z;
imageView.superview.layer.transform = transfrom;

Upvotes: 0

MrTJ
MrTJ

Reputation: 13192

Use also other transformation (I think you'll need translation) and concatenate them with rotation. This example even scales the image, so you get a scaled, shifted and rotated layer:

CATransform3D translate = CATransform3DMakeTranslation(yourShiftByX, yourShiftByY, 0);
CATransform3D scale = CATransform3DMakeScale(yourRateX, yourRateY, 1);
CATransform3D rotate = CATransform3DMakeRotation(DEGREES_TO_RADIANS(60.0f), 1.0, 0.0, 0.0);
CATransform3D transform = CATransform3DConcat(CATransform3DConcat(rotate, scale), translate);
// the order is important: FIRST we rotate, THEN we scale and AT LAST we position the object

// now apply 'transform' to your layer

Upvotes: 1

Related Questions