Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detecting collisions between rotated UIViews

I have two UIViews, one of which is rotated every .01 second using the following code:

    self.rectView.transform = CGAffineTransformRotate(self.rectView.transform, .05);

Now, I want to be able to tell if another UIView, called view, intersects rectView. I used this code:

if(CGRectIntersectsRect(self.rectView.frame, view.frame)) {
    //Intersection
}

There is a problem with this, however, as you probably know. Here is a screenshot: enter image description here

In this case, a collision is detected, even though obviously they are not touching. I have looked around, but I cannot seem to find real code to detect this collision. How can this be done? Working code for detecting the collision in this case would be greatly appreciated! Or maybe would there be a better class to be using other than UIView?

like image 295
Samuel Noyes Avatar asked Nov 30 '25 03:11

Samuel Noyes


1 Answers

when you rotate a view, its bounds won't change but its frame changes.

So, for my view with backgroundColor blue,
the initial frame i set to was

frame = (30, 150, 150, 35);
bounds={{0, 0}, {150, 35}};

but after rotating by 45 degree, the frame changed to

frame = (39.5926 102.093; 130.815 130.815);
bounds={{0, 0}, {150, 35}};

screenshot of running app showing frame of blue view with black border

Because the frame always return the smallest enclosing rectangle of that view.

So, in your case, even-though it looks both views are not intersecting,their frames intersect.

To solve it you can use, separating axis test. If you want learn on it, link here

I tried to solve it and finally got the solution. If you like to check, below is the code. Copy paste the below code into an empty project to check it out.

In .m file

@implementation ViewController{
    UIView *nonRotatedView;
    UIView *rotatedView;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    nonRotatedView =[[UIView alloc] initWithFrame:CGRectMake(120, 80, 150, 40)];
    nonRotatedView.backgroundColor =[UIColor blackColor];
    [self.view addSubview:nonRotatedView];

    rotatedView =[[UIView alloc] initWithFrame:CGRectMake(30, 150, 150, 35)];
    rotatedView.backgroundColor =[UIColor blueColor];
    [self.view addSubview:rotatedView];
    CGAffineTransform t=CGAffineTransformMakeRotation(M_PI_4);
    rotatedView.transform=t;

    CAShapeLayer *layer =[CAShapeLayer layer];
    [layer setFrame:rotatedView.frame];
    [self.view.layer addSublayer:layer];
    [layer setBorderColor:[UIColor blackColor].CGColor];
    [layer setBorderWidth:1];

    CGPoint p=CGPointMake(rotatedView.bounds.size.width/2, rotatedView.bounds.size.height/2);

    p.x = -p.x;p.y=-p.y;
    CGPoint tL =CGPointApplyAffineTransform(p, t);
    tL.x +=rotatedView.center.x;
    tL.y +=rotatedView.center.y;

    p.x = -p.x;
    CGPoint tR =CGPointApplyAffineTransform(p, t);
    tR.x +=rotatedView.center.x;
    tR.y +=rotatedView.center.y;

    p.y=-p.y;
    CGPoint bR =CGPointApplyAffineTransform(p, t);
    bR.x +=rotatedView.center.x;
    bR.y +=rotatedView.center.y;

    p.x = -p.x;
    CGPoint bL =CGPointApplyAffineTransform(p, t);
    bL.x +=rotatedView.center.x;
    bL.y +=rotatedView.center.y;


    //check for edges of nonRotated Rect's edges
    BOOL contains=YES;
    CGFloat value=nonRotatedView.frame.origin.x;
    if(tL.x<value && tR.x<value && bR.x<value && bL.x<value)
        contains=NO;
    value=nonRotatedView.frame.origin.y;
    if(tL.y<value && tR.y<value && bR.y<value && bL.y<value)
        contains=NO;
    value=nonRotatedView.frame.origin.x+nonRotatedView.frame.size.width;
    if(tL.x>value && tR.x>value && bR.x>value && bL.x>value)
        contains=NO;
    value=nonRotatedView.frame.origin.y+nonRotatedView.frame.size.height;
    if(tL.y>value && tR.y>value && bR.y>value && bL.y>value)
        contains=NO;

    if(contains==NO){
        NSLog(@"no intersection 1");
        return;
    }
    //check for roatedView's edges
    CGPoint rotatedVertexArray[]={tL,tR,bR,bL,tL,tR};

    CGPoint nonRotatedVertexArray[4];
    nonRotatedVertexArray[0]=CGPointMake(nonRotatedView.frame.origin.x,nonRotatedView.frame.origin.y);
    nonRotatedVertexArray[1]=CGPointMake(nonRotatedView.frame.origin.x+nonRotatedView.frame.size.width,nonRotatedView.frame.origin.y);
    nonRotatedVertexArray[2]=CGPointMake(nonRotatedView.frame.origin.x+nonRotatedView.frame.size.width,nonRotatedView.frame.origin.y+nonRotatedView.frame.size.height);
    nonRotatedVertexArray[3]=CGPointMake(nonRotatedView.frame.origin.x,nonRotatedView.frame.origin.y+nonRotatedView.frame.size.height);

    NSInteger i,j;
    for (i=0; i<4; i++) {
        CGPoint first=rotatedVertexArray[i];
        CGPoint second=rotatedVertexArray[i+1];
        CGPoint third=rotatedVertexArray[i+2];
        CGPoint mainVector =CGPointMake(second.x-first.x, second.y-first.y);
        CGPoint selfVector =CGPointMake(third.x-first.x, third.y-first.y);
        BOOL sign;
        sign=[self crossProductOf:mainVector withPoint:selfVector];
        for (j=0; j<4; j++) {
            CGPoint otherPoint=nonRotatedVertexArray[j];
            CGPoint otherVector = CGPointMake(otherPoint.x-first.x, otherPoint.y-first.y);
            BOOL checkSign=[self crossProductOf:mainVector withPoint:otherVector];
            if(checkSign==sign)
                break;
            else if (j==3)
                contains=NO;
        }
        if(contains==NO){
            NSLog(@"no intersection 2");
            return;
        }
    }
    NSLog(@"intersection");
}


-(BOOL)crossProductOf:(CGPoint)point1 withPoint:(CGPoint)point2{
    if((point1.x*point2.y-point1.y*point2.x)>=0)
         return YES;
    else
        return NO;
}

Hope this helps.

like image 107
santhu Avatar answered Dec 02 '25 20:12

santhu



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!