iPhone - intersection of rectangles - iphone

I have this NSMutableArray which is a collection of objects that are being moved on the screen. When an object intersects another one, I need to construct an array with this object intersected. If this object is by itself intersecting with another, this one must be included in that array and so on, recursively until I know all the objects intersecting with the object that was intersecting with the other and so on.
Example: I am moving object1 and I intersect object2, but object2 intersects object3 that intersects 4, that intersects 5 and so on.
I want to collect all these objects in one array.
What I did was this:
NSMutableArray *intersectingObjects = [NSMutableArray array];
for (Obj *oneObj in allObjects) {
if (oneObj != movingObject) {
if (CGRectIntersectsRect(movingObject.frame, oneObj)) {
[intersectingObjects addObject:oneObj];
}
}
}
// at this point I got an array of all objects intersecting with the
// moving object, then I created a similar block to
// test all these intersecting objects against all objects again,
// then I discovered the objects that were intersecting with the first block
The problem is this just gives me 2 levels deep.
How do I create a recursion here, that will go to the whole tree of possibilities?
thanks.

Because the 1 time calculation would be on the order of O(n^2), I would suggest maintaining an NSMutableArray for each object which contains the objects it is currently intersecting directly. Then the order for each new calculation changes to O(n), simply taking a union of the items in the tree.
However, if you'd still like to pursue the O(n^2) method, here's an example. I'm assuming Obj is a subclass of UIView?
- (void) addViewsWhichIntersectView:(Obj*)movingObject toArray:(NSMutableArray*) intersectingObjects
{
for (Obj *oneObj in allObjects)
{
if (movingObject != oneObj && //assuming you only care about address comparison, override isEqual and use that method otherwise
![intersectingObjects containsObject:oneObj) &&
CGRectIntersectsRect(movingObject.frame, oneObj.frame)
{
[intersectingObjects addObject:oneObj];
[self addViewsWhichIntersectView: oneObj toArray:intersectingObjects];
}
}
}
Then for the driver, just initialize a mutable array and pass in your reference to the original object.

[self intersectingObjects:allObjects withObject:movingObject];
- (NSMutableArray*) intersectingObjects:(NSArray*)objects withObject:(id)obj{
NSMutableArray * objectsToCheck = [NSMutableArray arrayWithArray:objects];
[objectsToCheck removeObject:obj];
NSMutableArray * intersectingWith = [NSMutableArray array];
for (id oneStepObj in objectsToCheck) {
if (CGRectIntersectsRect(obj.frame, oneStepObj)) {
//This object intersected with the provided object
[intersectingWith addObject:oneStepObj];
//Also add all the objects that intersect with oneStepObj, take care of duplicates
for(id nStepObj in [self intersectingObjects:objectsToCheck withObject:oneStepObj]){
if(![intersectingWith containsObject:nStepObj]){
[intersectingWith addObject:nStepObj];
}
}
}
}
}
return intersectingWith;
}

Here is a N^2 approach (good for small N):
*intersectingObjects = [NSMutableArray array];
for (Obj *oneObj in allObjects) {
for (Obj* twoObj in allObjects) {
if ( oneObj != twoObj ) {
if (CGRectIntersectsRect(movingObject.frame, oneObj)) {
[intersectingObjects addObject:oneObj];
}
}
}
}
To be faster, you'd have to do some indexing of some sort. Recursion isn't necessarily better here unless you have a data structure of objects indexed by location. But it takes work to maintain that index (typically when you update locations).

