Javascript  

 

 

 

 

 

Plot - 3D prjection on 2D

 

this code is created first by chatGPT on Feb 4 2023 (meaning using chatGPT 3.5) and then modified a little bit my me. The initial request that I put into chatGPT is as follows :

 

I wanted to draw some 3D math graphics using Javascript, but most of 3D graphics Javascript library available now looks too heavy and complicated to me. Those may be good for rendering complicated 3D object or basic gaming programe but not a good fit for 3D math plot. So I tried to build small library myself to fit my own/personal demand with the help of chatGPT. This example will be the base line of the library and other functionalities will be added to this baseline which would be generated with the help of chatGPT. The example of the extended functionalities are listed below.

 

NOTE : As you may notice, I put many of the function prototype in the requirement. When you are putting complicated / long requirement, I would suggest you to break them down into several functions and put those functions as a requirement. If you put the complicated requirement, it is highly likely for the chatGPT to hang up while writing the code. If you break down the requirement into several functions, you can easily let chatGPT complete the writing by saying 'rewrite this function'.

Write a javascript with the following requirement.

 

1. This is for projecting 3D coordinate onto 2d canvas

2. Use math.js library for math operations if necessary and assume that math.js file is located at mathjs/math.js

3. create a table at the center of the page. Gridlines of the table should be visible and thin.

4. the table has three rows and one column, make grid visible in thin line. Display every component in a cell in one line

5. In the first cell, put a textbox that can take in a number named as 'ViewDistance' and a button named as 'Draw'. The default value of ViewDistance is 5

6. In the second cell, put a textbox that can take in a number named as 'Azimuth' and put another textbox that can take in a number named as 'Elevation'. Both of the textbox should have spin button with step = 5

7. In the third cell, put a canvas with the size of 400x400 pixels. Do not put any background in the canvas. the background should be all white.

8. Draw following 3D object which are projected onto 2D canvas. This should apply the ViewDistance and ViewAngle (Azimuth, Elevation)

9. define a function called p2dtop3d(p3x,p3y,p3z,r,va,ve) that project a 3d point to a 2d point applying the distance and viewing angle.where (p3x,p3y,p3z) is a 3d point, r is distance from the origin and va is azimuth angle and ve is elevation in angle. all the angle is in degree

10. define a function called DrawCube(dim,cx,cy,cz,r,va,ve) that draw a cube, where dim indicates the length of each side of the cube and (cx,cy,cz) at the center of the cube, where r is distance from the origin and va is azimuth angle and ve is elevation in angle. all the angle is in degree. use p2dtop3d() for the projection.

11. define a function called DrawAxis(dim,r,va,ve) that draw axis at the origin (0,0,0). The range of x,y,z are all between -dim and +dim, where r is distance from the origin and va is azimuth angle and ve is elevation in angle. all the angle is in degree. use p2dtop3d() for the projection.

12. set the range of canvas to be -3 <= x <= 3 and -3 <= y <= 3 using scale() and translation()

13. do following when 'Draw' button is clicked.

     a). draw an x,y,z axis. The range of axis is all between -2 and +2 using DrawAxis() function

     b). draw a cube with every sides = 2 centered at the origin. Use drawCube()

14. javascript for the plot should be stored in a separate file

15. do not use css.

16. Create an html file that combines all of these components

NOTE : It is not guaranteed that you would have the same code as I got since chatGPT produce the answers differently depending on the context. And it may produce the different answers everytime you ask even with the exact the same question.

NOTE : I noticed that chatGPT misses some of the requirement or misinterprets.

NOTE : If you don't have any of your own idea for the request, copy my request and paste it into the chatGPT and put additional requests based on the output for the previous request. I would suggest to create a new thread in the chatGPT and put my request and then continue to add your own request.

 

Usage : Click on [Draw] button or change numbers in the inputbox by click on the spin button, you will get a 3D coordinate and a cube projected onto 2D canvas.

View Distance:
Azimuth: Elevation:

NOTE : red line indicates x axis, green line indicates y axis and blue line indicates z axis.

 

