I have 2 coordinates, a top left and a bottom right. I would like to find the center point of the region. Right now I have the following method to calculate it. The center point is way off. When I call the method with
[self.map setRegionTopLeft: CLLocationCoordinate2DMake(21.57524, -157.984514)
bottomRight: CLLocationCoordinate2DMake(21.309766, -157.80766)
animated:YES];
It should center on the island of Oahu in the State of Hawaii, USA. I found this math here so I'm not sure whats going on.
Code A - This is way off. It's not putting me anywhere near the island.
- (CLLocationCoordinate2D)centerPointFromRegionTopLeft:(CLLocationCoordinate2D)topLeft
bottomRight:(CLLocationCoordinate2D)bottomRight
{
CLLocationCoordinate2D centerPoint;
centerPoint.longitude = (topLeft.longitude + bottomRight.longitude) / 2;
if (fabs(bottomRight.longitude - topLeft.longitude) > 180)
{
if (centerPoint.longitude > 0)
{
centerPoint.longitude = centerPoint.longitude + 180;
} else {
centerPoint.longitude = centerPoint.longitude - 180;
}
}
centerPoint.latitude = asin((sin(bottomRight.latitude) + sin(topLeft.latitude))/2);
return centerPoint;
}
I've also, originally, tried this method. Its just what popped in my head when I thought center of a rectangle. If gets me a lot closer to what the center should be - I can see the island - but its still off.
Code B - Original code I tried. This is much closer to what I expected but still off.
- (CLLocationCoordinate2D)centerPointFromRegionTopLeft:(CLLocationCoordinate2D)topLeft
bottomRight:(CLLocationCoordinate2D)bottomRight
{
CLLocationCoordinate2D centerPoint;
centerPoint.latitude = ((topLeft.latitude + bottomRight.latitude) / 2);
centerPoint.longitude = ((topLeft.longitude + bottomRight.longitude) / 2);
return centerPoint;
}
So given a coordinate region (topLeft, bottomRight) how to I get the center coordinate? The idea is I should be able to give any 2 coordinates and get the center coordinate.
Update* Code B works. I had my topLeft and bottomRight wrong. Code A puts me very south and a little east of where it should.
You need the middle of L(longitude) and B(latitude). For B the problem is around the pole, but as you set it, you simply can't "put the cap on the pole", so, really no problems here.
Middle(B1,B2)=(B1+B2)/2.
But L is much worse. L can jump from -179 to -179. And another problem : the middle of (-179,+179) should be 180, and middle(-1,+1) should be 0. I.e., we should always choose middle along shorter way between opposite points, not around the whole Earth.
We should move the zero meridian so, that the difference between L1,L2 will be smaller, than 180, make normal middle of them and then return the zero meridian back.
Let L1
if L2-L1>180, let's choose L2 for the new zero meridian.
shift=L2
L2=L2-shift, L1=L1+360-shift. Now, notice, L1-L2<180!
LmShifted=(L1+L2)/2
Lm=LmShifted+shift.
If we'll take these formulas together, we'll have:
Lm=(L1-L2+360)/2+L2
if L2-L1<180, Lm=(L1+L2)/2
The problem is when L2-L1=180. In this case you have two opposite meridians, dividing the Earth in two, and for the role of the middle both "quarter" meridian, to the right and to the left, fit. It's up to you, what to choose.
Related
I've been working on a physics engine for about a week now, being stuck for several days trying to work out how to resolve collisions.
My problem is that if there's a box stuck in the middle of 2 other boxes, or between a box and a wall, my application will get stuck in a while loop. It wont resolve the collisions.
This is my code (note: if collision is right side, it means that object A is colliding against object B with its right side. Distance is negative because the objects are inside eachother, and it's in x or y axis depending on side of collision. If you need more code, for example the collision class, which is simply a container of the 2 objects, i can provide that.):
edit: Code edited with new way of dealing with collisions:
//Move colliding objects so they don't collide anymore.
while (getCollidingAmount(objectVector)){
for (int i = 0; i < objectVector.size(); i++){
PhysicsObject* A = objectVector[i];
if (objectVector[i]->getPhysicsType() != PhysicsType::staticT && A->_collision.size() > 0){
Collision collision = A->_collision[A->getDeepestPenetrationCollisionIndex(A->_collision)];
PhysicsObject* B = collision.getObject();
switch (collision.getSide()){
case SideOfCollision::left:
case SideOfCollision::top:
//Opposite velocity
if (A->_saveVelocity.x < 0 && B->_saveVelocity.x > 0){
long double percentageOfVelocity = std::min(abs(B->_saveVelocity.x), abs(A->_saveVelocity.x)) /
std::max(abs(B->_saveVelocity.x), abs(A->_saveVelocity.x));
A->_position.x -= percentageOfVelocity*collision.getVectorPenetration().x;
A->_position.y -= percentageOfVelocity*collision.getVectorPenetration().y;
}
else{
A->_position.x -= collision.getVectorPenetration().x;
A->_position.y -= collision.getVectorPenetration().y;
}
break;
case SideOfCollision::right:
case SideOfCollision::bottom:
//Opposite velocity
if (A->_saveVelocity.x > 0 && B->_saveVelocity.x < 0){
long double percentageOfVelocity = 1 - std::min(abs(B->_saveVelocity.x), abs(A->_saveVelocity.x)) /
std::max(abs(B->_saveVelocity.x), abs(A->_saveVelocity.x));
A->_position.x -= percentageOfVelocity*collision.getVectorPenetration().x;
A->_position.y -= percentageOfVelocity*collision.getVectorPenetration().y;
}
else{
A->_position.x -= collision.getVectorPenetration().x;
A->_position.y -= collision.getVectorPenetration().y;
}
break;
}
updateCollisions(objectVector);
}
}
}
Update
Something wrong with my trigonometry in bottom and top collisions:
sf::Vector2<long double> Collision::getVectorPenetration() const{
long double x;
long double y;
long double velX = _object->getVelocity().x;
long double velY = _object->getVelocity().y;
long double angle = atan2(velY, velX);
if (_side == SideOfCollision::left || _side == SideOfCollision::right){
x = getDistance();
y = x * tan(angle);
return sf::Vector2<long double>(x, y);
}
else if (_side == SideOfCollision::top || _side == SideOfCollision::bottom){
y = getDistance();
x = y / tan(angle);
return sf::Vector2<long double>(x, y);
}
}
Update 2
Thanks to Aiman, i solved my issue. Updated my collisionResponse code aswell to match my new way of dealing with collisions. I'm having another issue now where gravity makes it so i can't move in X direction when touching another object. If anyone familiar with this issue wants to give any tips to solve it, i appreciate it :).
Update 3
So it seems gravity is not actually the problem since i can swap gravity to the x axis, and then be able to slide boxes along the walls. There seems to still be something wrong with the trigonometry.
I can think of many ways to approach the problem.
1-**The more complicated one is to **introduce friction. Here is how I'd implement it, though this is untested and there is a chance I missed something in my train of thought.
Every shape gets a friction constant, and according to those your objects slide when they collide.
First, you need to get the angle that is perpendicular to your surface. To do this, you just get the arctan of the the surface's normal slope. The normal is simply -1/m, where m is the slope of your surface (which you is the ratio/quotient of how much the surface extends in y to/by how much it extends in x). Let's call this angle sNormal for "surface normal". We may also need sAngle-"surface angle" for later (you find that by arctan(m)). There remains some ambiguity in the angle that has to do with whether you're talking about the 'front' or the 'back' of the surface. You'll have to deal with that manually.
Next, you need the angle of the trajectory your object flies in, which you already know how to find (atan2(y,x)). We'll call this angle oAngle for "object's surface angle". Next, you calculate deltaAngle = sNormal - oAngle. This angle represents how much momentum was not blocked completely by the surface. A deltaAngle of 0 means all momentum is gone, and a value of PI/2 or 90 means the 2 surfaces are in parallel touching each other not blocking any momentum at all. Anything in between, we interpolate:
newSpeed = objectSpeed * deltaAngle/(PI/2);
newVelocity.x = cos(sAngle) * objectSpeed;
newVelocity.y = sin(sAngle) * objectSpeed;
Now this assumes 0 friction. If we let a friction of 1 be the maximum friction which doesn't allow the object to "slide", we modify the newSpeed before we apply the newVelocity values, like so: newSpeed *= (1-friction);.
And there we have it! Just give your platform a friction value of less than 1 and your box will be able to slide. If you're dealing with upright boxes, then the surface angle is PI for top wall, 0 for the bottom, PI/2 for the right and -PI/2 for the left wall.
2-The simpler option is to subtract gravity from the object's y-velocity in the solver's calculation.
i am programing something like planets moving around the Sun, and to move planets i am using a function
CGPointMake(object.center.x + 1, sqrt(75*75*150*150 - 75*75*(object.center.x - 300)*(object.center.x - 300))/150 + 150)
using eliptic equation where a = 150, b = 75, p = 300, q = 150, but when object closes to x = around 450 its speed rises, i guess that is because of pitagora because the path its passes is c = sqrt((x-x0)^2*(y-y0)^2)
i notice my c is always around 0.5, but when it gets on the end of x domain it rises up to 0.8 so i need either a programatic or mathematical solution to make object move at same speed around an eliptic curve
Thank you!
If you want the real thing
then the planets closer to the primary focus point (center of mass of stellar system ... very close to star) are moving faster so use Kepler's equation here: C++ implementation of mine. Do not forget to check out all the sub-links in that answer you can find there everything you need.
If you want constant speed instead
Then use parametric ellipse equation
x(a)=x0+rx*cos(a)
y(a)=y0+ry*sin(a)
where a is angle <0,2.0*PI> (x0,y0) is the ellipse center and (rx,ry) are the ellipse semi axises (radii).
if a is incremented with constant speed then the area increase is constant so the a is the mean circular angle not the visual on ellipse !!! For more info look here:
Issue with ellipse angle calculation and point calculation
[edit1] as MartinR pointed out the speed is not constant
so here is approximation with his formula for speed. Ellipse is axis aligned defined by x0,y0,rx,ry (rx>=ry) the perimeter aproximation l:
h=(rx-ry)/(rx+ry); h*=3.0*h; l=M_PI*(rx+ry)*(1.0+(h/(10.0+sqrt(4.0-h))));
if you want to have n chunks of equal sized steps along the perimeter then
l/=n;
initial computations:
double x0,y0,rx,ry,n,l,h;
x0=Form1->ClientWidth>>1; // center is centered on form
y0=Form1->ClientHeight>>1;
rx=200; // semiaxises rx>=ry !!!
ry=75;
n=40.0; // number of chunks per ellipse (1/speed)
//l=2.0*M_PI*sqrt(0.5*((rx*rx)+(ry*ry))); // not accurate enough
h=(rx-ry)/(rx+ry); h*=3.0*h; l=M_PI*(rx+ry)*(1.0+(h/(10.0+sqrt(4.0-h)))); // this is more precise
l/=n; // single step size in units,pixels,or whatever
first the slow bruteforce attack (black):
int i;
double a,da,x,y,xx,yy,ll;
a=0.0;
x=x0+rx*cos(a);
y=y0+ry*sin(a);
for (i=n;i>0;i--)
{
xx=x; yy=y;
for (da=a;;)
{
a+=0.001;
x=x0+rx*cos(a);
y=y0+ry*sin(a);
ll=sqrt(((xx-x)*(xx-x))+((yy-y)*(yy-y)));
if (ll>=l) break;
} da=a-da;
scr->MoveTo(5.0+50.0*a,5.0);
scr->LineTo(5.0+50.0*a,5.0+300.0*da);
scr->MoveTo(x0,y0);
scr->LineTo(xx,yy);
scr->LineTo(x ,y );
ll=sqrt(((xx-x)*(xx-x))+((yy-y)*(yy-y)));
scr->TextOutA(0.5*(x+xx)+20.0*cos(a),0.5*(y+yy)+20.0*sin(a),floor(ll));
}
Now the approximation (Blue):
a=0.0; da=0;
x=x0+rx*cos(a);
y=y0+ry*sin(a);
for (i=n;i>0;i--)
{
scr->MoveTo(5.0+50.0*a,5.0+300.0*da);
xx=rx*sin(a);
yy=ry*cos(a);
da=l/sqrt((xx*xx)+(yy*yy)); a+=da;
scr->LineTo(5.0+50.0*a,5.0+300.0*da);
xx=x; yy=y;
x=x0+rx*cos(a);
y=y0+ry*sin(a);
scr->MoveTo(x0,y0);
scr->LineTo(xx,yy);
scr->LineTo(x ,y );
ll=sqrt(((xx-x)*(xx-x))+((yy-y)*(yy-y)));
scr->TextOutA(0.5*(x+xx)+40.0*cos(a),0.5*(y+yy)+40.0*sin(a),floor(ll));
}
This is clean ellipse step (no debug draws)
a=???; // some initial angle
// point on ellipse
x=x0+rx*cos(a);
y=y0+ry*sin(a);
// next angle by almost constant speed
xx=rx*sin(a);
yy=ry*cos(a);
da=l/sqrt((xx*xx)+(yy*yy)); a+=da;
// next point on ellipse ...
x=x0+rx*cos(a);
y=y0+ry*sin(a);
Here the output of comparison bruteforce and approximation:
[edit2] little precision boost
a,da=???; // some initial angle and step (last)
x=x0+rx*cos(a);
y=y0+ry*sin(a);
// next angle by almost constant speed
xx=rx*sin(a+0.5*da); // use half step angle for aproximation ....
yy=ry*cos(a+0.5*da);
da=l/sqrt((xx*xx)+(yy*yy)); a+=da;
// next point on ellipse ...
x=x0+rx*cos(a);
y=y0+ry*sin(a);
the half step angle in approximation lead to much closer result to bruteforce attack
Hmm...
You could fake something like this very easily with SpriteKit. N.B. your entire app doesn't have to use SpriteKit. You can fairly easily put an SKView into a non-SpriteKit app.
Anyway...
Create your planet...
SKSpritNode *planet = [SKSpritNode spriteNodeWithImageNamed:#"mars"];
[solarSystemView addChild:planet];
Create your elliptical path...
UIBezierPath *ellipse = [UIBezierPath bezierPathWithOvalInRect:/*your rect*/]; //or create it any other way.
Create an action...
SKAction *singleOrbit = [SKAction followPath:ellipse.CGPath speed:10];
SKAction *orbit = [SKAction repeatActionForever:singleOrbit];
Run the action...
[planet runAction:orbit];
One of the Cocos2D games I am working on has circular explosion effects. These explosion effects need to deal a percentage of their set maximum damage to all game characters (represented by rectangular bounding boxes as the objects in question are tanks) within the explosion radius. So this boils down to circle to rectangle collision and how far away the circle's radius is from the closest rectangle edge. I took a stab at figuring this out last night, but I believe there may be a better way. In particular, I don't know the best way to determine what percentage of damage to apply based on the distance calculated.
Note : All tank objects have an anchor point of (0,0) so position is according to bottom left corner of bounding box. Explosion point is the center point of the circular explosion.
TankObject * tank = (TankObject*) gameSprite;
float distanceFromExplosionCenter;
// IMPORTANT :: All GameCharacter have an assumed (0,0) anchor
if (explosionPoint.x < tank.position.x) {
// Explosion to WEST of tank
if (explosionPoint.y <= tank.position.y) {
//Explosion SOUTHWEST
distanceFromExplosionCenter = ccpDistance(explosionPoint, tank.position);
} else if (explosionPoint.y >= (tank.position.y + tank.contentSize.height)) {
// Explosion NORTHWEST
distanceFromExplosionCenter = ccpDistance(explosionPoint,
ccp(tank.position.x, tank.position.y + tank.contentSize.height));
} else {
// Exp center's y is between bottom and top corner of rect
distanceFromExplosionCenter = tank.position.x - explosionPoint.x;
} // end if
} else if (explosionPoint.x > (tank.position.x + tank.contentSize.width)) {
// Explosion to EAST of tank
if (explosionPoint.y <= tank.position.y) {
//Explosion SOUTHEAST
distanceFromExplosionCenter = ccpDistance(explosionPoint,
ccp(tank.position.x + tank.contentSize.width,
tank.position.y));
} else if (explosionPoint.y >= (tank.position.y + tank.contentSize.height)) {
// Explosion NORTHEAST
distanceFromExplosionCenter = ccpDistance(explosionPoint,
ccp(tank.position.x + tank.contentSize.width,
tank.position.y + tank.contentSize.height));
} else {
// Exp center's y is between bottom and top corner of rect
distanceFromExplosionCenter = explosionPoint.x - (tank.position.x + tank.contentSize.width);
} // end if
} else {
// Tank is either north or south and is inbetween left and right corner of rect
if (explosionPoint.y < tank.position.y) {
// Explosion is South
distanceFromExplosionCenter = tank.position.y - explosionPoint.y;
} else {
// Explosion is North
distanceFromExplosionCenter = explosionPoint.y - (tank.position.y + tank.contentSize.height);
} // end if
} // end outer if
if (distanceFromExplosionCenter < explosionRadius) {
/*
Collision :: Smaller distance larger the damage
*/
int damageToApply;
if (self.directHit) {
damageToApply = self.explosionMaxDamage + self.directHitBonusDamage;
[tank takeDamageAndAdjustHealthBar:damageToApply];
CCLOG(#"Explsoion-> DIRECT HIT with total damage %d", damageToApply);
} else {
// TODO adjust this... turning out negative for some reason...
damageToApply = (1 - (distanceFromExplosionCenter/explosionRadius) * explosionMaxDamage);
[tank takeDamageAndAdjustHealthBar:damageToApply];
CCLOG(#"Explosion-> Non direct hit collision with tank");
CCLOG(#"Damage to apply is %d", damageToApply);
} // end if
} else {
CCLOG(#"Explosion-> Explosion distance is larger than explosion radius");
} // end if
} // end if
Questions:
1) Can this circle to rect collision algorithm be done better? Do I have too many checks?
2) How to calculate the percentage based damage? My current method generates negative numbers occasionally and I don't understand why (Maybe I need more sleep!). But, in my if statement, I ask if distance < explosion radius. When control goes through, distance/radius must be < 1 right? So 1 - that intermediate calculation should not be negative.
Appreciate any help/advice
EDIT
My occasional negative result was due to a misplaced parenthesis.
damageToApply = (1 - (distanceFromExplosionCenter/explosionRadius)) * explosionMaxDamage;
Still looking for input on how to calculate explosion radius damage.
You can check the distance of the objects using ccpDistance:
float distance = ccpDistance(sprite1.position, sprite2.position);
I think the ccpDistance will always return a positive value. But if its't, then get the absolute value: fabsf(distance)
I am testing a background-loop animation where there will be to images both 1024x768 pixels in dimension, move leftwards, go offscreen, then jump back to the other side, and repeat.
I was able to do this by creating a constant speed for both background image to move (successful), and then I tried the following code to make it jump, but there was a problem:
if((background.center.x) < -511){
background.center = CGPointMake(1536, background.center.y);
}
if((background2.center.x) < -511){
background2.center = CGPointMake(1536, background2.center.y);
}
Somehow this is not working the way I expected. It leaves a few pixels of gap every time, and I am confused why. Does anyone know what's causing this to happen and how to fix it? Thanks!
It seems like you have forgotten to take into account the distance moved. The greater than expression might have been triggered because you moved to far. I guess your movement is larger than 1 pixel/frame.
I am not sure what kind of values that are feeding your movement but I think to take into account the movement you should do something like...
if ((background.center.x) < -511){
CGFloat dist = background.center.x + 512;
background.center = CGPointMake(1536+dist, background.center.y);
}
if ((background2.center.x) < -511){
CGFloat dist = background2.center.x + 512;
background2.center = CGPointMake(1536+dist, background2.center.y);
}
Rather than have the two images move (sort of) independently, I would keep track of a single backgroundPosition variable and then constantly update the position of both images relative to that one position. This should keep everything nice and tidy:
CGFloat const backgroundWidth = 1024;
CGFloat const backgroundSpeed = 2;
- (void)animateBackground {
backgroundPosition -= backgroundSpeed;
if (backgroundPosition < 0) {
backgroundPosition += backgroundWidth;
}
background1.center.x = backgroundPosition - backgroundWidth/2;
background2.center.x = backgroundPosition + backgroundWidth/2;
}
I'm doing some core graphics, and I wonder how I may know if a line will have some parts of it visible on screen.
Let's take a line going from x-5, y3 to x2, y-7. If it's 1 pixel wide, nothing will be displayed onscreen. If it's 15 pixels wide, some parts of it will be displayed.
How may I check that ?
If you have lines only you can work with the function below. Otherwise I would recommend to go through the whole length of your line and create in a specific distance an square of the line width size and check if it is inside your view. An example: If you have a line from x0y0 to x7y0. You would go to x1y0 create a square of your draw line size (in this example 15) and see if this overlaps your screen. Next go to x2y0 and so on. The advantage is it will even work with bezier curves (a little wiki information how bezier work will be enough).
// EDIT: (made a little bezier check function, should work, but haven't tested) And I don't think its more performance efficient to check each line before drawing:
- (void)bezierWithStart:(CGPoint)start cp1:(CGPoint)cp1 cp2:(CGPoint)cp2 end:(CGPoint)end withWidth:(float)wid {
for (float i = 0.0; i<=1.0; i+=0.05) { // how many steps
CGPoint chk1 = CGPointMake(start.x+((cp1.x-start.x)*i), start.y+((cp1.y-start.y)*i));
CGPoint chk2 = CGPointMake(cp1.x+((cp2.x-cp1.x)*i), cp1.y+((cp2.y-cp1.y)*i));
CGPoint chk3 = CGPointMake(cp2.x+((end.x-cp2.x)*i), cp2.y+((end.y-cp2.y)*i));
CGPoint chk4 = CGPointMake(chk1.x+((chk2.x-chk1.x)*i), chk1.y+((chk2.y-chk1.y)*i));
CGPoint chk5 = CGPointMake(chk2.x+((chk3.x-chk2.x)*i), chk2.y+((chk3.y-chk2.y)*i));
CGPoint cPoint = CGPointMake(chk4.x+((chk5.x-chk4.x)*i), chk4.y+((chk5.y-chk4.y)*i));
CGRect drawLine = CGRectMake(cPoint.x-(wid/2), cPoint.y-(wid/2), wid, wid);
// check if rect is in view
}
}
// EDIT end
But now lets go to the simple line function:
- (void)testLine:(CGPoint)fp toSecond:(CGPoint)sp withWidth:(float)wid {
float xratio = sp.x - fp.x;
float yratio = sp.y - fp.y;
double a = sqrt(((wid*wid)*(xratio*xratio))/((yratio*yratio)+(xratio*xratio)));
a/=2; // because line width goes in both direction
double b = (yratio/xratio)*a;
if ((xratio<0.0 && yratio<0.0) || (xratio>0.0 && yratio>0.0))b*=-1;
CGPoint diffFrom1 = CGPointMake(fp.x+a, fp.y+b);
CGPoint diffTo1 = CGPointMake(sp.x+a, sp.y+b);
a*=-1;
b*=-1;
CGPoint diffFrom2 = CGPointMake(fp.x+a, fp.y+b);
CGPoint diffTo2 = CGPointMake(sp.x+a, sp.y+b);
}
you will get 4 points. 2 lines, one above and one below the original line, half the size of your draw width. The calculation behind is to get the draw direction and for that the difference to the original line. But for those who want to get into it, heres my pre calculation: