First off apologies for the crude drawing, I am by no means well versed in 3D manipulation and have only a very basic understanding of matrices, so please explain everything as clearly as you can and make no assumptions of my level of knowledge.
I am currently gathering longitude and latitude data and converting it into Cartesian X,Y and Z with the following function:
public CartesianXYZ LonLat2Cartesian(double lon, double lat)
{
CartesianXYZ XYZ = new CartesianXYZ();
var R = 6371000;
XYZ.X = Convert.ToInt32(R * Math.Cos(lat) * Math.Cos(lon));
XYZ.Y = Convert.ToInt32(R * Math.Sin(lat) * Math.Cos(lon));
XYZ.Z = Convert.ToInt32(R * Math.Sin(lat));
return XYZ;
}
When plotting these on a graph, I have found that the Y axis varies quite significantly, even though these points are very close to each other, I assume this is due to the fact we are sat on a sphere and unless I was at the north pole, it would be considered slanted, more so the closer to the equator I was. (If this is not the case please do let me know)
Please see the below three points plotted in a 3D scatter graph
Point A would be considered the "central" point where the user is
Point B is a point directly to the south of the user
Point C is a point directly to the north of the user
What I hope to achieve is to rotate points B and C around point A, so that their Y values are the same and I can consider the graph a 2D representation of the points, I found by just changing the Y to the same I ended up with a fairly skewed version of the points.
The end goal is to use the heading of the user and find out if any of the points are within their field of view (for augmented reality)
Any assistance, helpful critique or links to useful articles would be greatly appreciated.
You got wrong equation (did not spot it before :) either) it should be:
public CartesianXYZ LonLat2Cartesian(double lon, double lat)
{
CartesianXYZ XYZ = new CartesianXYZ();
var R = 6371000;
XYZ.X = Convert.ToInt32(R * Math.Cos(lat) * Math.Cos(lon));
XYZ.Y = Convert.ToInt32(R * Math.Cos(lat) * Math.Sin(lon));
XYZ.Z = Convert.ToInt32(R * Math.Sin(lat));
return XYZ;
}
btw this is just sphere which our planet is not so if you need more precise result use WGS84:
How to convert a spherical velocity coordinates into cartesian
Related
I am using a C# port of libnoise with XNA (I know it's dead) to generate planets.
There is a function in libnoise that receives the coordinates of a vertex in a sphere surface (latitude and longitude) and returns a random value (from -1 to 1).
So with that value, I can change the height of each vertex on the surface of the sphere (the altitude), creating some elevation, simulating the surface of a planet (I'm not simply wrapping a texture around the sphere, I'm actually creating each vertex from scratch).
An example of what I have:
Now I want to animate the sphere, like this
But the thing is, libnoise only works with 3D noise.
The "planet" function maps the latitude and longitude to XYZ coordinates of a cube.
And I believe that, to animate a sphere like I want to, I need an extra coordinate there, to be the "time" dimension. Am I right? Or is it possible to do this with what libnoise offers?
OBS: As I mentioned, I'm using an UV sphere, not an icosphere or a spherical cube.
EDIT: Here is the algorithm used by libnoise to map lat/long to XYZ:
public double GetValue(double latitude, double longitude) {
double x=0, y=0, z=0;
double PI = 3.1415926535897932385;
double DEG_TO_RAD = PI / 180.0;
double r = System.Math.Cos(DEG_TO_RAD * lat);
x = r * System.Math.Cos(DEG_TO_RAD * lon);
y = System.Math.Sin(DEG_TO_RAD * lat);
z = r * System.Math.Sin(DEG_TO_RAD * lon);
return GetNoiseValueAt(x, y, z);
}
An n dimensional noise function takes n independent inputs (i0, i1, ..., in-1, in) & returns a value v, thus 3D noise is sufficient to generate a height map that varies over time. In your case the inputs would be longitude, latitude & time and the output would be the height offset.
The simple general algorithm would be:
at each time step (t){
for each vertex (v) on a sphere centered on some point (c){
calculate the longitude & latitude
get the scalar noise value (n) for the longitude, latitude & time
calculate the new vertex position (p) as follows p = ((v-c)n)+c
}
}
Note: this assumes you are not replacing/modifiying the original vertex values. You could either save a copy of them (uses less computation, but more memory) or recalculate them them based on a distance from c (uses less memory, but more computation). Also, you might get a smoother animation by calculating 2 (or more) larger time steps & interpolating to get the intermediate frames.
To the best of my knowledge, this solution should work for a UV sphere, an icosphere or a spherical cube.
Ok I think I made it.
I just added the time parameter to the mapped XYZ coordinates.
Using the same latitude and longitude but incrementing time by 0.01d gave me a nice result.
Here is my code:
public double GetValue(double latitude, double longitude, double time) {
double x=0, y=0, z=0;
double PI = 3.1415926535897932385;
double DEG_TO_RAD = PI / 180.0;
double r = System.Math.Cos(DEG_TO_RAD * lat);
x = r * System.Math.Cos(DEG_TO_RAD * lon);
y = System.Math.Sin(DEG_TO_RAD * lat);
z = r * System.Math.Sin(DEG_TO_RAD * lon);
return GetNoiseValueAt(x + time, y + time, z + time);
}
If someone has a better solution please share it!
Sorry for the late answer, but I couldn't find a satisfactory answer elsewhere online, so I'm writing this up for anyone who has this problem in the future.
What worked for me was using multiple 3d perlin noise sources, and combining them into 1 single noise source. Adding time to the xyz coordinates just creates a very noticeable effect of terrain moving in the (-1,-1,-1) direction.
Averaging over 4 uncorrelated noise sources does change the noise characteristics a bit, so you might have to adapt some factors to your use case.
This solution still isn't perfect, but I haven't seen any visual artifacts.
Code is C++ libnoise, but it should translate equally well to other languages.
noise::module::Perlin perlin_noise[4];
float get_height(ofVec3f p, float time) {
p*=2;
time /= 10 ;
return (perlin_noise[0].GetValue(p.x, p.y, p.z) +
perlin_noise[1].GetValue(p.x, p.y, time) +
perlin_noise[2].GetValue(p.x, time, p.z) +
perlin_noise[3].GetValue(time, p.y, p.z))/2;
}
Ideally, for a single 3d noise source, you want to multiply you x,y,z coords with a monotonic function of t, such that it explores a constantly expanding sphere surface of the noise source, but I haven't figured out the math yet..
Edit: the framework I use (openframeworks) has a 4d perlin noise function built in ofSignedNoise(glm::vec4)
I have successfully implemented the floor clip plane to measure the distance of left foot to the floor, which is fairly accurate. The problem I have is that as I move away from the camera (i.e. left foot Z axis is increased), the foot distance to the floor changes (increases).
Note: The floor itself is not tilted nor the Kinect stand.
I tested it with Kinect 1 and had the same result. The subject's head height (Y axis) also changes value as I move away or get closer to the camera. It does not matter of the camera is tilted or line of sight. the D value in the FloorClipPlane equation shows a constant number during the test.
A = bodyFrame.FloorClipPlane.X;
B = bodyFrame.FloorClipPlane.Y;
C = bodyFrame.FloorClipPlane.Z;
D = bodyFrame.FloorClipPlane.W;
distanceLeftFoot = A * leftFootPosX + B * leftFootPosY + C * leftFootPosZ + D;
Just to let you know, I have coordinate mapping between depth and colour. Not sure if that has anything to do with the issue.
The FloorClipPlane is expressed in hessian normal form - as explained in the docs. Specifically, your A, B, and C values compromise the unit vector from camera origin (center of the Kinect) to floor plane such that it produces a perpendicular intersection with the floor plane. D is the magnitude of that vector (distance from camera origin to floor plane).
Even if you think the floor is flat and the Kinect is parallel to the ground, you have a perspective warping problem which means the body location (measured in depth space) is going to change as you come closer and further.
To fix this you need to provide as input both your 3D coordinate values and the floor plane, which will then give you back what you want, a measured distance from floor plane to joint:
// j is your joint - left foot or any other joint
float x = j.Position.X;
float y = j.Position.Y;
float z = j.Position.Z;
float distance = (Math.Abs((x * floorPlane.X) + (y * floorPlane.Y) + (z * floorPlane.Z) + floorPlane.W))/((float)Math.Sqrt((Math.Pow(floorPlane.X,2)) + (Math.Pow(floorPlane.Y, 2)) + (Math.Pow(floorPlane.Z, 2))));
I hope this helps you. Can't elaborate further what influence your mapping from depth to color might be doing here without seeing what you are specifically doing
I'm using C#, I have a list of Vector points and would like to try and approximate how close they look like a circle.
Any ideas how to implement this or if someone else has?
Based on the "gestures" tag I guess you not only want to know how close are these points to the smallest-circle (search for "Smallest-circle problem"), but you have to be concerned also about their order and spread:
I would start with the distance from the smallest-circle. If they are too far, you're done, it's not a circle.
If they are close enough to your configured threshold, compute the angle between the vector defined by the circle center, first point and each other point (picture bellow)
Check that each angle is greater than the previous.
Check that difference between any two angles next to each other is not over some configured threshold.
Check that the last point is close enough to the first one.
You will probably think of some other checks eventually, so make it simple to extend.
Another possibility:
Find the centroid
(http://en.wikipedia.org/wiki/Centroid#Of_a_finite_set_of_points) of
the points
Determine the distance (radius) of each point from the location of
the centroid
Calculate the distribution and determine if the points
are within acceptable tolerances (e.g. standard deviation < ±0.05 × mean radius or something like that)
Without knowing more about the source of the points, it's hard to suggest the best solution.
These might be of use: http://link.springer.com/article/10.1007%2FBF02276879#page-1 and http://www.dtcenter.org/met/users/docs/write_ups/circle_fit.pdf. Those methods will give you the best fitting circle through the points, but you are still going to need to determine whether your data points are close enough for your purposes.
UPDATE: based on the 'gesture' tag, somebody has already implemented it: http://depts.washington.edu/aimgroup/proj/dollar/
1) Pick any three points from that list, find the center of their appropriate circle
We can do this, using triangle circumcircle construction method, you find the medians of all three sides (two are sufficient) and their intersection is the center of the circle. Something like this:
public PointF findCenter(PointF a, PointF b, PointF c)
{
float k1 = (a.Y - b.Y) / (a.X - b.X) //Two-point slope equation
float k2 = (a.Y - c.Y) / (a.X - c.X) //Same for the (A,C) pair
PointF midAB = new PointF((a.X + b.X) / 2, (a.Y + b.Y) / 2) //Midpoint formula
PointF midAC = new PointF((a.X + c.X) / 2, (a.Y + c.Y) / 2) //Same for the (A,C) pair
k1 = -1*k1; //If two lines are perpendicular, then the product of their slopes is -1.
k2 = -1*k2; //Same for the other slope
float n1 = midAB.Y - k1*midAB.X; //Determining the n element
float n2 = midAC.Y - k2*midAC.Y; //Same for (A,C) pair
//Solve y1=y2 for y1=k1*x1 + n1 and y2=k2*x2 + n2
float x = (n2-n1) / (k1-k2);
float y = k1*x + n1;
return new PointF(x,y);
}
2) Check if the other points are equivalently distanced from this center, if yes, you have a circle, if no, you don't.
P.S. I haven't tested the code, so be prepared to debug. Ask if you need anything else
Take any three points from your point set.
If the points are co-linear then, your point set doesn't all lie on a circle.
Find the circumcircle of the triangle. The diameter is given by: d = (a*b*c)/2*area. The center of the circle is the point of intersection of the perpendicular bisectors of the three sides.
Now for every remaining point in the point set, if the distance from the center is not equal to the radius then the points are not on a circle. You can speed up the calculations by comparing the square of the radius against the square of the distance between the given point and the center.
Ok so I have searched and searched for a solution to my problem, but non seem to fix it.
I need to make a game with a rotating "cannon", my cannon is a simple rectangle placed in the middle of my panel that I can rotate with my keyboard. It rotates around one edge. I want to shoot out of the edge on the other side. I have found the starting point of where to shoot my bullets by using:
x = a + dia * (float)Math.Cos(angle);
y = b + dia * (float)Math.Sin(angle)
where "a, b" is the center coordinate I rotate it around and "dia" is the diagonal of the rectangle and "angle" is the angle of the one half of my rectangle.
public float rotate = 0.0f;
g.TranslateTransform(a , b);
g.RotateTransform(rotate);
I have a own class for my bullets that I put in a List.
So far so good. But when I rotate my cannon, the bullets don't come out from the tip anymore..they just start appearing far off where I want them to. it's because of this code:
x = (float)((x * Math.Cos(rotate)) - (y * Math.Sin(rotate)));
y = (float)((x * Math.Sin(rotate)) + (y * Math.Cos(rotate)));
that's supposed to update the x, y coordinates of the tip of the cannon.
If I delete it, it just fires from the same spot(no shit).
Can someone please explain to me what code I need to write to update the X, Y so they come out of my rectangle edge? It's driving me crazy..
Edit:
Found my answer staring at the screen in the early mornings. I had no need for any "find new x, y coordinates". I simply made a updater that updated the original angle with the float number it needed to move a little bit each time i rotated it.
hah! so simple, yet so hard to see.
First of all,
x = (float)((x * Math.Cos(rotate)) - (y * Math.Sin(rotate)));
y = (float)((x * Math.Sin(rotate)) + (y * Math.Cos(rotate)));
needs to be something like:
float oldx = x;
float oldy = y;
x = (float)((oldx * Math.Cos(rotate)) - (oldy * Math.Sin(rotate)));
y = (float)((oldx * Math.Sin(rotate)) + (oldy * Math.Cos(rotate)));
your new values need to be based purely off the old values..
If there's any other problem after fixing this, it may be related to how the rectangle is translated on the plane.
Edit: If this were a code review, I'd say the solution I just gave isn't quite the best solution either (it just doesn't suffer from the bug you introduced by using the new value of x to calculate the new value of y). See, Math.Cos and Math.Sin are generally expensive operations compared to multiplication and addition. If you had a bunch of points that need transformed the same way, best to calculate Math.Sin(rotate) and Math.Cos(rotate) once and use those values for every point. This might be a good place to use the Flyweight pattern and define a class where an instance would hold all your points for a given object/context so that operations can be done in aggregate.
I am trying to make a user-defined arc with the Helix 3D toolkit. The user selects 3 points on the arc (start, middle, end) and the program finds the center of the circle and draws the arc from start to end. My problem is I'm not good at math and I am having problems making this work. My main problem is getting the start and end angles and having it draw arcs of all sizes accurately. Any help is appreciated. Here is my code:
private void Draw_Arc(object sender, MouseButtonEventArgs e)
{
linept = new List<Point3D>();
linept.Add(startPoint);
linept.Add(endPoint);
linept.Add((Point3D)GetPoints(e));
LinesVisual3D line = new LinesVisual3D();
line.Thickness = 2;
line.Color = Colors.Blue;
line.Points = linept;
port.Children.Add(line);
double startAngle, sweepAngle;
Point3D center = GetCenterOfArc(linept.ElementAt(0), linept.ElementAt(1), linept.ElementAt(2));
GetAngles(linept.ElementAt(0), linept.ElementAt(1), linept.ElementAt(2), out startAngle, out sweepAngle);
circle = new PieSliceVisual3D();
double RadiusX = Math.Abs(startPoint.X - center.X);
double RadiusY = Math.Abs(startPoint.Y - center.Y);
circle.Center = center;
if (RadiusX >= RadiusY)
circle.OuterRadius = RadiusX;
else
circle.OuterRadius = RadiusY;
circle.InnerRadius = circle.OuterRadius + 3;
circle.StartAngle = (180 / Math.PI * Math.Atan2(startPoint.Y - circle.Center.Y, startPoint.X - circle.Center.X));
circle.EndAngle = (180 / Math.PI * Math.Atan2(linept.ElementAt(2).Y - circle.Center.Y, linept.ElementAt(2).X - circle.Center.X));
port.Children.Add(circle);
}
I think that you have to know the center of the circle in order to know the starting and ending angle of the arc.
Say that you just have three points, and you want to find a circle that goes through all three, you basically have three equations with three variables:
(x-x0)^2 + (y-y0)^2 = R^2
(x-x1)^2 + (y-y1)^2 = R^2
(x-x2)^2 + (y-y2)^2 = R^2
Solving that can get a little tricky if you try to program that on your own and have average knowledge in math, but you can do it fairly easily using matrices. Read here for a bit information.
After you've solved the three equations, you should have X, Y, R.
X and Y will be the center point of the circle, and R - it's radius.
Now, as far as I remember, they count the arc's degrees starting from the positive X axis, going upwards. So you would need to calculate the angle between two lines - the line that stretches between the center to your floating point, and the line that stretches from your center point to the "limitless" right. You may just Google "calculate angle between two lines". Repeating that process for both your starting point and your ending point, will give each their respective entering/exiting angle.
The middle point isn't really used anymore, but the radius is. You just set it to be the radius and you're good to go.
I haven't really implemented anything - just giving you a fair direction. (and I bet that there's a much cleaner and nicer-to-work-with solution)