I need an equation for equal movement along an ellipse - objective-c

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];

Related

Metal Fragment Shader, access current framebuffer color

I am writing a metal shader.
All I want is to access the current color of the fragment.
For example.
At the end of my fragment shader, when I put
return currentColor + 0.1, for example
The result will be screen going from black to white at FPS.
This is a basic program that draws a triangle strip that fills the screen.
Ultimate aim is writing a path tracer inside the shader, I have done this with opengl + glsl.
I am having trouble with the buffers. I thought an easy solution is to just pass the current output color back to the shader and average it in there.
These are the shaders:
#include <metal_stdlib>
using namespace metal;
#import "AAPLShaderTypes.h"
vertex float4 vertexShader(uint vertexID [[vertex_id]], constant AAPLVertex *vertices [[buffer(AAPLVertexInputIndexVertices)]], constant vector_uint2 *viewportSizePointer [[buffer(AAPLVertexInputIndexViewportSize)]], constant vector_float4 * seeds) {
float4 clipSpacePosition = vector_float4(0.0, 0.0, 0.0, 1.0);
float2 pixelSpacePosition = vertices[vertexID].position.xy;
vector_float2 viewportSize = vector_float2(*viewportSizePointer);
clipSpacePosition.xy = pixelSpacePosition / (viewportSize / 2.0);
return clipSpacePosition;
}
fragment float4 fragmentShader()
{
// I want to do this
// return currentColor + 0.1;
return float4(1.0, 1.0, 1.0, 1.0);
}
No worries, the answer was staring me in the face the whole time.
Pass in the current color into the fragment shader with float4 color0 [[color(0)]]
With these 2 options set
renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionLoad;
My problem was not the color, it was in calculating the average.
In my case I could not do regular average, as in add up all the colors so far and divide by the number of samples.
What I actually needed to do was calculate a Moving average.
https://en.wikipedia.org/wiki/Moving_average#Cumulative_moving_average

converting meter into pixel unit

i am trying to convert convert distance from meter to pixel in ros node, with pcl library and kinect xbox. I was using below code to access euclidean coordinates of every point from kinect inside ros node, which is in meter. But i wanted to get this measurments in pixel unit. What should i do?
void
cloud_cb (const sensor_msgs::PointCloud2ConstPtr& input)
{
pcl::PointCloud<pcl::PointXYZRGB> output;
pcl::fromROSMsg(*input,output );
for(int i=0;i<=400;i++)
{
for(int j=0;j<=400;j++)
{
p[i][j] = output.at(i,j);
ROS_INFO("\n p.z = %f \t p.x = %f \t p.y = %f",p[i][j].z,p[i][j].x,p[i][j].y);
}
}
sensor_msgs::PointCloud2 cloud;
pcl::toROSMsg(output,cloud);
pub.publish (cloud);
}
Here P[raw][col] is a Point structure which contains the x,y,z coordinates value in meter, which i want to convert in pixel unit. As i see the value of pixel unit is not constant, so cant use any value found in google.
I got similar question here: Kinect depth conversion from mm to pixels, but it has no solution.
There's a problem with trying to convert meters to pixels. Pixels aren't a standard unit. The physical size of 1 pixel varies on different devices depending on screen resolution and size of a screen.
If you know the resolution of the screen the conversion is still non-trivial.
const int L = 1920; //screen width
const int H = 1280; //screen height
for(int i=0;i<=L;i++){
for(int j=0;j<=H;j++){
p[i][j] = output.at(i*400/L,j*400/H);
}
}
Thus for every pixel you'll have a depth value corresponding to the depth value in the map. This will need some int conversion and improvement.

Collision response for rectangles

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.

Center coordinate of a coordinate region

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.

Graphics - How may I know if a line is visible onscreen taking account its width

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: