Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

neil.b

macrumors member
Original poster
Nov 20, 2008
65
0
In my app view I'm detecting if the user has touch within one of 16 defined rectangles;

Code:
-(int) whichBoxIsTouched:(CGPoint)theTouch
	{	
	
	NSArray *hitBoxArray = [NSArray arrayWithObjects:
							@"{{64,404},{48,48}}",
							@"{{112,404},{48,48}}",
							@"{{160,404},{48,48}}",
							@"{{208,404},{48,48}}",
							@"{{64,356},{48,48}}",
							@"{{112,356},{48,48}}",
							@"{{160,356},{48,48}}",
							@"{{208,356},{48,48}}",
							@"{{64,308},{48,48}}",
							@"{{112,308},{48,48}}",
							@"{{160,308},{48,48}}",
							@"{{208,308},{48,48}}",
							@"{{64,260},{48,48}}",
							@"{{112,260},{48,48}}",
							@"{{160,260},{48,48}}",
							@"{{208,260},{48,48}}",
							nil];
	
	CGRect theHitRect;
	for (int i = 0; i < [hitBoxArray count]; i++)
	{
		theHitRect = CGRectFromString([hitBoxArray objectAtIndex:i]);
		if (CGRectContainsPoint(theHitRect, theTouch))
		{
			return i;
		}
	}
	//if not found, return out-of-range value
	return [hitBoxArray count];
}

I've been trying to look at ways to optimize the hit test. I thought of putting the CGRects in an array so that I don't have to do the CGRectFromString conversion in the loop but I can't figure out how to store the rect information in an array.

Can anyone offer a different solution that would be faster than my current method?

As you can see the hit rectangles are in fact all squares of the same size so maybe that fact can be used.

Thanks
 

ghayenga

macrumors regular
Jun 18, 2008
190
0
In my app view I'm detecting if the user has touch within one of 16 defined rectangles;

Code:
-(int) whichBoxIsTouched:(CGPoint)theTouch
	{	
	
	NSArray *hitBoxArray = [NSArray arrayWithObjects:
							@"{{64,404},{48,48}}",
							@"{{112,404},{48,48}}",
							@"{{160,404},{48,48}}",
							@"{{208,404},{48,48}}",
							@"{{64,356},{48,48}}",
							@"{{112,356},{48,48}}",
							@"{{160,356},{48,48}}",
							@"{{208,356},{48,48}}",
							@"{{64,308},{48,48}}",
							@"{{112,308},{48,48}}",
							@"{{160,308},{48,48}}",
							@"{{208,308},{48,48}}",
							@"{{64,260},{48,48}}",
							@"{{112,260},{48,48}}",
							@"{{160,260},{48,48}}",
							@"{{208,260},{48,48}}",
							nil];
	
	CGRect theHitRect;
	for (int i = 0; i < [hitBoxArray count]; i++)
	{
		theHitRect = CGRectFromString([hitBoxArray objectAtIndex:i]);
		if (CGRectContainsPoint(theHitRect, theTouch))
		{
			return i;
		}
	}
	//if not found, return out-of-range value
	return [hitBoxArray count];
}

I've been trying to look at ways to optimize the hit test. I thought of putting the CGRects in an array so that I don't have to do the CGRectFromString conversion in the loop but I can't figure out how to store the rect information in an array.

Can anyone offer a different solution that would be faster than my current method?

As you can see the hit rectangles are in fact all squares of the same size so maybe that fact can be used.

Thanks

Since your hitBoxArray never changes why wouldn't you make that an instance variable and initialize it to be an array of the CGRects that you want instead of re-initializing the that array on every touch and then then converting every string to a CGRect on the fly?
 

Niiro13

macrumors 68000
Feb 12, 2008
1,719
0
Illinois
What he said...if you're still wondering how that would look, here's the code.

At the top of the class, under the import line and above the implementation, put:
Code:
static NSArray *hitBoxArray = nil;

Now in the init method (or awakefromnib, viewwillappear, etc.) put:

