finding iteration formula for points arranged in a triangle - objective-c

I want to arrange my sprites at the following points using a for loop:
ccp(240.0, 160.0);
ccp(300.0, 120.0);
ccp(300.0, 200.0);
ccp(360.0, 80.0);
ccp(360.0, 160.0);
ccp(360.0, 240.0);
I am trying to get an iteration formula for these points using a for loop. I've been at it for a while. Below is the visual representation of points. Please help.
*
*
* *
*
*

Do you want something like this? (its a quick, sketch only)
Assume initial parameters:
initPoint (x0, y0)
initVector (vX,vY)
num of iteration c
index = c
while index
for(j = index, currentPoint = initPoint+(c-index)*(0,2*vY); j;j--, currentPoint += initVector)
draw currentPoint
index--
Basically the main idea is, that you start from the top right point, and shifts the context of the drawing with the initial vector as many times, as the iteration holds (to the bottom left corner) and draws the points. Then shifts the initial point down along the y axis and repeat minus one times.

Here's one way:
I didn't spend but a couple of minutes thinking about it, but it makes it easier if you start from the largest row and work your way down:
PatternTest.h
#import "cocos2d.h"
#interface PatternTest : CCLayer
#end
PatternTest.m
#implementation PatternTest
-(id) init
{
if( (self=[super init]))
{
CCNode *grid = [self generateArrowPatternWithBaseRowOfNumSprites:5 spacedApart:ccp(25.0f, 25.0f)];
[grid setPosition:ccp(50.0f,50.0f)];
[self addChild:grid];
}
return self;
}
-(CCNode *) generateArrowPatternWithBaseRowOfNumSprites:(float) numSprites spacedApart:(CGPoint) space
{
CCNode* patternNode = [CCNode node];
CGPoint tempPos = ccp(0.0f, 0.0f);
float offset = 0.0f;
while (numSprites > 0)
{
for(int x=numSprites;x>0;x--)
{
CCSprite *patternSprite = [CCSprite spriteWithFile:#"Icon.png"];
[patternSprite setScale: 0.3f];
[patternSprite setPosition: tempPos];
[patternNode addChild:patternSprite];
tempPos = ccpAdd(tempPos, ccp(0.0f,space.y));
}
tempPos = ccp(tempPos.x, 0.0f);
offset = offset + (space.y / 2.0f);
tempPos = ccpAdd(tempPos, ccp(space.x, offset));
numSprites -= 1;
}
return patternNode;
}
#end

Related

Plotting hits within given areas of circle - Code for setup getting bloated

[Alternative approach given by user1118321, old and new code listed for reference]
I am busy writing a small app which allows a game of darts 501 to be scored. I have set up a class to contain the individual scoring areas (GDSBoardArea) and one mutable array which represents the entire dart board and holds all the individual scoring class instances.
I have written the code for one section (20 score, inner and outer, and triple and double 20) but the issue I have is that the code for this is quite bloated and was woindering if there was a better way to init all the areas with less, more managable code as I will have to repeat this below another 19 times for the other sections?
I have a class that holds the settings for the various individual areas, one class to hold all these areas and helper functions.
I did attempt to try reuse the CGPoints and area arrays but this simply caused issues I think related to the fact that it updates a reference as the scoringAreas addObject does not copy the array object (Please correct me if I am wrong).
I setup the scoring areas once when the application runs using the createAreas function and I cant seem to reuse the CGPoints and arrays holding them.
My questions are:
1 - Is there a way to pass in a variable array of CGPoints to "GDSBoardArea"'s last parameter instead of having to create the CGPoints, add them to an array and pass the array in.
2 - Does the mutable array method addobject copy or reference the object passed to it? Is it there any way to update an array and then make a copy of it to a "master" mutable array so that the master array is effectively holding a different array in each element. I am basically try to avoid having to create a new array for each of the master arrays elements.
createAreas
-(void)createAreas
{
scoringAreas = [[NSMutableArray alloc] init];
/*
20 scoring section
*/
// 20 score - Inner triangle area
/////////////////////////////////////////
// Set up CGPoints for area
CGPoint pt_inner20_1 = CGPointMake(368.0f, 234.0f);
CGPoint pt_inner20_2 = CGPointMake(413.0f, 234.0f);
CGPoint pt_inner20_3 = CGPointMake(389.0f, 360.0f);
// Add points to array for passing to GDSBoardArea Class
NSMutableArray * areaPointsArray_Inner20 = [NSMutableArray array];
[areaPointsArray_Inner20 addObject:[NSValue valueWithCGPoint:pt_inner20_1]];
[areaPointsArray_Inner20 addObject:[NSValue valueWithCGPoint:pt_inner20_2]];
[areaPointsArray_Inner20 addObject:[NSValue valueWithCGPoint:pt_inner20_3]];
// Create GDSBoardArea Instance with init values
GDSBoardArea *scoreAreaFor_Inner20 = [[GDSBoardArea alloc] initWithName:#"20-Inner" abbrev:#"20" areaValue:20 pointsArray:NULL];
// Add Array of CGPoints to GDSScoreArea
[scoreAreaFor_Inner20 setPointsForPath:areaPointsArray_Inner20];
// Add ScoreArea to ScoreAreas Class
[scoringAreas addObject:scoreAreaFor_Inner20];
// 20 score - Outer Quadrilateral
/////////////////////////////////////////
CGPoint pt_outer20_1 = CGPointMake(353.0f, 125.0f);
CGPoint pt_outer20_2 = CGPointMake(429.0f, 127.0f);
CGPoint pt_outer20_3 = CGPointMake(415.0f, 208.0f);
CGPoint pt_outer20_4 = CGPointMake(365.0f, 209.0f);
NSMutableArray * areaPointsArray_Outer20 = [NSMutableArray array];
[areaPointsArray_Outer20 addObject:[NSValue valueWithCGPoint:pt_outer20_1]];
[areaPointsArray_Outer20 addObject:[NSValue valueWithCGPoint:pt_outer20_2]];
[areaPointsArray_Outer20 addObject:[NSValue valueWithCGPoint:pt_outer20_3]];
[areaPointsArray_Outer20 addObject:[NSValue valueWithCGPoint:pt_outer20_4]];
GDSBoardArea *scoreAreaFor_Outer20 = [[GDSBoardArea alloc] initWithName:#"20-Outer" abbrev:#"20" areaValue:20 pointsArray:NULL];
[scoreAreaFor_Outer20 setPointsForPath:areaPointsArray_Outer20];
[scoringAreas addObject:scoreAreaFor_Outer20];
// Double 20 Quadrilateral
/////////////////////////////////////////
CGPoint pt_d20_1 = CGPointMake(351.0f, 102.0f);
CGPoint pt_d20_2 = CGPointMake(353.0f, 118.0f);
CGPoint pt_d20_3 = CGPointMake(430.0f, 119.0f);
CGPoint pt_d20_4 = CGPointMake(433.0f, 104.0f);
NSMutableArray * areaPointsArray_Double20 = [NSMutableArray array];
[areaPointsArray_Double20 addObject:[NSValue valueWithCGPoint:pt_d20_1]];
[areaPointsArray_Double20 addObject:[NSValue valueWithCGPoint:pt_d20_2]];
[areaPointsArray_Double20 addObject:[NSValue valueWithCGPoint:pt_d20_3]];
[areaPointsArray_Double20 addObject:[NSValue valueWithCGPoint:pt_d20_4]];
GDSBoardArea *scoreAreaFor_double20 = [[GDSBoardArea alloc] initWithName:#"20-Double" abbrev:#"d20" areaValue:40 pointsArray:NULL];
[scoreAreaFor_double20 setPointsForPath:areaPointsArray_Double20];
[scoringAreas addObject:scoreAreaFor_double20];
// Triple 20 Quadrilateral
/////////////////////////////////////////
CGPoint pt_t20_1 = CGPointMake(367.0f, 214.0f);
CGPoint pt_t20_2 = CGPointMake(368.0f, 226.0f);
CGPoint pt_t20_3 = CGPointMake(412.0f, 226.0f);
CGPoint pt_t20_4 = CGPointMake(413.0f, 214.0f);
NSMutableArray * areaPointsArray_Triple20 = [NSMutableArray array];
[areaPointsArray_Triple20 addObject:[NSValue valueWithCGPoint:pt_t20_1]];
[areaPointsArray_Triple20 addObject:[NSValue valueWithCGPoint:pt_t20_2]];
[areaPointsArray_Triple20 addObject:[NSValue valueWithCGPoint:pt_t20_3]];
[areaPointsArray_Triple20 addObject:[NSValue valueWithCGPoint:pt_t20_4]];
GDSBoardArea *scoreAreaFor_triple20 = [[GDSBoardArea alloc] initWithName:#"20-Triple" abbrev:#"t20" areaValue:60 pointsArray:NULL];
[scoreAreaFor_triple20 setPointsForPath:areaPointsArray_Triple20];
[scoringAreas addObject:scoreAreaFor_triple20];
}
ScoringAreas is a Mutable Array that simplt represents all the areas of the board and is defined as:
#property (strong, atomic) NSMutableArray *scoringAreas;
GDSBoardArea Class in case its relevant
// GDSBoardArea.h
#import <Foundation/Foundation.h>
#interface GDSBoardArea : NSObject
#property NSMutableArray *pointsForPath;
#property int areaValue;
#property NSString *name;
#property NSString *abbrev;
-(id) init;
-(id)initWithName:(NSString *)name_ abbrev:(NSString *)abbrev_ areaValue:(int)areaValue_ pointsArray:(NSMutableArray*)pointsArray;
#end
--
// GDSBoardArea.m
#import "GDSBoardArea.h"
#implementation GDSBoardArea
-(id)init{
if (self = [super init]) {
/* perform your post-initialization logic here */
[self setName:#""];
[self setAbbrev:#""];
[self setAreaValue:0];
[self setPointsForPath:NULL];
}
return self;
}
-(id)initWithName:(NSString *)name_ abbrev:(NSString *)abbrev_ areaValue:(int)areaValue_ pointsArray:(NSMutableArray*)pointsArray
{
self = [super init];
if (self) {
self.name = name_;
self.abbrev = abbrev_;
self.areaValue = areaValue_;
self.pointsForPath = pointsArray;
}
return self;
}
#end
Code change as per answer from user1118321:
createAreas function was completely removed and direct calculation done as follows (Will add more as it is fleshed out until complete)
-(GDSBoardArea *)checkPointsScore:(CGPoint)tappedPoint
{
#define NUM_PIE_SLICES 20
#define NUM_RINGS 7
#define DEG_PER_SLICE 18
#define PII 3.141592653
#define OFFSETFORBOARD 99.3f
float radius_board = 344.0f;
float thickness_innerbull = 16.0f;
float thickness_outerbull = 18.0f;
float thickness_innerSingleScore = 116.0f;
float thickness_double = 16.0f;
float thickness_outerSingleScore = 80.0f;
float thickness_triple = 20.0f;
float thickness_noscore = 78.0f;
GDSBoardArea *tappedScoreArea = NULL;
double dartX = tappedPoint.x;
double dartY = tappedPoint.y;
double centerX = 390.0f;
double centerY = 390.0f;
double angle = atan2((dartY - centerY), (dartX - centerX)) * 180 / PII;
angle = angle + OFFSETFORBOARD; // To force slice 0 to correspond to Score of 20
int slice = (int)floor(angle / DEG_PER_SLICE);
NSLog(#"angle: %f", angle);
NSLog(#"slice #: %i", slice);
return tappedScoreArea;
}
If it were me, I wouldn't bother at all with storing the various areas of the board. First, they aren't triangles and quads, and second, it's fairly easy to calculate them. You can figure out which "pie slice" you're in by getting the arctangent of the point where the dart lands, like this:
double angle = atan2((dartY - centerY) / (dartX - centerX));
You can then convert that to an integer between 1 and # of slices like this:
int slice = (int)floor(angle / anglePerSlice);
Next, you can figure out which ring the user is in by using the distance from the board's center:
double deltaX = dartX - centerX;
double deltaY = dartY - centerY;
double distFromCenter = sqrt (deltaX * deltaX + deltaY * deltaY);
You could then convert that into an index by looking it up in an array. You have a single array containing all the boundaries in a single slice. All slices have their boundaries at the same distances. So it would look something like this:
double concentricBounds[kNumBounds] = { 10, 15, 100, 110, 200, 210 }; // Just made up - I don't know what distances you're using.
bool done = false;
int index = 0;
for (int i = 0; (i < kNumBounds) && (!done); i++)
{
if (distFromCenter < concentricBounds [ i ])
{
done = true;
index = i;
}
}
if (!done)
{
...point was off the board...
}
So now you have the slice it was in and the ring it's in. You can just have a 2D array of point values to look up into:
int points [][] = { {50, 25, 20, 60, 20, 40 }, { 50, 25, 5, 15, 5, 15 }, ...etc... };
Note that atan2() returns angles with 0° being parallel to the X-axis and it increases in counter-clockwise order. So you may have to rotate the scores to orient your board correctly.

Having problems seeing polygons in my cocos2d code. Using cocos2d and box2d. Only in debug mode the actual polygons are visible

So I need help figuring out what code I am missing here. I have checked all over the place, but I need specifics on wether its the formulas used or a typo that i haven't noticed yet.
Here is the polygon class. I am trying to create random polygons with 8 vertices and then of course fill with a plain color. But I want them to continue to generate random position but leave them fixed. In a better way the poly's are my terrain.Ok revise: the polygons are there and my character interacts with them, but I cannot see them, and yes they are on the same layer. Oh but they don't keep generating at the bottom, which i am guessing i just need to delete the old ones once they go off the screen and it should make a new poly.
-(void) genBody:(b2World *)world pos:(CGPoint *)pos {
//Here we generate a somewhat random convex polygon by sampling
//the "eccentric anomaly" of an ellipse with randomly generated
//x and y scaling constants (a,b). The algorithm is limited by
//the parameter max_verts, and has a number of tunable minimal
//and scaling values.
// I need to change this to randomly choosing teh number of vertices between 3-8,
// then choosing random offsets from equally distributed t values.
// This will eliminate teh while loop.
screen_pos = ccp(pos->x, pos->y);
float cur_t;
float new_t;
float delta_t;
float min_delta_t = 0.5;
float t_scale = 1.5;
b2Vec2 *verts= new b2Vec2[m_maxVerts]; // this should be replaced by a private verts ... maybe ... hmm that will consume more ram though
float t_vec[m_maxVerts];
// Generate random vertices
int vec_len;
while (true) {
cur_t = 0.0;
for (vec_len=0; vec_len<m_maxVerts; vec_len++) {
//delta_t = t_scale*(float)rand()/(float)RAND_MAX; // wish they just had a randf method :/
delta_t = t_scale*floorf((double)arc4random()/ARC4RANDOM_MAX);
#ifdef POLY_DEBUG
CCLOG(#"delta_t %0.2f", delta_t);
#endif
if (delta_t < min_delta_t) {
delta_t = min_delta_t;
}
new_t = cur_t + delta_t;
if (new_t > 2*PI) {
break;
}
t_vec[vec_len] = new_t;
cur_t = new_t;
}
// We need at least three points for a triangle
if ( vec_len > 3 ) {
break;
}
}
At least where the body is being generated.
then...
float num_verts = vec_len;
b2BodyDef BodyDef;
BodyDef.type = b2_staticBody;
BodyDef.position.Set(pos->x/PTM_RATIO, pos->y/PTM_RATIO);
BodyDef.userData = self; // hope this is correct
m_polyBody = world->CreateBody(&BodyDef);
b2PolygonShape polyShape;
int32 polyVert = num_verts;
polyShape.Set(verts, polyVert);
b2FixtureDef FixtureDef;
FixtureDef.shape = &polyShape;
FixtureDef.userData = self; // hope this is correct
FixtureDef.density = 1.6f;
FixtureDef.friction = 0.4f;
FixtureDef.restitution = 0.5f;
m_polyBody->CreateFixture(&FixtureDef);
for (int i=0; i < num_verts; i++) {
// Convert from b2Vec2 to CCPoint and from physics units to pixels
m_verts[i] = ccp(verts[i].x*PTM_RATIO, verts[i].y*PTM_RATIO);
}
m_numVerts = num_verts;
delete verts;
}
-(void) setColor:(ccColor4F) color {
m_color = color;
}
-(BOOL) dirty {
return true;
}
-(void) draw {
//[super draw];
ccDrawPoly(m_verts, m_numVerts, YES);
CCLOG(#"Drawing?");
}
-(CGAffineTransform) nodeToParentTransform {
b2Vec2 pos = m_polyBody->GetPosition();
float x = pos.x * PTM_RATIO;
float y = pos.y * PTM_RATIO;
/*if ( ignoreAnchorPointForPosition_ ) {
x += anchorPointInPixels_.x;
y += anchorPointInPixels_.y;
}*/
// Make matrix
float radians = m_polyBody->GetAngle();
float c = cosf(radians);
float s = sinf(radians);
if( ! CGPointEqualToPoint(anchorPointInPixels_, CGPointZero) ){
x += c*-anchorPointInPixels_.x + -s*-anchorPointInPixels_.y;
y += s*-anchorPointInPixels_.x + c*-anchorPointInPixels_.y;
}
// Rot, Translate Matrix
transform_ = CGAffineTransformMake( c, s,
-s, c,
x, y );
return transform_;
}
there is some stuff in between but its less important. I can post it if asked.
Then the update function, which is based in my game scene class.
-(void)updateObstacles
{
//CCLOG(#"updating obstacles");
int xpos;
int ypos;
CGPoint pos;
for (int i=0; i<MAX_OBSTACLES; i++ ) {
// If there is no obstacle generate a new one
if ( obstacles[i] == NULL ) {
polyObstacleSprite *sprite = [[polyObstacleSprite alloc] init];
ypos = int(_winSize.width/2*(double)arc4random()/ARC4RANDOM_MAX) - _winSize.width/2;
xpos = int(_winSize.height/2*(double)arc4random()/ARC4RANDOM_MAX) - _winSize.height/2;
//CCLOG(#"generating obstacle at %d,%d", xpos, ypos);
pos = ccp(xpos, ypos);
[sprite genBody:_world pos:&pos];
[self addChild:sprite z:1];
obstacles[i] = sprite;
}
//CCLOG(#"position: %d, %d", obstacles[i]->screen, obstacles[i]->position.y); FINISH
}
}
Sorry if its sort of a mess I set this up quick, but pretty much what I want to do is have randomly generated polygons appear at the bottom of my iphone screen as my character moves down with gravity. I got everything else but the polygons working.
Thanks in advance for spending the time to read this.

method to return the intersection of 2 rectangles

I'm new to programming and objective-c (so i could be way off here) and i'm working my way through programming in objective c 4th edition text book and have become stuck on one of the exercises.
Can anyone tell what's wrong with the method below? In my program there is a rectangle class which has methods to set it's width, height and it's origin (from a class called XYPoint).
The containsPoint method checks to see if a rectangle's origin is within another rectangle. When i test this method it always returns 'No' even when the rectangle does contain the point.
The intersects method takes a rectangle as an argument (aRect) and uses the containsPoint method in an if statement to check if it intersects with the reciever and if it does, return a rectangle with an origin at the intersect and the correct width and height.
-(BOOL) containsPoint:(XYPoint *) aPoint
{
//create two variables to be used within the method
float upperX, upperY;
//assign them values, add the height and width to the origin values to range which the XYPoint must fall into
upperX = origin.x + height;
upperY = origin.y + width;
//if the value of aPoint's x and y points fall between the object's origin and the upperX or upperY values then the rectangle must contain the XYPoint and a message is sent to NSLog
if ((aPoint.x >= origin.x) && (aPoint.x <= upperX) && (aPoint.y >= origin.y) && (aPoint.y <= upperY) )
{
NSLog(#"Contains point");
return YES;
}
else
{
NSLog(#"Does not contain point");
return NO;
}
}
-(Rectangle *) intersects: (Rectangle *) aRect
{
//create new variables, Rectangle and XYPoint objects to use within the method
Rectangle *intersectRect = [[Rectangle alloc] init];
XYPoint *aRectOrigin = [[XYPoint alloc] init];
float wi, he; //create some variables
if ([self containsPoint:aRect.origin]) { //send the containsPoint method to self to test if the intersect
[aRectOrigin setX:aRect.origin.x andY:origin.y]; //set the origin for the new intersecting rectangle
[intersectRect setOrigin:aRectOrigin];
wi = (origin.x + width) - aRect.origin.x; //determine the width of the intersecting rectangle
he = (origin.y + height) - aRect.origin.y; //determine the height of the intersecting rectangle
[intersectRect setWidth:wi andHeight:he]; //set the rectangle's width and height
NSLog(#"The shapes intersect");
return intersectRect;
}
//if the test returned NO then send back these values
else {
[intersectRect setWidth:0. andHeight:0.];
[aRectOrigin setX:0. andY:0.];
[intersectRect setOrigin:aRectOrigin];
NSLog(#"The shapes do not intersect");
return intersectRect;
}
}
when i test with the following code
int main (int argc, char * argv [])
{
#autoreleasepool {
Rectangle *aRectangle = [[Rectangle alloc] init];
Rectangle *bRectangle = [[Rectangle alloc] init];
Rectangle *intersectRectangle = [[Rectangle alloc] init];
XYPoint *aPoint = [[XYPoint alloc] init];
XYPoint *bPoint = [[XYPoint alloc] init];
[aPoint setX:200.0 andY:420.00];
[bPoint setX:400.0 andY:300.0];
[aRectangle setWidth:250.00 andHeight:75.00];
[aRectangle setOrigin:aPoint];
[bRectangle setWidth:100.00 andHeight:180.00];
[bRectangle setOrigin:bPoint];
printf("Are the points within the rectangle's borders? ");
[aRectangle containsPoint: bPoint] ? printf("YES\n") : printf("NO\n");
intersectRectangle = [aRectangle intersects:bRectangle];
}
return 0;
}
i get the following output
Contains point
The origin is at 0.000000,0.000000, the width is 250.000000 and the height is 75.000000
Here is the code (I'm currently using "Programming in Objective-C 5th edition by Stephen Kochan) and judging by your variables and syntax, it looks like you are too.
// Use Floats for XYPoint and Rectangular exercise 8.4 on pg. 166
import "Rectangle.h"
import "XYPoint.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
Rectangle *myRect = [[Rectangle alloc] init];
Rectangle *yourRect = [[Rectangle alloc] init];
XYPoint *myPoint = [[XYPoint alloc] init];
XYPoint *yourPoint = [[XYPoint alloc] init];
// For first Rectangle set w, h, origin
[myRect setWidth:250 andHeight:75];
[myPoint setX:200 andY:420];
myRect.origin = myPoint;
// Second Rectangle
[yourRect setWidth:100 andHeight:180];
[yourPoint setX:400 andY:300];
yourRect.origin = yourPoint;
// Find Points of intersection
float x1, x2, y1, y2;
x1 = MAX(myRect.origin.x, yourRect.origin.x);
x2 = MIN(myRect.origin.x + myRect.width, yourRect.origin.x + yourRect.width);
y1 = MAX(myRect.origin.y, yourRect.origin.y);
y2 = MIN(myRect.origin.y + myRect.height, yourRect.origin.y + yourRect.height);
// Make Intersecting Rectangle
Rectangle *intrRect = [[Rectangle alloc] init];
[intrRect setWidth: abs(x2 - x1) andHeight: abs(y2 - y1)];
// Print Intersecting Rectangle's infor for a check
NSLog(#"Width = %g, Height = %g, Bottom point = (%g, %g), Top point = (%g, %g)", intrRect.width, intrRect.height, x1, y1, x2, y2);
}
return 0;
}
Why not just use the included functions for determining intersection and/or containment:
if (CGRectContainsPoint(CGRect rect, CGPoint point))
{
// Contains point...
}
or
if (CGRectIntersectsRect(CGRect rectOne, CGRect rectTwo))
{
// Rects intersect...
}
or
if (CGRectContainsRect(CGRect rectOne, CGRect rectTwo))
{
// RectOne contains rectTwo...
}
You have made a number of errors, both in geometry and Objective-C, but this is how you learn!
Geometry: for two rectangles to intersect the origin of one does not need to be within the area of the other. Your two rectangles intersect but the intersection does not contain either origin. So your containsPoint: will return NO.
You only allocate objects if you create them, not if code which you call creates and returns them. So your allocation in Rectangle *intersectRectangle = [[Rectangle alloc] init]; creates an object which is then discarded by your assignment intersectRectangle = [aRectangle intersects:bRectangle];
For simple classes such as these it is usual to create init methods which set the properties, as creating an object without setting the properties is not useful. E.g. for your XYPoint class you would normally have an initialisation method - (id) initWithX:(float)x andY:(float)y;. You then use [[XYPoint alloc] initWithX:200.0 andY:420.0]] to create and initialise in one go.
As you are experimenting this is not important, but in real code for very simple and small classes which have value semantics - which means they behave much like integers and floats - it is common to use structures (struct) and functions and not objects and methods. Look up the definitions of NSRect and NSPoint as examples.
HTH
//intersect function
-(Rectangle *)intersect:(Rectangle *)secondRec
{
int intersectRectWidth;
int intersectRectHeight;
int intersectRectOriginX;
int intersectRectOriginY;
Rectangle * intersectRec =[[Rectangle alloc]init];
intersectRectOriginX = MAX(self.origin.x,secondRec.origin.x);
intersectRectOriginY = MAX((self.origin.y) , (secondRec.origin.y));
int myWidth=self.width+self.origin.x;
int secondRecWidth=secondRec.width+secondRec.origin.x;
int tempWidth=secondRecWidth - myWidth;
if (tempWidth > 0) {
intersectRectWidth=secondRec.width - tempWidth;
}
else
{
tempWidth=abs(tempWidth);
intersectRectWidth = self.width - tempWidth;
}
//height
int myHeight=self.height+self.origin.y;
int secondRecHeight=secondRec.height +secondRec.origin.y;
int tempHeight=secondRecHeight - myHeight;
if (tempHeight > 0) {
intersectRectHeight=secondRec.height - tempHeight;
}
else
{
tempHeight=abs(tempHeight);
intersectRectHeight = self.height - tempHeight;
}
intersectRec.width=intersectRectWidth;
intersectRec.height=intersectRectHeight;
XYPoint * intersectOrigin =[[XYPoint alloc]init];
[intersectOrigin setX:intersectRectOriginX andY:intersectRectOriginY];
[intersectRec setOrigin:intersectOrigin];
return intersectRec;
}
//main
XYPoint * org1=[[XYPoint alloc]init];
XYPoint * org2=[[XYPoint alloc]init];
Rectangle * firstRec=[[Rectangle alloc]init];
Rectangle * secondRec=[[Rectangle alloc]init];
Rectangle * intersectRec=[[Rectangle alloc]init];
[org1 setX:400 andY:300];
[org2 setX:200 andY:420];
[firstRec setOrigin:org1];
[secondRec setOrigin:org2];
[firstRec setWidth:100 andHeight:180];
[secondRec setWidth:250 andHeight:75];
intersectRec=[firstRec intersect:secondRec];
NSLog(#"intersect rectangle origin.x= %i origin.y=%i width=%i height=%i",intersectRec.origin.x,intersectRec.origin.y,intersectRec.width,intersectRec.height);

Objective-C: Get closest NSView to dragged NSView

If I have a NSView with 10 subviews, and I drag one of the subviews, what is the easiest way to determine which of the remaining views is closes to the dragged one? My code is working but I somehow feel I'm using a monkey wrench to fine tune a violin. Is there a more elegant way?
subviews is an array of the subviews of the parent view of this view (so it includes this view)
the toolTip of the sub views contain their page # formatted like "Page_#"
- (void) mouseUp: (NSEvent* ) e {
//set up a ridiclous # for current distance
float mtDistance = 12000000.0;
//get this page number
selfPageNum = [[[self toolTip] substringFromIndex:5] intValue];
//set up pageViews array
pageViews = [[NSMutableArray alloc] init];
//loop through the subviews
for (int i=0; i<[subviews count]; i++) {
//set up the view
thisView = [subviews objectAtIndex:i];
//filter for view classes
NSString* thisViewClass = [NSString stringWithFormat:#"%#", [thisView className]];
if ( [thisViewClass isEqual:#"Page"]) {
//add to the pageViews array
[pageViews addObject:thisView];
if (self != thisView) {
//get the view and self frame
NSRect movedViewFrame = [self frame];
NSRect thisViewFrame = [thisView frame];
//get the location area (x*y)
float movedViewLoc = movedViewFrame.origin.x * (movedViewFrame.origin.y * -1);
float thisViewLoc = thisViewFrame.origin.x * (thisViewFrame.origin.y * -1);
//get the difference between x locations
float mtDifference = movedViewLoc - thisViewLoc;
if (mtDifference < 0) {
mtDifference = mtDifference * -1;
}
if (mtDifference < mtDistance) {
mtDistance = mtDifference;
closesView = thisView;
}
}
}//end class check
}//end loop
//....more stuff
}
http://en.wikipedia.org/wiki/Distance
sqrt(pow((x2 - x1), 2) + pow((y2 - y1), 2)))
Edit: Since you only need to find the shortest distance, not exact distances, you don't need to take the square root. Thanks for the insight Abizern
pow((x2 - x1), 2) + pow((y2 - y1), 2)

Pointers, Invalid Operands to Binary, and Noobs

[Edit: Rectangle definition added at bottom.]
[Edit2: XYPoint interface added at bottom.]
I'm working on a method that checks if two rectangles overlap. (Yeah, I'm in Kochan's Programming in Objective-C, doing the exercises, and I am painfully new at this.) When I compile this, the error message is: "Invalid operands to binary +". I get it on the first if statement and on the if-else that follows it.
I think I have an issue with pointers, but Kochan doesn't talk about this much.
And, if I take out these lines, the rest of the method works just fine. And the relevant variables are all floating type.
Help?
Also, any other thoughts on the method would be totally welcome. (Like, how do I make lines of code not go out so long. Like I said, painfully new at this.)
-(void) overlap: (Rectangle *)r2
{
overlapRectangle = [[Rectangle alloc] init];
leftRectangle = [[Rectangle alloc] init];
rightRectangle = [[Rectangle alloc] init];
lowerRectangle = [[Rectangle alloc] init];
upperRectangle = [[Rectangle alloc] init];
BOOL xIntersect = NO;
BOOL yIntersect = NO;
// Test to see if the Rectangle contains, or is equal to, Rectangle b
if (origin.x <= r2.origin.x && origin.y <= r2.origin.y && (origin.x + width) >= (r2.origin + r2.width) && (origin.y + height) >= (r2.origin.y + r2.height) )
{
overlapRectangle = r2;
}
// Test to see if Retangle b contains, or is equal to, the Rectangle
else if (origin.x >= r2.origin.x && origin.y >= r2.origin.y && origin.x + width <= r2.origin + r2.width && origin.y + height <= r2.origin.y + r2.height )
{
overlapRectangle = self;
}
// I should add tests for triangles overlapping on three
// sides or overlapping on two sides, but I'm not going
// to right now. Just learning objects and methods.
// Test to see if rectangles overlap on the x-axis
// Current is an if, because I wanted to run the code below
// to see if it worked, and it did.
if (origin.x <= r2.origin.x)
{
leftRectangle = self;
rightRectangle = r2;
}
else
{
rightRectangle = self;
leftRectangle = r2;
}
if (rightRectangle.origin.x + rightRectangle.width > leftRectangle.origin.x)
{
xIntersect = YES;
}
// Test to see if rectangles overlap on the y-axis
if (origin.y <= r2.origin.y)
{
lowerRectangle = self;
upperRectangle = r2;
}
else
{
lowerRectangle = self;
upperRectangle = r2;
}
if (lowerRectangle.origin.y + lowerRectangle.height > upperRectangle.origin.y)
{
yIntersect = YES;
}
// If retangles overlap on both the x-axis and y-axis,
// determination of overlapping rectangle's origin, height, and width
// and display same.
if (xIntersect == YES && yIntersect == YES)
{
overlapRectangle.origin.y = upperRectangle.origin.y;
overlapRectangle.origin.x = rightRectangle.origin.x;
overlapRectangle.height = lowerRectangle.height - (upperRectangle.origin.y - lowerRectangle.origin.y);
overlapRectangle.width = leftRectangle.width - (rightRectangle.origin.x - leftRectangle.origin.x);
NSLog (#"Your rectangles overlap.");
NSLog (#"Rectangle: w = %g, h = %g", overlapRectangle.width, overlapRectangle.height);
NSLog (#"Area = %g, Perimeter = %g", [overlapRectangle area], [overlapRectangle perimeter]);
NSLog (#"Origin at (%g, %g)", overlapRectangle.origin.x, overlapRectangle.origin.y);
}
else
{
NSLog (#"Your rectangles do not overlap.");
}
[overlapRectangle autorelease];
[leftRectangle autorelease];
[rightRectangle autorelease];
[lowerRectangle autorelease];
[upperRectangle autorelease];
}
Rectangle Definition:
// Interface, Rectangle Class
#interface Rectangle : NSObject
{
float width;
float height;
XYPoint *origin;
// For overlapping calculations
Rectangle *overlapRectangle;
Rectangle *leftRectangle;
Rectangle *rightRectangle;
Rectangle *lowerRectangle;
Rectangle *upperRectangle;
}
#property float width, height;
-(XYPoint *) origin;
-(void) setOrigin: (XYPoint *) pt;
-(void) setWidth: (float) w andHeight: (float) h;
-(float) area;
-(float) perimeter;
-(void) print;
-(void) translate;
-(void) overlap: (Rectangle *)r2;
-(void) draw;
#end
XYPoint interface:
#import <Foundation/Foundation.h>
#interface XYPoint : NSObject
{
float x;
float y;
}
#property float x, y;
-(void) setX: (float) xVal andY: (float) yVal;
#end
You've just got what is probably a typo:
// Test to see if the Rectangle contains, or is equal to,
// Rectangle b
if (origin.x <= r2.origin.x && origin.y <= r2.origin.y &&
(origin.x + width) >= (r2.origin + r2.width) &&
//^^^This is trying to add an XYPoint,
// which is an object, to a float.
(origin.y + height) >= (r2.origin.y + r2.height) )
{
overlapRectangle = r2;
}
// Test to see if Rectangle b contains, or is equal to,
// the Rectangle
else if (origin.x >= r2.origin.x && origin.y >= r2.origin.y &&
origin.x + width <= r2.origin + r2.width &&
//^^^Same thing.
origin.y + height <= r2.origin.y + r2.height )
{
...
The compiler should have told you what the types were that you were asking to be added:
error: invalid operands to binary + (have 'struct XYPoint *' and 'float')
that's the key. You just need to change the r2.origin to r2.origin.x so that you're adding two floats.
As for the length of the lines, there's two things you can do. You can move each segment of the conditions to different lines as I've done, but it would probably be best to create a couple of methods for Rectangle that will do the tests for you. This will make the code more readable, so when you come back to it in six months and the line reads:
if( [self containsRectangle:r2] || [self isEqualToRectangle:r2] ){
you'll know what's going on right away. Here's some suggestions for that:
- (BOOL)containsRectangle:(Rectangle *)otherRect {
BOOL originBelow = ((origin.x <= otherRect.origin.x) &&
(origin.y <= otherRect.origin.y));
float maxX = origin.x + width;
float otherMaxX = otherRect.origin.x + otherRect.width;
BOOL maxXGreater = maxX >= otherMaxX;
Bfloat maxY = origin.y + height;
float otherMaxY = otherRect.origin.y + otherRect.height;
BOOL maxYGreater = maxY >= otherMaxY;
return originBelow && maxXGreater && maxYGreater;
}
- (BOOL)isEqualToRectangle:(Rectangle *)otherRect {
BOOL sizeEqual = ((width == otherRect.width) &&
(height == otherRect.height));
return sizeEqual && [origin isEqualToXYPoint:otherRect.origin];
}
Note: I didn't test these, just pasted them together from the conditions of your ifs, so double-check them before you use them. I did fix the typo, though.
Notice that I also made up a method on XYPoint here, isEqualToXYPoint:; you can implement that as well, to return a BOOL if the x and y of both XYPoints are equal.