# using trigonometric functions to adjust values to be from -1 to 1

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.

• Something like $\frac2{\pi}\arctan(\cot\,\pi x)$? – J. M. is a poor mathematician Aug 1 '11 at 6:47
• What is this y function? It seems like it's somehow related (maybe even inverse proportional) to the deviation of the segments of x values from the desired. That is, it seems that as y approaches $\pm$1 the x curve behaves as needed, but as it approaches 0 the x curve "compresses" (evenly from both sides). Gauging it with my eyes means you'd probably want something similar to f(t)=x(t)*c*(1-|y(t)|) for some constant c, but I can't really say for sure without knowing what y actually means... – user864940 Aug 1 '11 at 6:59
• Seeing the functions that produce this would probably not help much, but I've added some information to the original post at the end. – HappyEngineer Aug 2 '11 at 0:07
• J.M., that doesn't seem to do it. It makes things look all weird. Now that I've included a link to the csv in the message above feel free to try it out. (Thanks to both of you for helping me by the way! The math for this is pretty difficult to get correct. I've spent a lot of time tweaking this.) – HappyEngineer Aug 2 '11 at 0:29
• Now, what do you mean by the pitch, the roll, and the azimuth? I got used to the idea of an "airplane cockpit view" when the azimuth would be the direction of the meridian to which the plane body projects, the pitch would be the angle at which the body of the plane is tilted towards or away from the ground and the roll would be the angle of rotation of the wings from the "horizontal" position. However, with this interpretation, dz cannot depend on the roll and your dz does, so you mean something else. Can you explain what exactly? – fedja Aug 5 '11 at 0:39

As I mentioned in the last edit of the text of the question I've worked out something that seems to work. I worked with a mathematician to get the original code, but that was months ago and I don't know how to contact him again, so I'll just make do with my results.

The important thing is that when I play the game the plotted objects float around on screen in a way that seems natural. If it's incorrect then it's below the threshold that I can sense it while playing.