javascripthtmlmathcanvasz-axis

How to calculate positions on a z-axis


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?


Solution

  • 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.