d3tod2_base.html

<!DOCTYPE html>

<html>

  <head>

 

  </head>

  <body>

    <table align="center" style="border-collapse: collapse; border: 1px solid black;">

      <tr>

        <td>

          View Distance: <input id="ViewDistance" type="number" value="5" onClick="draw()" onChange="draw()"/>

          <button id="Draw" onClick="draw()">Draw</button>

        </td>

      </tr>

      <tr>

        <td>

          Azimuth: <input id="Azimuth" type="number" value="30" onClick="draw()" onChange="draw()"/>

          Elevation: <input id="Elevation" type="number" value="30" onClick="draw()" onChange="draw()"/>

        </td>

      </tr>

      <tr>

        <td>

          <center><canvas id="myCanvas" width="400" height="400" ></canvas></center>

        </td>

      </tr>

    </table>

    <script src="mathjs/math.js"></script>

    <script src="d3tod2_base.js"></script>

  </body>

</html>

NOTE : I downloaded the math.js library from Mathjs homepage and placed in local path : mathjs/math.js

NOTE : I took me longer time for me to debug this code than other examples. It seems chatGPT is not well trained with the details of handling the details of canvas.

d3tod2_base.js.js

var canvas = document.getElementById("myCanvas");

var ctx = canvas.getContext("2d");

var r = document.getElementById("ViewDistance").value;

var va = document.getElementById("Azimuth").value;

var ve = document.getElementById("Elevation").value;

 

 

function p2dtop3d(p3x, p3y, p3z, r, va, ve) {

    // convert angle to radian

    va = va * Math.PI / 180;

    ve = ve * Math.PI / 180;

    

    // this is what I put during the debugging process, to swap y and z axis displayed on canvas.

    var tmp = p3y;

    p3y = p3z;

    p3z = tmp;

  

    // apply rotation on x axis

    var y1 = p3y * Math.cos(ve) + p3z * Math.sin(ve);

    var z1 = -p3y * Math.sin(ve) + p3z * Math.cos(ve);

 

    // apply rotation on y axis

    var x2 = p3x * Math.cos(va) + z1 * Math.sin(va);

    var z2 = -p3x * Math.sin(va) + z1 * Math.cos(va);

 

 

    // scale the point

    //px = x2 * r / (r + z2);

    //py = y1 * r / (r + z2);

    // Calculate the value of px

    const px = math.evaluate('x2 * r / (r + z2)', { x2, r, z2 });

 

    // Calculate the value of py

    const py = math.evaluate('y1 * r / (r + z2)', { y1, r, z2 });

  

    return {

      x: px,

      y: py

    };

}

 

// this is the part that I spent a lot of time to get the expected result. It generated the coordinates of each vertices

// of the cube corrected and stored them in points array. But the part to drawing lines connecting the vertices to form

// a cube was complete mess.