Code:
hitBoxArray = [[NSArray init] initWithObjects:
							CGRectMake(64,404,48,48),
							CGRectMake(112,404,48,48),
							CGRectMake(160,404,48,48),
							CGRectMake(208,404,48,48),
							CGRectMake(64,356,48,48),
							CGRectMake(112,356,48,48),
							CGRectMake(160,356,48,48),
							CGRectMake(208,356,48,48),
							CGRectMake(64,308,48,48),
							CGRectMake(112,308,48,48),
							CGRectMake(160,308,48,48),
							CGRectMake(208,308,48,48),
							CGRectMake(64,260,48,48),
							CGRectMake(112,260,48,48),
							CGRectMake(160,260,48,48),
							CGRectMake(208,260,48,48),
							nil]];

Lastly, change what you gave us to:


Code:
-(int) whichBoxIsTouched:(CGPoint)theTouch
	{	
	for (int i = 0; i < [hitBoxArray count]; i++)
	{
		if (CGRectContainsPoint([hitBoxArray objectAtIndex:i], theTouch))
		{
			return i;
		}
	}
	//if not found, return out-of-range value
	return [hitBoxArray count];
}

Basically, what he told you, only here's the code for it.
 

neil.b

macrumors member
Original poster
Nov 20, 2008
65
0
Thanks for the tips guys. :)

I thought you couldn't put CGRects in an array - I'd tried a few ways without much success. I'll give your suggestion a try.
 

neil.b

macrumors member
Original poster
Nov 20, 2008
65
0
That code throws up two errors.

Firstly on the arrray init;

Code:
error: incompatible type for argument 1 of 'initWithObjects:'

And then in the for...loop;

Code:
error: incompatible type for argument 1 of 'initWithObjects:'

Any ideas?
 

neil.b

macrumors member
Original poster
Nov 20, 2008
65
0
Sorted it. I created a NSObject class "HitBoxRect" that has a CGRect as a property;

Interface:

Code:
@interface HitBoxRect : NSObject {
	CGRect rect;
}

-(id) initWithCGRect:(CGRect)aRect;
@property (nonatomic) CGRect rect;

@end

Implementation:

Code:
#import "HitBoxRect.h"

@implementation HitBoxRect
@synthesize rect;

-(id) initWithCGRect:(CGRect)aRect
{
	rect = aRect;
	return self;
}

@end

Then created a NSMutableArray "hitBoxArray" and populated it;

Code:
-(void) initHitBoxArray
{
	hitBoxArray = [[NSMutableArray alloc] init];
	NSArray *boxes = [NSArray arrayWithObjects:
							@"{{64,404},{48,48}}",
							@"{{112,404},{48,48}}",
							@"{{160,404},{48,48}}",
							@"{{208,404},{48,48}}",
							@"{{64,356},{48,48}}",
							@"{{112,356},{48,48}}",
							@"{{160,356},{48,48}}",
							@"{{208,356},{48,48}}",
							@"{{64,308},{48,48}}",
							@"{{112,308},{48,48}}",
							@"{{160,308},{48,48}}",
							@"{{208,308},{48,48}}",
							@"{{64,260},{48,48}}",
							@"{{112,260},{48,48}}",
							@"{{160,260},{48,48}}",
							@"{{208,260},{48,48}}",
							nil];
	
	CGRect aRect;
	HitBoxRect *hRect;
	for (int i = 0; i < [boxes count]; i++)
	{
		aRect = CGRectFromString([boxes objectAtIndex:i]);
		hRect = [[HitBoxRect alloc] initWithCGRect:aRect];
		[hitBoxArray addObject:hRect];
		[hRect release];
	}
}

So I can then access the custom objects with;

Code:
-(int) whichBoxIsTouched:(CGPoint)theTouch
{		
	for (int i = 0; i < [hitBoxArray count]; i++)
	{
		if (CGRectContainsPoint([[hitBoxArray objectAtIndex:i] rect], theTouch))
		{
			return i;
		}
	}
	//if not found, return out-of-range value
	return [hitBoxArray count];
}
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.