2

I am creating multiple custom UIView's in a custom UIView. The creation of the custom sub-views is ok. They look like this:

enter image description here

The draw method is quite straightforward:

[[UIColor brownColor] set];

CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(ctx,
                      5.0f);
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, 0.0f, 0.0f);
CGContextAddLineToPoint(ctx, 100.0f, 0.0);
CGContextAddLineToPoint(ctx, 130.0f, 25.0f);
CGContextAddLineToPoint(ctx, 100.0f, 50.0f);
CGContextAddLineToPoint(ctx, 0.0f, 50.0f);
CGContextClosePath(ctx);
CGContextStrokePath(ctx);
[super drawRect:rect];

Adding it to the super view is also quite simple:

    ITContextFigure *view = [[ITContextFigure alloc] initWithFrame:CGRectMake(location.x, location.y, 135.0f, 50.0f)];
    [view setBackgroundColor:[UIColor yellowColor]];
    [self addSubview:view];

So my questions are:

1) How can I detect when one overlaps the other?

I saw this solution:

if (CGRectContainsRect([myImageView1 frame], [myImageView2 frame])) {
        NSLog(@"Overlaped, it's working!");
}

But if I have multiple UIViews, doing a for on the super view and checking every single sub-view doesn't seem a good solution for me.

2) In this case, what can be done?

enter image description here

My main goal is to detect when this happens:

enter image description here


Update 1.0

Going to try what has been showed here, since there isn't a more elegant way. If I am able to achieve it, I will post the code on Github, if anyone needs it.

Community
  • 1
  • 1
Rui Peres
  • 25,741
  • 9
  • 87
  • 137
  • I think you may want to search for algorithm to detect intersecting polygon (which involves brute-force every pair of line segments and line segment intersection). – nhahtdh Jul 13 '12 at 15:12
  • I'm guessing that your case in 2) is that the bounding rectangles overlap, but the primary content of the view doesnt? – Dan F Jul 13 '12 at 15:12
  • @nhahtdh not always a brute force, you can do separating axis theorem for detecting intersections on arbitrary (convex) polygons – Dan F Jul 13 '12 at 15:13
  • @DanF Yes exactly. nhahtdh That's what I am trying to avoid. – Rui Peres Jul 13 '12 at 15:13
  • @DanF: I agree that might help, but will it be any better than brute force in this case? – nhahtdh Jul 13 '12 at 15:15
  • @JackyBoy You are going to need to do some collision checks basically. Thankfully, though, this is a very known problem. Try doing some research on arbitrary 2d polygon intersection, like I mentioned, there is the separating axis theorem – Dan F Jul 13 '12 at 15:15
  • @DanF thought that iOS would have an elegant solution for this kind of problem... – Rui Peres Jul 13 '12 at 16:53
  • It is possible that it contains some interface for detecting overlaps, I do not know of any, but I haven't really tried all that hard to look – Dan F Jul 13 '12 at 17:22
  • Are these the only shapes that will occur or will there be other shapes too? Roughly how many polygons do you think will be on the screen at once? -- Given the answers to these two questions I think I may be able to help you out! – idz Jul 13 '12 at 22:09
  • Yes it will be mainly that, some arrows other rectangle shaped figures , and some circles, and that's it. – Rui Peres Jul 14 '12 at 11:56

1 Answers1

3

You can dramatically cut down on the number of collision detections you need to do by cleverly sorting your data (these are called scan line or seep line algorithms). Here's an outline of how you might apply this technique to your situation.

Sort your subviews into an array ordered by ascending y. If two subviews share the same y order them by ascending x. This is your inactive list and this forms the main input to the algorithm.

The algorithm proceeds as follows.

  1. While there are inactive subviews, choose an active_y. This is the y coordinate of the first subview on the inactive list.

  2. Move all subviews with origins on the active_y line to a working list, sorted by ascending x. This is the active list.

  3. Run through the active list collision testing each each subview with subsequent ones on the list. You do this using two indices into the list (let's call them left and right). As soon as you see a right subview that cannot intersect with the left you can advance the left index.

  4. While doing the collision detection you also check to see if a subview is now completely below the active_y. Once it is, you should remove it from the active list.

The algorithm completes when all the subviews on the inactive list have been consumed and the final run through the active list completed.

This algorithm greatly cuts down on the number of collision detections you will need to perform and is roughly O(n log n), but it can also simplify the collision detection itself.

Since the active list is sorted left to right you always know when you are doing you detection routine which one is on the left and which is on the right. So, for example, when comparing the arrow shapes in your example you only need to check whether the the two leftmost vertices of the right shape fall within the left shape. You may find CGPathContainsPoint useful.

If the number of distinct shapes you are dealing with increases then you might need to consider pushing the collision detection into the scan line algorithm itself. This is a little trickier, but basically instead of the list holding subview pointers they would hold line segments that make up the shapes (excluding horizontal ones).

idz
  • 12,825
  • 1
  • 29
  • 40
  • idz is it possible to remove that extra yellow part? – Rui Peres Jul 19 '12 at 08:47
  • 1
    Yes, instead of setting the background color, fill the path with the yellow color, then stroke it with brown color. Set the background color of the `UIView` to `clearColor` and make sure the `opaque` property of the view is `NO`. Checkout `CGContextFillPath` and you may also need `CGContextSetRGBFillColor` (I can't remember whether setting via `UIColor` sets the fill color too). – idz Jul 19 '12 at 18:01