I have some code which is projecting 3D points onto a 2D surface based on the user's viewpoint. The top graph shows the actual points that I get from my code. The y values seem to be correct, but the x values are off. The blue x values in the top graph should be more like the bottom graph (stretching from 1 to -1).
I need to somehow take the blue x and orange y values from the top graph and get the desired x values that you see in the bottom graph. I don't know much about curve fitting. Does anyone know any combination of trigonometric functions that I can apply to the top values to get the desired x values in the bottom graph?
.
EDIT: To get the x and y values requires a full page of code which takes x y z of a point, x y z of the camera, azimuth pitch roll of the camera and somehow churns out an x,y coordinate which is appropriate for displaying the point on the screen. I was hoping that just knowing the values of X and Y relative to each other would be enough to allow us to unsqueeze the x line.
If it helps, I've uploaded a CSV file containing the points used to make this graph at: http://g42.org/temp/projectionXY.csv
Those points represent calculations starting at -45 degrees horizontal and -45 degrees vertical and then progressing along the horizontal by 5 degree increments until it reaches +45. It then increments the vertical by 5 degrees and starts again.
In other words, the camera is pointed down 45 degrees and left 45 degrees and then is moved along a horizontal path until it reaches right 45 degrees and then goes up 5 degrees and repeats.
For what it's worth, here's the actual code.
double ax = obj.p().x(); double ay = obj.p().y(); double az = obj.p().z(); double cx = northPole.x(); double cy = northPole.y(); double cz = northPole.z(); double pi180 = Math.PI / 180; { // Adjust ax, ay, az to bring the point into the frame of reference of the fixed camera double ax2, ay2, az2; // Rotate the point clockwise around the z-axis by its longitude ax2 = ax * Math.cos(cam.lon()*pi180) + ay * Math.sin(cam.lon()*pi180); ay2 = -ax * Math.sin(cam.lon()*pi180) + ay * Math.cos(cam.lon()*pi180); ax = ax2; ay = ay2; // Rotate around y-axis by minus (90 - latitude) ax2 = ax * Math.cos((90-cam.lat())*pi180) - az * Math.sin((90-cam.lat())*pi180); az2 = ax * Math.sin((90-cam.lat())*pi180) + az * Math.cos((90-cam.lat())*pi180); ax = ax2; az = az2; } double cosOx = Math.cos(pitch*pi180); double sinOx = Math.sin(pitch*pi180); double cosOy = Math.cos(roll*pi180); double sinOy = Math.sin(roll*pi180); double cosOz = Math.cos(azimuth*pi180); double sinOz = Math.sin(azimuth*pi180); double p1 = sinOz * ay + cosOz * ax; double p2 = cosOz * ay - sinOz * ax; double dz = cosOx * (cosOy * az + sinOy * p1) - sinOx * p2; double dx = ( cosOy * p1) - sinOy * az; double dy = sinOx * (cosOy * az + sinOy * p1) + cosOx * p2; double bx = -dx/dz; double by = dy/dz;
In the above code, bx is the "x" on the graph image. by is the "y" on the graph image. The dz value is only used to determine if the value is in front of or behind the viewer. If it's positive then it's in front. Other than that I don't use its value for anything.
EDIT 2: I figured out the solution empirically. Since dy was being calculated correctly I just played around with manipulations that were similar to dy until I got a result for dx which looked correct. The end result is that the source code above would have the following two lines change:
double dx = sinOx * (-sinOy * az + cosOy * p1) + sinOx * p2; double bx = -dx/dz - 1.086087667;
Ideally I'd like to understand why this works, but honestly I'm just thrilled that I've finally got the projection code working like it's supposed to.
If anyone has an alternate way of expressing the above change which doesn't require the ugly -1.086087667 there, I'd love to see it.