I wrote an app that does this pretty well, it is called QColor - send me a request for a promo code if you want to see it.
In my experience, the iPhone stopped updating live with an inefficient algorithm. Here is what I settled on (pseudocode - sorry, the full source has a lot of other stuff going on).
One thing to note, this algorithm keeps multiple overlapping rectangles so when you update the screen you need to bringSubviewToFront: on the intersections with the most rectangles.
NSArray intersections; // each Intersection is an array of rectangles with a calculated rect - contact me if you want code that can do this (it's not glorious).
- (void) addRect: newRect {
intersections addObject: newRect;
for (Intersection *intersection in intersections) {
if (intersection intersects newRect) {
create new intersection of intersection + newRect
// note, do NOT modify the intersection - add a NEW one. Important point.
}
}
}
- (void) removeRect: aRect {
remove every intersection that contains aRect - careful, don't use fast enumeration while editing the data structure.
}
- (void) moveRect: aRect {
for (Intersection *intersection in intersections) {
if (intersection contains aRect) {
recompute the intersection with the moved aRect. If ANY rectangle no longer intersects, delete the entire intersection (compare count before and after recalculation)
} else {
if (intersection intersects newRect) {
create new intersection of intersection + newRect
}
}
}
}
This is not as pretty as a recursive algorithm, but it's important to keep the total number of intersections low. Now I first tried this with an n! algorithm so of course it choked. The n^2 one above, I'm not sure if it will be adequate. As I understand my algorithm, each time through is order n though some worst case examples with everything overlapping (but not completely) could be n! (that's the data, not the algorithm).
I have some screenshots on my app page if you want to visualize the rectangles: http://itunes.apple.com/ph/app/qcolor/id490077718?mt=8
Gotta run - sorry for any typos!
Damien

Related

Determining the player who got connected in first place

In the game I'm working on I would like to give users the possibility to select between asynchronous and real-time turn-based matches. The one thing I'm missing from the latter is how do I know who's got the first turn. I'm trying to find a way to detect who's got connected first and set that player's turn automatically. Unfortunately, it seems that both players get connected at the same time since right after finding a match expectedPlayersCount yields 1 for both players and in the didChangeState event that same variable yields 0 for both of them too. So I have no way to tell who's got first to the match since it seems that it all happens simultaneously.
As a temporary fix, I'm just using their IDs. The one with the lowest ID is the one having the first turn. However, I'm trying to find a better way since with this approach player A will always get the first turn when playing against player B and that represents a small advantage in my game.
This method is what I call when starting a new game. I pass a realTime bool value to specify whether the game returned by the game center wrapper should be a GKMatch or GKTurnBasedMatch.
//Calculate the level group to find other players in the same group
NSInteger levelGroup = (self.player.levelValue / STLevelSkillInterval) * STLevelSkillInterval;
//Find a match for the local player
[self.gameCenter findMatchWithPlayerAttributes:levelGroup realTime:realTime group:0 onCompletion:^(GKTurnBasedMatch *turnMatch, GKMatch *realTimeMatch) {
[self handleTurnMatchFound:turnMatch realTimeMatch:realTimeMatch completionBlock:onCompletion];
}];
...
This method here is the responsible to handle game center response after finding match. The create and synchronize method stores a match instance in CoreData and also fills its data by fetching the corresponding info from Game Center and my backend. So if at the time of that's done the expected player count reached 0, I call the completion block immediately since the match can begin. Otherwise I just store the completion block so I can use it later when the other player connects to the match. The problem in that part is that it never reaches 0, not for any of the two players.
- (void)handleTurnMatchFound:(GKTurnBasedMatch *)turnMatch realTimeMatch:(GKMatch *)realTimeMatch completionBlock:(void(^)(STMatch *match, NSError *error))onCompletion
{
if (turnMatch != nil)
{
[self createAndSynchronizeLocalMatch:turnMatch onCompletion:^(STMatch *localMatch) {
onCompletion(localMatch, nil);
}];
}
else if (realTimeMatch != nil)
{
[self createAndSynchronizeRealTimeLocalMatch:realTimeMatch onCompletion:^(STMatch *localMatch) {
if (realTimeMatch.expectedPlayerCount == 0)
{
onCompletion(localMatch, nil);
}
else
{
self.findRealTimeMatchCompletionBlock = onCompletion;
}
}];
}
else
{
onCompletion(nil, nil);
}
}
...
This is the method that handles player's state changes. So basically here I just update the local instance of the real time match with values from the connected player and then I call the completion block stored earlier.
- (void)match:(GKMatch *)match player:(NSString *)playerID didChangeState:(GKPlayerConnectionState)state
{
if (state == GKPlayerStateConnected)
{
STMatchParticipant *placeholderParticipant = [self.realTimeMatch getMatchParticipantWithPlayerId:nil];
placeholderParticipant.playerId = playerID;
placeholderParticipant.statusValue = GKTurnBasedParticipantStatusActive;
//This will sync the information for the connected player only
[self syncAllPlayerInfoForRealTimeMatchOnCompletion:^(NSArray *players) {
self.findRealTimeMatchCompletionBlock(self.realTimeMatch, nil);
}];
}
else
{
//Notify the observers that the participant has disconnected from the match
STMatchParticipant *participant = [self.realTimeMatch getMatchParticipantWithPlayerId:playerID];
for (id<STTurnBasedMatchDelegate> observer in self.matchesObservers)
{
if ([observer respondsToSelector:#selector(realTimeMatch:participantDidDisconnect:)])
{
[observer realTimeMatch:self.realTimeMatch participantDidDisconnect:participant];
}
}
}
}
I would appreciate any comments.
Instead of trying to determine who got connected first, why don't you just have all the players pick a random number using arc4random ?
int randomNumber = arc4random%1000000;
The player with the biggest number can play first. You will have to make sure all the players send each other their random numbers so everyone can compare and decide who is first.
In the above example, the range would be upto 1 million, so the odds of two players picking the same random number is low.
If two players do pick the same random number, you could compute the hash of their playerId's, and have the player with the larger hash be first.
if ([GKLocalPlayer localPlayer].playerID.hash > otherPlayerId.hash )
The chances of a hash collision occurring are very low, since the playerId strings are short. You can still check for this collision and handle it appropriately (maybe by hashing again ;) ).

Writing a for loop in Objective C way

I have a doubts in writing a for loop in Objective C way. I can do the same loop in traditional C for loop, However I am trying to learn Objective C. Here is my question.
listdata is a mutable array holding objects of ofi_vc_modal_ab_user_info objects, I want to compare each email of list data with email until list data count and find its position and if found I want to delete the object from list data.
for (ofi_vc_modal_ab_user_info *loc_obj in listData)
{
if (strcasecmp(loc_obj->email, email) == 0) {
// What need to do here.
}
}
How to proceed here... thanks for your helps :)
you can just use C's for.
in fact, it's an error to mutate the collection you iterate over when using for (e in collection).
BOOL foundObject = NO;
ofi_vc_modal_ab_user_info *loc_found_obj = nil;
for (ofi_vc_modal_ab_user_info *loc_obj in listData)
{
if (strcasecmp(loc_obj->email, email) == 0) {
// Set your flag here
loc_found_obj = loc_obj;
foundObject = YES;
break;
}
}
if(foundObject) {
// Do your stuffs as object is found
// Your found object is in loc_found_obj
[listData removeObject:loc_found_obj];
}
I hope below code explains what you want. Please explain bit more if you need more help.
EDIT : If you are using NSMutableArray then you do not need index of the object. you can directly delete object as mentioned in my edited code.

Add object to sorted NSMutable array and answer index path

I have a sorted mutable array of a class called Topic. The topics represent a an array of Publications. I present the topics in a table, and periodically fetch new publications from a web service. When a new publication arrives, I'd like to add to the table with an animation.
What's bothering me is the computational work I need to do to add into this array, and answer the correct index path. Can someone suggest a more direct way than this:
// add a publication to the topic model. if the publication has a new topic, answer
// the index path of the new topic
- (NSIndexPath *)addPublication:(Publication *)pub {
// first a search to fit into an existing topic
NSNumber *topicId = [pub valueForKey:#"topic_id"];
for (Topic *topic in self.topics) {
if ([topicId isEqualToNumber:[topic valueForKey:"id"]]) {
// this publication is part of an existing topic, no new index path
[topic addPublication:pub];
return nil;
}
}
// the publication must have a new topic, add a new topic (and therefore a new row)
Topic *topic = [[Topic alloc] initWithPublication:publication];
[self.topics addObject:topic];
// sort it into position
[self.topics sortUsingSelector:#selector(compareToTopic:)];
// oh no, we want to return an index path, but where did it sort to?
// yikes, another search!
NSInteger row = [self.topics indexOfObject:topic];
return [NSIndexPath indexPathForRow:row inSection:0];
}
// call this in a loop for all the publications I fetch from the server,
// collect the index paths for table animations
// so much computation, poor user's phone is going to melt!
There's no getting around the first search, I guess. But is there some more efficient way to add a new thing to an array, maintaining a sort and remembering where it got placed?
It's pretty straightforward to insert a value into a sorted list. Think about how you would insert the number "3" into the list "1, 2, 7, 9", for instance. You want to do exactly the same thing.
Loop through the array by index, using a for loop.
For each object, use compareToTopic: to compare it to the object you want to insert.
When you find the appropriate index to insert at, use -[NSArray insertObject:atIndex:] to insert it.
Then return an NSIndexPath with that index.
Edit: and, as the other answers point out, a binary search would be faster -- but definitely trickier to get right.
This is almost certainly not an issue; NSArrays are actually hashes, and search is a lot faster than it would be for a true array. How many topics can you possibly have anyways?
Still, if you measure the performance and find it poor, you could look into using a B-tree; Kurt Revis commented below with a link to a similar structure (a binary heap) in Core Foundation: CFBinaryHeap.
Another option (which would also need to be measured) might be to do the comparison as you walk the array the first time; you can mark the spot and do the insertion directly:
NSUInteger insertIndex = 0;
NSComparisonResult prevOrder = NSOrderedDescending;
for (Topic *topic in self.topics) {
NSComparisonResult order = [topicId compareToTopic:topic];
if (NSOrderedSame == order) {
// this publication is part of an existing topic, no new index path
[topic addPublication:pub];
return nil;
}
else if( prevOrder == NSOrderedDescending &&
order == NSOrderedAscending )
{
break;
}
insertIndex++;
prevOrder = order;
}
Please note that I haven't tested this, sorry.
I'm not sure this is actually better or faster than the way you've written it, though.
Don't worry about the work the computer is doing unless it's demonstrably doing it too slowly.
What you have done is correct I guess. There's another way. You can write your own binary search implementation method. (Which has only few lines of code). And you can retrieve the index where the new object should fit in. And add the new object to the required index using insertObject:atIndex: method.

How to get high scores from OpenFeint?

In their support OpenFeint give you this, but I don't quite understand. How can I get the leaderboard data, say top 10 and show it in my own UI?
Original link: http://www.openfeint.com/ofdeveloper/index.php/kb/article/000028
[OFHighScoreService getPage:1 forLeaderboard:#"leaderboard_id_string" friendsOnly:NO silently:YES onSuccess:OFDelegate(self, #selector(_scoresDownloaded:)) onFailure:OFDelegate(self, #selector(_failedDownloadingScores))];
- (void)_scoresDownloaded:(OFPaginatedSeries*)page
{
NSMutableArray* highscores = nil;
if ([page count] > 0)
{
if ([[page objectAtIndex:0] isKindOfClass:[OFTableSectionDescription class]])
{
// NOTE: In the following line, we access "[page objectAtIndex:1]" to retrieve high scores from
// the global leaderboard. Using "[page objectAtIndex:0]" would retrieve scores just for the local player.
// Older versions of OpenFeint did not break this out into 2 sections.
highscores = [(OFTableSectionDescription*)[page objectAtIndex:1] page].objects;
}
else
{
highscores = page.objects;
}
}
for (OFHighScore* score in highscores)
{
// ...
}
}
- (BOOL)canReceiveCallbacksNow
{
return YES;
}
The code to request a page of high scores is the first line, i.e.:
[OFHighScoreService getPage:1 forLeaderboard:#"leaderboard_id_string" friendsOnly:NO silently:YES onSuccess:OFDelegate(self, #selector(_scoresDownloaded:)) onFailure:OFDelegate(self, #selector(_failedDownloadingScores))];
You put this line in the place where you want to start the query for high scores. You can change the page number as required. Once the page of high scores has been retrieved, the callback _scoresDownloaded is called. The example shows you how you would iterate through the OFHighScore objects in the highscores array. You would replace the comment // ... with your own code to show the scores to the player, or whatever.
(In case of error _failedDownloadingScores is called; you should implement that to show an error.)

How to make a big switch control structure with variable check values?

For example, I have a huge switch control structure with a few hundred checks. They're an animation sequence, which is numbered from 0 to n.
Someone said I can't use variables with switch. What I need is something like:
NSInteger step = 0;
NSInteger i = 0;
switch (step) {
case i++:
// do stuff
break;
case i++:
// do stuff
break;
case i++:
// do stuff
break;
case i++:
// do stuff
break;
}
The point of this is, that the animation system calls a method with this big switch structure, giving it a step number. I want to be able to simply cut-copy-paste large blocks and put them in a different position inside the switch. for example, the first 50 blocks to the end.
I could do that easily with a huge if-else structure, but it would look ugly and something tells me switch is much faster.
How to?
By not doing it this way. A case label has to be a constant.
Here's one way to do this that might be better:
Define a selector for each thing you want to do e.g.
-(void) doOneThing;
-(void) doAnotherThing;
// etc
Put them in an array:
SEL anArray[] = { #selector(doOneThing), #selector(doAnotherThing) /* other selectors */, NULL };
And then you can just iterate through them.
SEL* selPtr = anArray;
while (selPtr != NULL)
{
[self performSelector: *selPtr];
selPtr++;
}