function DrawCube(dim, cx, cy, cz, r, va, ve) {

    var points = [];

    points.push([cx - dim / 2, cy - dim / 2, cz - dim / 2]);

    points.push([cx - dim / 2, cy - dim / 2, cz + dim / 2]);

    points.push([cx - dim / 2, cy + dim / 2, cz - dim / 2]);

    points.push([cx - dim / 2, cy + dim / 2, cz + dim / 2]);

    points.push([cx + dim / 2, cy - dim / 2, cz - dim / 2]);

    points.push([cx + dim / 2, cy - dim / 2, cz + dim / 2]);

    points.push([cx + dim / 2, cy + dim / 2, cz - dim / 2]);

    points.push([cx + dim / 2, cy + dim / 2, cz + dim / 2]);

 

    

    ctx.strokeStyle = "black";

    ctx.beginPath();

    for (var i = 0; i < 4; i++) {

        var p2d1 = p2dtop3d(points[i][0], points[i][1], points[i][2], r, va, ve);

        var p2d2 = p2dtop3d(points[i + 4][0], points[i + 4][1], points[i + 4][2], r, va, ve);

        ctx.moveTo(p2d1.x, p2d1.y);

        ctx.lineTo(p2d2.x, p2d2.y);

    }

    ctx.closePath();

    ctx.stroke();

 

    ctx.beginPath();

    for (var i = 0; i < 8; i+=2) {

        var p2d1 = p2dtop3d(points[i][0], points[i][1], points[i][2], r, va, ve);

        var p2d2 = p2dtop3d(points[i + 1][0], points[i + 1][1], points[i + 1][2], r, va, ve);

        ctx.moveTo(p2d1.x, p2d1.y);

        ctx.lineTo(p2d2.x, p2d2.y);

    }

    ctx.closePath();

    ctx.stroke();

 

    ctx.beginPath();

    for (var i = 0; i < 2; i+=1) {

        var p2d1 = p2dtop3d(points[i][0], points[i][1], points[i][2], r, va, ve);

        var p2d2 = p2dtop3d(points[i + 2][0], points[i + 2][1], points[i + 2][2], r, va, ve);

        ctx.moveTo(p2d1.x, p2d1.y);

        ctx.lineTo(p2d2.x, p2d2.y);

    }

    ctx.closePath();

    ctx.stroke();

 

    ctx.beginPath();

    for (var i = 4; i < 6; i+=1) {

        var p2d1 = p2dtop3d(points[i][0], points[i][1], points[i][2], r, va, ve);

        var p2d2 = p2dtop3d(points[i + 2][0], points[i + 2][1], points[i + 2][2], r, va, ve);

        ctx.moveTo(p2d1.x, p2d1.y);

        ctx.lineTo(p2d2.x, p2d2.y);

    }

    ctx.closePath();

    ctx.stroke();

 

}

 

 

function DrawAxis(dim, r, va, ve) {

    

    // draw x-axis

    ctx.strokeStyle = "red";

    ctx.beginPath();

    var px = p2dtop3d(-dim, 0, 0, r, va, ve).x;

    var py = p2dtop3d(-dim, 0, 0, r, va, ve).y;

    ctx.moveTo(px, py);

 

    var px = p2dtop3d(dim, 0, 0, r, va, ve).x

    var py = p2dtop3d(dim, 0, 0, r, va, ve).y

    ctx.lineTo(px, py);

    ctx.stroke();

    

    // draw y-axis

    ctx.strokeStyle = "green";

    ctx.beginPath();

    var px = p2dtop3d(0, -dim, 0, r, va, ve).x

    var py = p2dtop3d(0, -dim, 0, r, va, ve).y

    ctx.moveTo(px, py);

 

    var px = p2dtop3d(0, dim, 0, r, va, ve).x

    var py = p2dtop3d(0, dim, 0, r, va, ve).y

    ctx.lineTo(px, py);

    ctx.stroke();

    

    // draw z-axis

    ctx.strokeStyle = "blue";

    ctx.beginPath();

    var px = p2dtop3d(0, 0, -dim, r, va, ve).x

    var py = p2dtop3d(0, 0, -dim, r, va, ve).y

    ctx.moveTo(px, py);

 

    var px = p2dtop3d(0, 0, dim, r, va, ve).x

    var py = p2dtop3d(0, 0, dim, r, va, ve).y

    ctx.lineTo(px, py);

    ctx.stroke();

}

  

 

function draw() {

 

    var r = document.getElementById("ViewDistance").value;

    var va = document.getElementById("Azimuth").value;

    var ve = document.getElementById("Elevation").value;

 

    var s = 1;

    var dim = 2;

    var cx = 0, cy = 0, cz = 0;

    ctx.clearRect(0, 0, canvas.width, canvas.height);

    ctx.save();

    ctx.lineWidth = 1/(canvas.width/4);

    ctx.scale(s*canvas.width / 4, -s*canvas.height / 4);

    ctx.translate(s*2, -s*2);

    ctx.strokeStyle = "black";

    DrawAxis(2, r, va, ve);

    DrawCube(dim, cx, cy, cz, r, va, ve);

    ctx.restore();

 

};