How to calculate positions on a z-axis

2.3k Views Asked by At

I drew a simple 3-coordinate graph in canvas below

I drew a simple 3-coordinate graph in canvas above;

I defined a few constants to work with

var width = window.innerWidth * 0.5 , height = window.innerWidth * 0.5;
var cx = width / 2, cy = height / 2, cz = height / 2, blcx = 0, blcy = height, brcz = height, brcx = width, brcy = height;

var ySegment = height / 30;
var xSegment = width / 30; 
var xSegmentRatio = xSegment / width; 
var ySegmentRatio = ySegment / height;

cx and cy and cz all refer to the center of the canvas. blcx means bottom-left-corner-x, brcy is bottom-right-corner-y, etc. I realize this is a bit hack-ish, as this is the first attempt to do this, but if you bear with me, there is a real concept I am trying to grasp here.

and then drew the red lines like this:

(function() {
    var gridCx   = cx,       gridCy   = cy,        gridCz = cz;
    var gridBlCx = blcx,     gridBlCy = brcy;
    for (var i = cx; i < width; i++) {
        gridCx += xSegment;
        gridBlCx += ySegment;
        gridCzx -= gridCzx * (xSegmentRatio / ySegmentRatio);
        ctx.beginPath();
        ctx.moveTo(gridCx, cy);
        ctx.lineTo(gridBlCx, height);
        ctx.strokeStyle="#FF0000";
        ctx.stroke();
    }
})();

I tried this:

gridCzx -= gridCzx * xSegmentRatio;
gridCzy += gridCzy * ySegmentRatio;
ctx.beginPath();
ctx.moveTo(gridCzx, gridCzy);
ctx.lineTo(width, gridCzy);
ctx.strokeStyle = "#ff0000";
ctx.stroke();

and got this:

enter image description here

But I realized I am missing some fundamental mathematical concept. Thanks for any insight you can give! My basic question here is: How do I select a point on the z-axis given a distance away from the center?

2

There are 2 best solutions below

3
On BEST ANSWER

Transforming 3 dimensions into 2 dimensions

Axis tell us how to move

To find a point in the first dimension x, move along the x axis, from left to right. To find a point in the second dimension y you must integrate both the first and second dimensions, thus 1st x from left to right, once you have that then 2nd along the y axis down the screen.

With each dimension you rely on the positioning of the previous dimension. It also relies on the axis, the x and y axis are at 90 deg to each other, but they could be at 45 or 120, it would not make any difference to finding 2D coords of a point. First along X axis then Y along the y axis.

Vector functions

Because the display is only 2D the axis can be represented as 2D vectors. The length of the vector tells the scale of the axis. Thus for the x axis if I define the axis as a 2D (display coords) vector (2,0) then I am saying that it goes 2 across and 0 down for each unit in the x coordinate. If I want to be at x coordinate 10 I multiply it by the axis to get the screen position.

Thus to the code...

function screenCoord(x,y){  // creates a coordinate in screen space
                            // screen space is always in pixels
                            // screen space always has the origin (0,0) at the top left
    return {
       x : x,
       y : y,
    }
}

function screenVector(x,y){ // a vector in screen space it points in a direction
                            // and its is in pixels 
    return {                // basically identical to the last function 
       x : x,
       y : y,
    }
}

So let's define the X axis that I had (2,0) scaled up by 2

var xAxis = screenVector(2,0);

Now a x position say 10

var xPos = 10

To find its location we need to get the screen coordinates along the x axis. We do that by multiplying xAxis by xPos. To make it easier we can create a vector multiply function

function multiply(vector, value){
     var x = vector.x * value; // multiply the vector x by value
     var y = vector.y * value; // multiply the vector y by value
     return screenCoord(x,y)
}

Now to find the first dimensional pos of xPos

var screenPos = multiply(xAxis, xPos); // returns the screen position of x = 10

Now to do the 2nd dimension we add that to the previous. Let's define a function to add a vector to another

function add(vector1, vector2){ // adds two vectors returning a new one
     var x = vector1.x + vector2.x;
     var y = vector1.y + vector2.y;
     return screenCoord(x,y);
}

So now lets create the y axis

var yAxis = new screenVector(0,2); // y goes down the screen and has no x change

and the y pos

var posY = 10;

So now lets do it from x

var screenPosX = multiply(xAxis,posX); // get the x position on the screen
var screenPosY = multiply(yAxis,posY); // get the y position on the screen

Now we add the results of the two coordinates

var screenXY = add(screenPosX,screenPosY);

And we have a coordinate on the screen of x = 10 and y = 10 (which in this case turns out to be at pixel location 20 across 20 down.

The 3rd dimension

Now it does not take much to guess what happens for the 3rd z dimension. For x along x axis, then y along the y axis and then z along the z axis

So define the z axis

var yAxis = new screenVector(1,-1); // z axis along the diagonal from bottom left to top right

Now the z coord

var posZ = 10;

So to find our 3d position, x along its axis, then add y along its and then add z along its axis

var screenPosX = multiply(xAxis,posX); // get the x position on the screen
var screenPosY = multiply(yAxis,posY); // get the y position on the screen
var screenPosZ = multiply(zAxis,posZ); // get the z position on the screen

need to add them together

var screenXY = add(screenPosX,screenPosY);

Then z

var screenXYZ = add(screenPosXY,screenPosZ);

and there you have how to do the conversion from one set of coordinates to another. It is called a transform

The origin

We are missing one last bit of information. The origin. This is where on the screen the the coords 0,0,0 (x,y,z) will be. It is the last part of the transform and is in screen coords (x,y)

var origin = screenCoords(100,500); // set the origin at 100 pixels across 500 down

From the last calculation we got the screenXYZ coordinate in screen space, we need to add the origin to it

screenXYZ = add(screenXYZ ,origin);

Now you can draw the pixel at coordinates (10,10,10) (x,y,z) from the origin in on the 2d screen.

The Matrix

Hope that helps, if you understand that you have just learnt how to use a 3D transformation matrix. It holds the x,y,z axis as a set of three 2D directions, and the coordinates of the origin. The matrix does the very same functional steps, just it does it in a simple array that makes it more efficient and following some matrix rules allows very complex transforms. For example if you want to rotate, all you need to do is change the direction of the axies and you have rotated the object. To change the scale just change the length of the axis, to move the object just change the position of the origin.

0
On

It turns out, JavaScript's Math.cos() and Math.sin() are super helpful in this scenario. The way I had to think about it was as if there were successive concentric circles with radiuses starting at my grid segment length and doubling for each succession. From there, I google a bit about how to find the point on a circle given a degree. It turns out circles, like triangles, have degrees, i.e. radians, and the one for the direction of my z-index is 5PI/4. Therefore, my function looks something like this:

for (var i = 0; i < zDistance; i++) {
    var r = xSegment * i;
    ctx.beginPath();
    ctx.arc(cx, cy, r, 0, 2 * Math.PI);
    ctx.strokeStyle="white";
    ctx.stroke();
    var zRadian = {
        divided: function() {
            return 5 * Math.PI
        }, 
        divisor: function() {
            return 4;
        }
    }
    var zRadian = zRadian.divided() / zRadian.divisor();
    var x = cx + r * Math.cos(zRadian);
    var y = cy - r * Math.sin(zRadian);
    ctx.beginPath();
    ctx.fillText('(' + x + ', ' + y + ')', x, y);
    ctx.stroke();
}

And here is the result:

enter image description here