I want to write a c++ program that will calculate collision between sphere and plane.
The rule is that the angle of the falling object equals to angle of reflection.
What do I have for sphere:
//sphere coordinates and radius
float x;
float y;
float z;
float r;
//sphere velocity vector projections
float vx;
float vy;
float vz;
Plane is described by plane equation coefficients:
float A;
float B;
float C;
float D;
With sphere-plane collision detection I have no problem. But how to find velocity after collision?
What did I find:
So, ultimately I need to calculate updated values for vx vy vz.
#Beta’s answer on c++:
float wl = sqrt(plane->A*plane->A+plane->B*plane->B+plane->C+plane->C); // “W” vector length
float nx = plane->A/wl; //Normal components
float ny = plane->B/wl;
float nz = plane->C/wl;
float scope = (sphere->vx*nx + sphere->vy*ny + sphere->vz*nz)*2; // 2(V . n)
nx = nx*scope; // 2(V . n)n
ny = ny*scope;
nz = nz*scope;
sphere->vx -= nx; // V' = V - 2(V . n)n
sphere->vy -= ny;
sphere->vz -= nz;
The equation defining the plane is
Ax + By + Cz + D = 0
So the vector normal to the plane is
W = (A, B, C)
Normalize it:
n = W/|W|
Now take the velocity vector:
V = (vx, vy, vz)
Its component normal to the plane is
Vn = (V . n) n
and the rest of it, the part parallel to the plane is
Vp = V - Vn
We want to reverse the normal component and leave the parallel component unchanged:
V' = -Vn + Vp
which works out to
V' = V - 2(V . n)n
Related
I have a line with two points in latitude and longitude
A: 3.222895, 101.719751
B: 3.227511, 101.724318
and 1 point
C: 3.224972, 101.722932
How can I calculate minimum distance between point C and a line consists of point A and B?
It will be convenient if you can provide the calculation and objective-c code too. The distance is around 89 meters (using ruler in Google Earth).
Thanks to mimi and this great article http://www.movable-type.co.uk/scripts/latlong.html but they don't give the whole picture. Here is a detail one. All this points are collected using Google Earth using Placemark to mark the locations. Make sure lat/long are set to decimal degrees in Preferences.
lat A = 3.222895
lon A = 101.719751
lat B = 3.222895
lon B = 101.719751
lat C = 3.224972
lon C = 101.722932
Earth radius, R = 6371
1. First you have to find the bearing from A to C and A to B.
Bearing formula
bearingAC = atan2( sin(Δλ)*cos(φ₂), cos(φ₁)*sin(φ₂) − sin(φ₁)*cos(φ₂)*cos(Δλ) )
bearingAB = atan2( sin(Δλ)*cos(φ₂), cos(φ₁)*sin(φ₂) − sin(φ₁)*cos(φ₂)*cos(Δλ) )
φ is latitude, λ is longitude, R is earth radius
2. Find A to C distance using spherical law of cosines
distanceAC = acos( sin(φ₁)*sin(φ₂) + cos(φ₁)*cos(φ₂)*cos(Δλ) )*R
3. Find cross-track distance
distance = asin(sin(distanceAC/ R) * sin(bearingAC − bearing AB)) * R
Objective-C code
double lat1 = 3.227511;
double lon1 = 101.724318;
double lat2 = 3.222895;
double lon2 = 101.719751;
double lat3 = 3.224972;
double lon3 = 101.722932;
double y = sin(lon3 - lon1) * cos(lat3);
double x = cos(lat1) * sin(lat3) - sin(lat1) * cos(lat3) * cos(lat3 - lat1);
double bearing1 = radiansToDegrees(atan2(y, x));
bearing1 = 360 - ((bearing1 + 360) % 360);
double y2 = sin(lon2 - lon1) * cos(lat2);
double x2 = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lat2 - lat1);
double bearing2 = radiansToDegrees(atan2(y2, x2));
bearing2 = 360 - ((bearing2 + 360) % 360);
double lat1Rads = degreesToRadians(lat1);
double lat3Rads = degreesToRadians(lat3);
double dLon = degreesToRadians(lon3 - lon1);
double distanceAC = acos(sin(lat1Rads) * sin(lat3Rads)+cos(lat1Rads)*cos(lat3Rads)*cos(dLon)) * 6371;
double min_distance = fabs(asin(sin(distanceAC/6371)*sin(degreesToRadians(bearing1)-degreesToRadians(bearing2))) * 6371);
NSLog(#"bearing 1: %g", bearing1);
NSLog(#"bearing 2: %g", bearing2);
NSLog(#"distance AC: %g", distanceAC);
NSLog(#"min distance: %g", min_distance);
Actually there's a library for this. You can find it here https://github.com/100grams/CoreLocationUtils
Calculate bearing for each: C to A , and C to B:
var y = Math.sin(dLon) * Math.cos(lat2);
var x = Math.cos(lat1)*Math.sin(lat2) -
Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
var brng = Math.atan2(y, x).toDeg();
dLon= lon2-lon1;
Calculate cross-track distance:
var dXt = Math.asin(Math.sin(distance_CB/R)*Math.sin(bearing_CA-bearing_CB)) * R;
R is the radius of earth, dXt is the minimum distance you wanted to calculate.
Code to carry out this calculation is posted at here.
This implements an accurate solution in terms of ellipsoidal geodesics.
For the basic geodesic calculations, you can use
GeographicLib or the port of these algorithms to C which are included in version 4.9.0 of PROJ.4. This C interface is documented here.
Here's the result of compiling and running intercept.cpp:
$ echo 3.222895 101.719751 3.227511 101.724318 3.224972 101.722932 | ./intercept
Initial guess 3.225203 101.7220345
Increment 0.0003349040566247297 0.0003313413822354505
Increment -4.440892098500626e-16 0
Increment 0 0
...
Final result 3.225537904056624 101.7223658413822
Azimuth to A1 -135.1593040635131
Azimuth to A2 44.84069593652217
Azimuth to B1 134.8406959363608
Distance to line is 88.743m:
$ echo 3.224972 101.722932 3.225537904056624 101.7223658413822 | GeodSolve -i
-45.15927221 -45.15930407 88.743
See post here:
https://stackoverflow.com/a/33343505/4083623
For distance up to a few thousands meters I would simplify the issue from sphere to plane.
Then, the issue is pretty simply as a easy triangle calculation can be used:
We have points A and B and look for a distance X to line AB. Then:
Location a;
Location b;
Location x;
double ax = a.distanceTo(x);
double alfa = (Math.abs(a.bearingTo(b) - a.bearingTo(x))) / 180
* Math.PI;
double distance = Math.sin(alfa) * ax;
If you know how to calculate the distance of two points, get the distances between each two points, you get AB, AC, and BC. You want to know the closest distance between point C and line AB.
First get the value of P
P=(AB+BC+AC)/2
Using P, you need to get S
S=SQRT((P(P-AC)(P-AB)(P-AC))
SQRT means square root. Then you get what you want by
2*S/AB
I write this question because I don't understand a piece of code in an example provided by Cocos2D for iPhone:
-(CGAffineTransform) nodeToParentTransform
{
b2Vec2 pos = body_->GetPosition();
float x = pos.x * PTM_RATIO;
float y = pos.y * PTM_RATIO;
if ( ignoreAnchorPointForPosition_ ) {
x += anchorPointInPoints_.x;
y += anchorPointInPoints_.y;
}
// Make matrix
float radians = body_->GetAngle();
float c = cosf(radians);
float s = sinf(radians);
if( ! CGPointEqualToPoint(anchorPointInPoints_, CGPointZero) ){
x += c*-anchorPointInPoints_.x + -s*-anchorPointInPoints_.y;
y += s*-anchorPointInPoints_.x + c*-anchorPointInPoints_.y;
}
// Rot, Translate Matrix
transform_ = CGAffineTransformMake( c, s,
-s, c,
x, y );
return transform_;
}
It's in the PhysicsSprite.mm file.
Maybe it's because I'm very bad with space geometry, but if someone can explain me, I very appreciate.
Thanks a lot.
if( ! CGPointEqualToPoint(anchorPointInPoints_, CGPointZero) ){
x += c*-anchorPointInPoints_.x + -s*-anchorPointInPoints_.y;
y += s*-anchorPointInPoints_.x + c*-anchorPointInPoints_.y;
}
The above piece of code simply rotates the xy coordinate axes anticlockwise by $180-\theta$
degrees, and adds the new coordinates to the previous x,y coordinates that were obtained from the piece of code before the above mentioned lines.
http://en.wikipedia.org/wiki/Rotation_of_axes provides the rotation of axes formula
Draw a circle in KML
How do you take the GPS coordinates of a point on the globe (say in decimal degree format) and generate the coordinates for a polygon approximating a circle centred on that point?
A polygon with 20+ data points looks like a circle. The more data points - the better looking the circle.
I am writing a program that will generate KML and dont know how to calculate the coordinates of the polygon vertices.
Example of data inputs:
Latitude, Longitude, Circle radius (in feet), NumberOfDataPoints
26.128477, -80.105149, 500, 20
I don't know if this is the simplest solution and it assumes the world is a sphere.
Define:
R is the radius of the sphere (i.e. the earth).
r is the radius of the circle (in the same units).
t is the angle subtended by a great-circle arc of length r at the centre of the sphere so t=r/R radians.
Now suppose the sphere has radius 1 and is centred at the origin.
C is a unit vector representing the centre of the circle.
Imagine a circle round the North pole and consider the point where the plane of the circle intersects the line from the centre of the earth to the North pole. Clearly this point will be somewhere below the North pole.
K is the corresponding point "below" C (i.e. where the plane of your circle intersects C) so K=cos(t)C
s is the radius of the circle measured in 3D space (i.e. not on the sphere) so s=sin(t)
Now we want points on the circle in 3D space with centre K, radius s and lying in the plane passing through and perpendicular to K.
This answer (ignore the rotation stuff) explains how to find a basis vector for the plane (i.e. a vector orthogonal to the normal K or C). Use the cross product to find a second.
Call these basis vectors U and V.
// Pseudo-code to calculate 20 points on the circle
for (a = 0; a != 360; a += 18)
{
// A point on the circle and the unit sphere
P = K + s * (U * sin(a) + V * cos(a))
}
Convert each point to spherical coordinates and you are done.
Being bored, I coded this up in C#. The results are plausible: they are in a circle and lie on the sphere. Most of the code implements a struct representing a vector. The actual calculation is very simple.
using System;
namespace gpsCircle
{
struct Gps
{
// In degrees
public readonly double Latitude;
public readonly double Longtitude;
public Gps(double latitude, double longtitude)
{
Latitude = latitude;
Longtitude = longtitude;
}
public override string ToString()
{
return string.Format("({0},{1})", Latitude, Longtitude);
}
public Vector ToUnitVector()
{
double lat = Latitude / 180 * Math.PI;
double lng = Longtitude / 180 * Math.PI;
// Z is North
// X points at the Greenwich meridian
return new Vector(Math.Cos(lng) * Math.Cos(lat), Math.Sin(lng) * Math.Cos(lat), Math.Sin(lat));
}
}
struct Vector
{
public readonly double X;
public readonly double Y;
public readonly double Z;
public Vector(double x, double y, double z)
{
X = x;
Y = y;
Z = z;
}
public double MagnitudeSquared()
{
return X * X + Y * Y + Z * Z;
}
public double Magnitude()
{
return Math.Sqrt(MagnitudeSquared());
}
public Vector ToUnit()
{
double m = Magnitude();
return new Vector(X / m, Y / m, Z / m);
}
public Gps ToGps()
{
Vector unit = ToUnit();
// Rounding errors
double z = unit.Z;
if (z > 1)
z = 1;
double lat = Math.Asin(z);
double lng = Math.Atan2(unit.Y, unit.X);
return new Gps(lat * 180 / Math.PI, lng * 180 / Math.PI);
}
public static Vector operator*(double m, Vector v)
{
return new Vector(m * v.X, m * v.Y, m * v.Z);
}
public static Vector operator-(Vector a, Vector b)
{
return new Vector(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
}
public static Vector operator+(Vector a, Vector b)
{
return new Vector(a.X + b.X, a.Y + b.Y, a.Z + b.Z);
}
public override string ToString()
{
return string.Format("({0},{1},{2})", X, Y, Z);
}
public double Dot(Vector that)
{
return X * that.X + Y * that.Y + Z * that.Z;
}
public Vector Cross(Vector that)
{
return new Vector(Y * that.Z - Z * that.Y, Z * that.X - X * that.Z, X * that.Y - Y * that.X);
}
// Pick a random orthogonal vector
public Vector Orthogonal()
{
double minNormal = Math.Abs(X);
int minIndex = 0;
if (Math.Abs(Y) < minNormal)
{
minNormal = Math.Abs(Y);
minIndex = 1;
}
if (Math.Abs(Z) < minNormal)
{
minNormal = Math.Abs(Z);
minIndex = 2;
}
Vector B;
switch (minIndex)
{
case 0:
B = new Vector(1, 0, 0);
break;
case 1:
B = new Vector(0, 1, 0);
break;
default:
B = new Vector(0, 0, 1);
break;
}
return (B - minNormal * this).ToUnit();
}
}
class Program
{
static void Main(string[] args)
{
// Phnom Penh
Gps centre = new Gps(11.55, 104.916667);
// In metres
double worldRadius = 6371000;
// In metres
double circleRadius = 1000;
// Points representing circle of radius circleRadius round centre.
Gps[] points = new Gps[20];
CirclePoints(points, centre, worldRadius, circleRadius);
}
static void CirclePoints(Gps[] points, Gps centre, double R, double r)
{
int count = points.Length;
Vector C = centre.ToUnitVector();
double t = r / R;
Vector K = Math.Cos(t) * C;
double s = Math.Sin(t);
Vector U = K.Orthogonal();
Vector V = K.Cross(U);
// Improve orthogonality
U = K.Cross(V);
for (int point = 0; point != count; ++point)
{
double a = 2 * Math.PI * point / count;
Vector P = K + s * (Math.Sin(a) * U + Math.Cos(a) * V);
points[point] = P.ToGps();
}
}
}
}
I have written Polycircles, small open-source package in Python that does it. It uses geographiclib for the geospatial calculation.
I'm building a cocos2d iPhone game with lots of bullets and moving enemies and I'm detecting collisions between them. Every sprite can be represented by a circle for collision purposes. I'm considering the following options:
1) Simple Sphere Detection
I detect like this at regular intervals:
-(BOOL) isCollidingSphere:(CCSpriteExt*) obj1 WithSphere:(CCSprite *) obj2
{
float minDistance = obj1.radius + obj2.radius;
float dx = obj2.position.x - obj1.position.x;
float dy = obj2.position.y - obj1.position.y;
if (! (dx > minDistance || dy > minDistance) )
{
float actualDistance = sqrt( dx * dx + dy * dy );
return (actualDistance <= minDistance);
}
return NO;
}
2) Box2d for collision detection only
I create a Box2d body for all sprites as shown in this tutorial: http://www.raywenderlich.com/606/how-to-use-box2d-for-just-collision-detection-with-cocos2d-iphone
My question is simple: If my priority is optimisation, which approach is faster?
Thanks!
If all you need is distance/radius based collision checks, you don't need a physics engine.
You should get rid of the sqrt though. First of all, you're using the square root function that works on doubles. For the float version use sqrtf.
To get rid entirely of the square root, make sure your objects store their radius squared (radiusSquared = radius * radius). That way you don't have to take the square root anymore:
-(BOOL) isCollidingSphere:(CCSpriteExt*) obj1 WithSphere:(CCSprite *) obj2
{
float r1 = obj1.radius;
float r2 = obj2.radius;
float minDistanceSquared = r1 * r1 + r2 * r2 + 2 * r1 * r2;
float dx = obj2.position.x - obj1.position.x;
float dy = obj2.position.y - obj1.position.y;
float actualDistanceSquared = dx * dx + dy * dy;
return (actualDistanceSquared <= minDistanceSquared);
}
Given vertices V1 (x1,y1,z1), V2 (x2,y2,z2), V3 (x3,y3,z3) of a triangle T, I have to find z coordinate of a point by it's x,y coordinate if I know that (x,y) lies within projection of triangle Tp (x1,y1), (x2,y2), (x3,y3).
Actually, triangle plane in 3D is defined by equation: Ax+By+Cz+D=0, and I can find z = (D-Ax-By)/C
The problem is that A, B, C, D are too expensive to calculate in run-time:
A = y1(z2-z3) + y2(z3-z1) + y3(z1-z2)
B = z1(x2-x3) + z2(x3-x1) + z3(x1-x2)
C = x1(y2-y3) + x2(y3-y1) + x3(y1-y2)
D = -x1(y2*z3 – y3*z2) – x2(y3*z1 – y1*z3) – x3 (y1*z2 – y2*z1)
Is it possible to calculate A, B, C, D using, say, opengl shaders? Are there optimized algorithms to find plane coefficients?
The technique is called Barycentric coordinates but the wiki page is pretty hard to follow -
See http://www.alecjacobson.com/weblog/?p=1596
float calcY(vec3 p1, vec3 p2, vec3 p3, float x, float z) {
float det = (p2.z - p3.z) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.z - p3.z);
float l1 = ((p2.z - p3.z) * (x - p3.x) + (p3.x - p2.x) * (z - p3.z)) / det;
float l2 = ((p3.z - p1.z) * (x - p3.x) + (p1.x - p3.x) * (z - p3.z)) / det;
float l3 = 1.0f - l1 - l2;
return l1 * p1.y + l2 * p2.y + l3 * p3.y;
}
Code from http://www.gamedev.net/topic/597393-getting-the-height-of-a-point-on-a-triangle/ - carefull about computer graphics vs maths use of Y Z
ps. I Don't know of any faster version using shaders. One quick dirty+solution is to render the triangle using colors based on the height of the vertices and pick the pixel color at your X,Y - in practice this never ends up being much faster on a desktop machine, don't know about opengl-es