converting isometric tile map coordinates to screen coordinates - ios

I'm trying to convert isometric tile coordinates to screen coordinates.
I seem to have problem especially with the Y coordinates, looks like the X part works just fine. here is what I got so far.
// calculate screen coordinates from tile coordinates
- (CGPoint)positionForTileCoord:(CGPoint)pos {
float halfMapWidth = _tileMap.mapSize.width*0.5;
float mapHeight = _tileMap.mapSize.height;
float tileWidth = _tileMap.tileSize.width;
float tileHeight = _tileMap.tileSize.height;
int x = halfMapWidth*tileWidth + tileWidth*pos.x*0.5-tileWidth*pos.y*0.5;
int y = ............
return ccp(x, y);
my player is added as a child to the Tile map itself and the map is added to the layer at screenSize.x/2, scrrensize.y/2 with an anchor point of 0.5
I have done the same thing successfully with an orthogonal map but seem to struggle with the isometric one.
Thank you

really its look like this:
// calculate screen coordinates from tile coordinates
- (CGPoint)positionForTileCoord:(CGPoint)pos {
float halfMapWidth = _tileMap.mapSize.width*0.5;
float mapHeight = _tileMap.mapSize.height;
float tileWidth = _tileMap.tileSize.width;
float tileHeight = _tileMap.tileSize.height;
int x = halfMapWidth*tileWidth + tileWidth*pos.x*0.5-tileWidth*pos.y*0.5;
int y = (pos.y + (mapHeight * tileWidth/2) - (tileHeight/2)) - ((pos.y + pos.x) * tileHeight/2) + tileHeight;
return ccp(x, y);
}
// calculating the tile coordinates from screen location
-(CGPoint) tilePosFromLocation:(CGPoint)location
{
CGPoint pos = location;
float halfMapWidth = _tileMap.mapSize.width*0.5;
float mapHeight = _tileMap.mapSize.height;
float tileWidth = _tileMap.tileSize.width;
float tileHeight = _tileMap.tileSize.height;
CGPoint tilePosDiv = CGPointMake(pos.x/tileWidth, pos.y/tileHeight);
float invereseTileY = mapHeight - tilePosDiv.y;
// Cast int to make sure that result is in whole numbers
float posX = (int)(invereseTileY + tilePosDiv.x - halfMapWidth);
float posY = (int)(invereseTileY - tilePosDiv.x + halfMapWidth);
return CGPointMake(posX, posY);
}

int y = (pos.y + (mapHeight * tileWidth/2) - (tileHeight/2)) - ((pos.y + pos.x) * tileHeight/2) + tileHeight;

Related

Calculating the angle to a location

I have an image of an arrow that behaves like a compass to a specific location. Sometimes it works, and other times it's mirrored. So if I was facing east and the location is directly east of me, it should point up, but sometimes it points down.
#define RADIANS_TO_DEGREES(radians) ((radians) * (180.0 / M_PI))
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)heading
{
// update direction of arrow
CGFloat degrees = [self p_calculateAngleBetween:_myLocation
and:_otherLocation];
CGFloat rads = (degrees - heading.trueHeading) * M_PI / 180;
CGAffineTransform tr = CGAffineTransformIdentity;
tr = CGAffineTransformConcat(tr, CGAffineTransformMakeRotation(rads) );
[_directionArrowView setTransform:tr];
}
-(CGFloat) p_calculateAngleBetween:(CLLocationCoordinate2D)coords0 and:(CLLocationCoordinate2D)coords1 {
double x = 0, y = 0 , deg = 0, deltaLon = 0;
deltaLon = coords1.longitude - coords0.longitude;
y = sin(deltaLon) * cos(coords1.latitude);
x = cos(coords0.latitude) * sin(coords1.latitude) - sin(coords0.latitude) * cos(coords1.latitude) * cos(deltaLon);
deg = RADIANS_TO_DEGREES(atan2(y, x));
if(deg < 0)
{
deg = -deg;
}
else
{
deg = 360 - deg;
}
return deg;
}
Is this the correct way to calculate my angle with another location? Or am I missing a step? Being the arrow points directly in the opposite direction sometimes, my assumption is it's an issue with my math.
To calculate radians from x & y:
double r = atan(y/x);
if (x<0)
r = M_PI + r;
else if (x>0 && y<0)
r = 2 * M_PI + r;
There is not issue of dividing by 0 when X is zero because the atan function handles this correctly:
If the argument is positive infinity (negative infinity), +pi/2 (-pi/2) is returned.

Triangle Gradient With Core Graphics

I'm trying to draw a triangle like this one in a view (one UIView, one NSView):
My first thought was CoreGraphics, but I couldn't find any information that would help me draw a gradient between three points of arbitrary color.
Any help?
Thanks!
Actually it's pretty simple with CoreGraphics. Below you can find code that renders given triangle, but first let's think how we can solve this problem.
Theory
Imagine equilateral triangle with side length w. All three angles are equal to 60 degrees:
Each angle will represent component of a pixel: red, green or blue.
Lets analyze intensity of a green component in a pixel near top angle:
The more closer pixel to the angle, the more component intense it'll have and vice versa. Here we can decompose our main goal to smaller ones:
Draw triangle pixel by pixel.
For each pixel calculate value for each component based on distance from corresponding angle.
To solve first task we will use CoreGraphics bitmap context. It will have four components per pixel each 8 bits long. This means that component value may vary from 0 to 255. Fourth component is alpha channel and will be always equal to max value - 255. Here is example of how values will be interpolated for the top angle:
Now we need to think how we can calculate value for component.
First, let's define main color for each angle:
Now let's choose an arbitrary point A with coordinates (x,y) on the triangle:
Next, we draw a line from an angle associated with red component and it passes through the A till it intersects with opposite side of a triangle:
If we could find d and c their quotient will equal to normalized value of component, so value can be calculated easily:
(source: sciweavers.org)
Formula for finding distance between two points is simple:
(source: sciweavers.org)
We can easily find distance for d, but not for c, because we don't have coordinates of intersection. Actually it's not that hard. We just need to build line equations for line that passes through A and line that describes opposite side of a triangle and find their intersection:
Having intersection point we can apply distance formula to find c and finally calculate component value for current point.
Same flow applies for another components.
Code
Here is the code that implements concepts above:
+ (UIImage *)triangleWithSideLength:(CGFloat)sideLength {
return [self triangleWithSideLength:sideLength scale:[UIScreen mainScreen].scale];
}
+ (UIImage *)triangleWithSideLength:(CGFloat)sideLength
scale:(CGFloat)scale {
UIImage *image = nil;
CGSize size = CGSizeApplyAffineTransform((CGSize){sideLength, sideLength * sin(M_PI / 3)}, CGAffineTransformMakeScale(scale, scale));
size_t const numberOfComponents = 4;
size_t width = ceilf(size.width);
size_t height = ceilf(size.height);
size_t realBytesPerRow = width * numberOfComponents;
size_t alignedBytesPerRow = (realBytesPerRow + 0xFF) & ~0xFF;
size_t alignedPixelsPerRow = alignedBytesPerRow / numberOfComponents;
CGContextRef ctx = CGBitmapContextCreate(NULL,
width,
height,
8,
alignedBytesPerRow,
CGColorSpaceCreateDeviceRGB(),
(CGBitmapInfo)kCGImageAlphaPremultipliedLast);
char *data = CGBitmapContextGetData(ctx);
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
int edge = ceilf((height - i) / sqrt(3));
if (j < edge || j > width - edge) {
continue;
}
CGFloat redNormalized = 0;
CGFloat greenNormalized = 0;
CGFloat blueNormalized = 0;
CGPoint currentTrianglePoint = (CGPoint){j / scale, (height - i) / scale};
[self calculateCurrentValuesAtGiventPoint:currentTrianglePoint
sideLength:sideLength
sideOne:&redNormalized
sideTwo:&greenNormalized
sideThree:&blueNormalized];
int32_t red = redNormalized * 0xFF;
int32_t green = greenNormalized * 0xFF;
int32_t blue = blueNormalized * 0xFF;
char *pixel = data + (j + i * alignedPixelsPerRow) * numberOfComponents;
*pixel = red;
*(pixel + 1) = green;
*(pixel + 2) = blue;
*(pixel + 3) = 0xFF;
}
}
CGImageRef cgImage = CGBitmapContextCreateImage(ctx);
image = [[UIImage alloc] initWithCGImage:cgImage];
CGContextRelease(ctx);
CGImageRelease(cgImage);
return image;
}
+ (void)calculateCurrentValuesAtGiventPoint:(CGPoint)point
sideLength:(CGFloat)length
sideOne:(out CGFloat *)sideOne
sideTwo:(out CGFloat *)sideTwo
sideThree:(out CGFloat *)sideThree {
CGFloat height = sin(M_PI / 3) * length;
if (sideOne != NULL) {
// Side one is at 0, 0
CGFloat currentDistance = sqrt(point.x * point.x + point.y * point.y);
if (currentDistance != 0) {
CGFloat a = point.y / point.x;
CGFloat b = 0;
CGFloat c = -height / (length / 2);
CGFloat d = 2 * height;
CGPoint intersection = (CGPoint){(d - b) / (a - c), (a * d - c * b) / (a - c)};
CGFloat currentH = sqrt(intersection.x * intersection.x + intersection.y * intersection.y);
*sideOne = 1 - currentDistance / currentH;
} else {
*sideOne = 1;
}
}
if (sideTwo != NULL) {
// Side two is at w, 0
CGFloat currentDistance = sqrt(pow((point.x - length), 2) + point.y * point.y);
if (currentDistance != 0) {
CGFloat a = point.y / (point.x - length);
CGFloat b = height / (length / 2);
CGFloat c = a * -point.x + point.y;
CGFloat d = b * -length / 2 + height;
CGPoint intersection = (CGPoint){(d - c) / (a - b), (a * d - b * c) / (a - b)};
CGFloat currentH = sqrt(pow(length - intersection.x, 2) + intersection.y * intersection.y);
*sideTwo = 1 - currentDistance / currentH;
} else {
*sideTwo = 1;
}
}
if (sideThree != NULL) {
// Side three is at w / 2, w * sin60 degrees
CGFloat currentDistance = sqrt(pow((point.x - length / 2), 2) + pow(point.y - height, 2));
if (currentDistance != 0) {
float dy = point.y - height;
float dx = (point.x - length / 2);
if (fabs(dx) > FLT_EPSILON) {
CGFloat a = dy / dx;
CGFloat b = 0;
CGFloat c = a * -point.x + point.y;
CGFloat d = 0;
CGPoint intersection = (CGPoint){(d - c) / (a - b), (a * d - b * c) / (a - b)};
CGFloat currentH = sqrt(pow(length / 2 - intersection.x, 2) + pow(height - intersection.y, 2));
*sideThree = 1 - currentDistance / currentH;
} else {
*sideThree = 1 - currentDistance / height;
}
} else {
*sideThree = 1;
}
}
}
Here is a triangle image produced by this code:

Calculate distance from a lat/lon coord to a street

I have the coordinates for a street, for example:
CLLocationCoordinate2D street[3];
street[0] = CLLocationCoordinate2DMake(-17.3521, 145.5898);
street[1] = CLLocationCoordinate2DMake(-17.3518, 145.5910);
street[2] = CLLocationCoordinate2DMake(-17.3515, 145.5917);
And a location that is fairly close to the street (about 60 meters):
CLLocationCoordinate2D location = CLLocationCoordinate2DMake(-17.3525, 145.5911);
How can I calculate the distance between the location and position along the street's path?
I'm not looking for the distance to the closest point in the array, I want the distance to the closest position in between the points.
EDIT
It's easier to describe my question with a picture:
street is the three red dots
location is the blue dot
I want to calculate the length of the yellow line in meters.
Take a look at this site: link.
It shows different types of distance measuring with latitude and longitude coordinates and even some code examples (in javascript).
If you have the find the crow distance between two locations, make CLLocation object of two coords, then
CLLocationDistance meters = [newLocation distanceFromLocation:oldLocation];
and if you have find the actual road distance divide the two coords to several coords in straight line and find the distance and add them up.
Here is my attempt to solve this, but I'm not sure if it's the best way?
// http://www.cprogramto.com/c-program-to-find-shortest-distance-between-point-and-line-segment/
double FindDistanceToSegment(double x1, double y1, double x2, double y2, double pointX, double pointY)
{
double diffX = x2 - x1;
float diffY = y2 - y1;
if ((diffX == 0) && (diffY == 0))
{
diffX = pointX - x1;
diffY = pointY - y1;
return sqrt(diffX * diffX + diffY * diffY);
}
float t = ((pointX - x1) * diffX + (pointY - y1) * diffY) / (diffX * diffX + diffY * diffY);
if (t < 0)
{
//point is nearest to the first point i.e x1 and y1
diffX = pointX - x1;
diffY = pointY - y1;
}
else if (t > 1)
{
//point is nearest to the end point i.e x2 and y2
diffX = pointX - x2;
diffY = pointY - y2;
}
else
{
//if perpendicular line intersect the line segment.
diffX = pointX - (x1 + t * diffX);
diffY = pointY - (y1 + t * diffY);
}
//returning shortest distance
return sqrt(diffX * diffX + diffY * diffY);
}
-
CLLocationCoordinate2D street[3];
street[0] = CLLocationCoordinate2DMake(-17.3521, 145.5898);
street[1] = CLLocationCoordinate2DMake(-17.3518, 145.5910);
street[2] = CLLocationCoordinate2DMake(-17.3515, 145.5917);
CLLocationCoordinate2D location = CLLocationCoordinate2DMake(-17.3525, 145.5911);
CLLocationDegrees distanceDegrees = CGFLOAT_MAX;
for (NSUInteger nodeIndex = 1; nodeIndex < 3; nodeIndex++) {
CLLocationCoordinate2D nodeCoord = street[nodeIndex];
CLLocationCoordinate2D prevNodeCoord = street[nodeIndex - 1];
CLLocationDegrees distanceToCurrent = FindDistanceToSegment(prevNodeCoord.longitude, prevNodeCoord.latitude, nodeCoord.longitude, nodeCoord.latitude, location.longitude, location.latitude);
if (distanceToCurrent < distanceDegrees)
distanceDegrees = distanceToCurrent;
}
CLLocationDistance distance = distanceDegrees * 111111; // 1.0 degree is approximately 111,111 meters
NSLog(#"%f", distance); // 78.15 meters

touch object GLKIT or OpenGL any functions?

I am looking for a description to touch my OpenGL object or get an Event if I touch it.
Have the GLKit or OpenGL ES some functions to use?
Or I have to calculate the position of my Object and have to compare it with the coordination ob my Touch?
There is no built in one, but here is a ported version of the gluProject function that will put a point from your object in screen coordinates, so you can see if your touch is near that point:
GLKVector3 gluProject(GLKVector3 position,
GLKMatrix4 projMatrix,
GLKMatrix4 modelMatrix,
CGRect viewport
)
{
GLKVector4 in;
GLKVector4 out;
in = GLKVector4Make(position.x, position.y, position.z, 1.0);
out = GLKMatrix4MultiplyVector4(modelMatrix, in);
in = GLKMatrix4MultiplyVector4(projMatrix, out);
if (in.w == 0.0) NSLog(#"W = 0 in project function\n");
in.x /= in.w;
in.y /= in.w;
in.z /= in.w;
/* Map x, y and z to range 0-1 */
in.x = in.x * 0.5 + 0.5;
in.y = in.y * 0.5 + 0.5;
in.z = in.z * 0.5 + 0.5;
/* Map x,y to viewport */
in.x = in.x * (viewport.size.width) + viewport.origin.x;
in.y = in.y * (viewport.size.height) + viewport.origin.y;
return GLKVector3Make(in.x, in.y, in.z);
}
- (GLKVector2) getScreenCoordOfPoint {
GLKVector3 out = gluProject(self.point, modelMatrix, projMatrix, view.frame);
GLKVector2 point = GLKVector2Make(out.x, view.frame.size.height - out.y);
return point;
}

Different x and y speed (acceleration) in cocos2d?

I want to create a visible object with a trajectory using standard actions (CCMoveBy and e.t.c) which is similar to:
x = sin(y)
My code:
CCMoveBy *moveAction1 = [CCMoveBy actionWithDuration:1.5 position:ccp(300, 0)];
CCEaseInOut *easeInOutAction1 = [CCEaseInOut actionWithAction:moveAction1 rate:2];
CCMoveBy *moveAction2 = [CCMoveBy actionWithDuration:1.5 position:ccp(-300, 0)];
CCEaseInOut *easeInOutAction2 = [CCEaseInOut actionWithAction:moveAction2 rate:2];
CCMoveBy *moveAction3 = [CCMoveBy actionWithDuration:1.5 position:ccp(0, -32)];
CCSpawn *moveActionRight = [CCSpawn actionOne:easeInOutAction1 two:moveAction3];
CCSpawn *moveActionLeft = [CCSpawn actionOne:easeInOutAction2 two:moveAction3];
CCSequence *sequenceOfActions = [CCSequence actionOne:moveActionRight two:moveActionLeft];
CCRepeatForever *finalMoveAction = [CCRepeatForever actionWithAction:sequenceOfActions];
[enemy runAction:finalMoveAction];
This code shows move down only. The problem is that object has a different x and y accelerations and I don't know how to combine them
UPDATED
- (void)tick:(ccTime)dt
{
CGPoint pos = self.position;
pos.y -= 50 * dt;
if (pos.y < activationDistance) {
pos.x = 240 + sin(angle) * 140;
angle += dt * 360 * 0.007;
if (angle >= 360) {
angle = ((int)angle) % 360;
}
}
self.position = pos;
}
It is my current solution. I can increase activationDistance to adjust the object trajectory. But I want to setup an initial value of the angle variable.
I use numbers instead of variables because they are used inside this function only.
SOLVED
To change the initial angle:
angle = point.x < 240 ? -asin((240 - point.x) / 140) : asin((point.x - 240) / 140);
the main problem was my tiled map has its own coordinates and cover 320x320 part of the screen only
I think it will be easier for you to just do it in your frame update method (the one I assume you schedule for updating your objects. So why not just do :
- (void)tick:(ccTime)dt {
CGPoint pos = myObject.position;
pos.x = <desired x> + sin(angle);
pos.y = pos.y - y_acceleration * dt;
angle += dt * 360 * x_acceleration;
if (angle >= 360)
angle = ((int)angle) % 360;
myObject.position = pos;
}
And you can apply the same for the y axis of the object

Resources