画布 3d 图 [英] Canvas 3d graph

查看:26
本文介绍了画布 3d 图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我做了一个 3d 图形来绘制其中的点.我已经绘制了 x、y 和 z 轴.您也可以通过按箭头键来旋转轴.现在我的问题是将轴标记为 x、y 和 z .我尝试使用填充文本在画布中添加文本.但是文本被添加到画布但不旋转.这是因为我猜我还没有为它设置旋转效果.那么我如何将旋转设置为文本,以便在轴旋转时文本也一起旋转.下面是我的代码.

I have done a 3d graph to plot points in it. I have drawn x,y and z axis. Also you can rotate the axis by pressing arrow keys.Now my problem marking the axis as x,y and z .I tried to add text in canvas by using filltext.But the text gets added to canvas but its not rotating.It is because i have not set rotation effect for it i guess.So how can i set the rotation to text so that when axis rotates the text also rotates together.Below is my code.

  <!DOCTYPE html>



  <html>

  <head>

 <title>Canvas Surface Rotation</title>

 <style>

  body {

    text-align: center;

  }



  canvas {

    border: 1px solid black;

  }

  </style>

  <script>   

  var constants = {

    canvasWidth: 600, // In pixels.

    canvasHeight: 600, // In pixels.

    leftArrow: 37,

    upArrow: 38,

    rightArrow: 39,

    downArrow: 40,

    xMin: -10, // These four max/min values define a square on the xy-plane that the surface will be plotted over.

    xMax: 10,

    yMin: -10,

    yMax: 10, 

    xDelta: 0.01, // Make smaller for more surface points. 

    yDelta: 0.01, // Make smaller for more surface points. 

    colorMap: ["#060"], // There are eleven possible "vertical" color values for the surface, based on the last row of http://www.cs.siena.edu/~lederman/truck/AdvanceDesignTrucks/html_color_chart.gif

    pointWidth: 2, // The size of a rendered surface point (i.e., rectangle width and height) in pixels.

    dTheta: 0.05, // The angle delta, in radians, by which to rotate the surface per key press.

    surfaceScale: 24 // An empirically derived constant that makes the surface a good size for the given canvas size.

  };



  // These are constants too but I've removed them from the above constants literal to ease typing and improve clarity.

  var X = 0;

  var Y = 1;

  var Z = 2;



  // -----------------------------------------------------------------------------------------------------  



  var controlKeyPressed = false; // Shared between processKeyDown() and processKeyUp().

  var surface = new Surface(); // A set of points (in vector format) representing the surface.



  // -----------------------------------------------------------------------------------------------------



  function point(x, y, z)

  /*

    Given a (x, y, z) surface point, returns the 3 x 1 vector form of the point.

  */

  {       

    return [x, y, z]; // Return a 3 x 1 vector representing a traditional (x, y, z) surface point. This vector form eases matrix multiplication.

  }



  // -----------------------------------------------------------------------------------------------------



  function Surface()

  /*

    A surface is a list of (x, y, z) points, in 3 x 1 vector format. This is a constructor function.

  */

  {

    this.points = []; // An array of surface points in vector format. That is, each element of this array is a 3 x 1 array, as in [ [x1, y1, z1], [x2, y2, z2], [x3, y3, z3], ... ]

  }



  // -----------------------------------------------------------------------------------------------------  



  Surface.prototype.equation = function(x, y)

  /*

    Given the point (x, y), returns the associated z-coordinate based on the provided surface equation, of the form z = f(x, y).

  */

  {

    var d = Math.sqrt(x*x + y*y); // The distance d of the xy-point from the z-axis.



    return 4*(Math.sin(d) / d); // Return the z-coordinate for the point (x, y, z). 

  }



  // -----------------------------------------------------------------------------------------------------  



  Surface.prototype.generate = function()

  /*

    Creates a list of (x, y, z) points (in 3 x 1 vector format) representing the surface.

  */

  {

    var i = 0;



    for (var x = constants.xMin; x <= constants.xMax; x += constants.xDelta)

    {

      for (var y = constants.yMin; y <= constants.yMax; y += constants.yDelta)

      {

        this.points[i] = point(x, y, this.equation(x, y)); // Store a surface point (in vector format) into the list of surface points.              

        ++i;

      }

    }

  }



  // -----------------------------------------------------------------------------------------------------



  Surface.prototype.color = function()

  /*

    The color of a surface point is a function of its z-coordinate height.

  */

  {

    var z; // The z-coordinate for a given surface point (x, y, z).



    this.zMin = this.zMax = this.points[0][Z]; // A starting value. Note that zMin and zMax are custom properties that could possibly be useful if this code is extended later.

    for (var i = 0; i < this.points.length; i++)

    {            

      z = this.points[i][Z];

      if (z < this.zMin) { this.zMin = z; }

      if (z > this.zMax) { this.zMax = z; }

    }   



    var zDelta = Math.abs(this.zMax - this.zMin) / constants.colorMap.length; 



    for (var i = 0; i < this.points.length; i++)

    {

      this.points[i].color = constants.colorMap[ Math.floor( (this.points[i][Z]-this.zMin)/zDelta ) ];

    }



    /* Note that the prior FOR loop is functionally equivalent to the follow (much less elegant) loop:       

    for (var i = 0; i < this.points.length; i++)

    {

      if (this.points[i][Z] <= this.zMin + zDelta) {this.points[i].color = "#060";}

      else if (this.points[i][Z] <= this.zMin + 2*zDelta) {this.points[i].color = "#090";}

      else if (this.points[i][Z] <= this.zMin + 3*zDelta) {this.points[i].color = "#0C0";}

      else if (this.points[i][Z] <= this.zMin + 4*zDelta) {this.points[i].color = "#0F0";}

      else if (this.points[i][Z] <= this.zMin + 5*zDelta) {this.points[i].color = "#9F0";}

      else if (this.points[i][Z] <= this.zMin + 6*zDelta) {this.points[i].color = "#9C0";}

      else if (this.points[i][Z] <= this.zMin + 7*zDelta) {this.points[i].color = "#990";}

      else if (this.points[i][Z] <= this.zMin + 8*zDelta) {this.points[i].color = "#960";}

      else if (this.points[i][Z] <= this.zMin + 9*zDelta) {this.points[i].color = "#930";}

      else if (this.points[i][Z] <= this.zMin + 10*zDelta) {this.points[i].color = "#900";}          

      else {this.points[i].color = "#C00";}

    }

    */

  }



  // -----------------------------------------------------------------------------------------------------



  function appendCanvasElement()

  /*

    Creates and then appends the "myCanvas" canvas element to the DOM.

  */

  {

    var canvasElement = document.createElement('canvas');



    canvasElement.width = constants.canvasWidth;

    canvasElement.height = constants.canvasHeight;

    canvasElement.id = "myCanvas";



    canvasElement.getContext('2d').translate(constants.canvasWidth/2, constants.canvasHeight/2); // Translate the surface's origin to the center of the canvas.



    document.body.appendChild(canvasElement); // Make the canvas element a child of the body element.

  }



  //------------------------------------------------------------------------------------------------------



  Surface.prototype.sortByZIndex = function(A, B) 

  {

    return A[Z] - B[Z]; // Determines if point A is behind, in front of, or at the same level as point B (with respect to the z-axis).

  }



  // -----------------------------------------------------------------------------------------------------



  Surface.prototype.draw = function()

  {

    var myCanvas = document.getElementById("myCanvas"); // Required for Firefox.

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



    this.points = surface.points.sort(surface.sortByZIndex); // Sort the set of points based on relative z-axis position. If the points are visibly small, you can sort of get away with removing this step.

  var c=document.getElementById("myCanvas");
 var ctx=c.getContext("2d");
  ctx.font="20px Arial";
  ctx.fillText("X",250,0);

    for (var i = 0; i < this.points.length; i++)

    {

      ctx.fillStyle = this.points[i].color; 

      ctx.fillRect(this.points[i][X] * constants.surfaceScale, this.points[i][Y] * constants.surfaceScale, constants.pointWidth, constants.pointWidth);  

    }    

  }



  // -----------------------------------------------------------------------------------------------------



  Surface.prototype.multi = function(R)

  /*

    Assumes that R is a 3 x 3 matrix and that this.points (i.e., P) is a 3 x n matrix. This method performs P = R * P.

  */

  {

    var Px = 0, Py = 0, Pz = 0; // Variables to hold temporary results.

    var P = this.points; // P is a pointer to the set of surface points (i.e., the set of 3 x 1 vectors).

    var sum; // The sum for each row/column matrix product.



    for (var V = 0; V < P.length; V++) // For all 3 x 1 vectors in the point list.

    {

      Px = P[V][X], Py = P[V][Y], Pz = P[V][Z];

      for (var Rrow = 0; Rrow < 3; Rrow++) // For each row in the R matrix.

      {

        sum = (R[Rrow][X] * Px) + (R[Rrow][Y] * Py) + (R[Rrow][Z] * Pz);

        P[V][Rrow] = sum;

      }

    }     

  }



  // -----------------------------------------------------------------------------------------------------



  Surface.prototype.erase = function()

  {

    var myCanvas = document.getElementById("myCanvas"); // Required for Firefox.

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



    ctx.clearRect(-constants.canvasWidth/2, -constants.canvasHeight/2, myCanvas.width, myCanvas.height);

  }



  // -----------------------------------------------------------------------------------------------------



  Surface.prototype.xRotate = function(sign)

  /*

    Assumes "sign" is either 1 or -1, which is used to rotate the surface "clockwise" or "counterclockwise".

  */

  {

    var Rx = [ [0, 0, 0],

               [0, 0, 0],

               [0, 0, 0] ]; // Create an initialized 3 x 3 rotation matrix.



    Rx[0][0] = 1;

    Rx[0][1] = 0; // Redundant but helps with clarity.

    Rx[0][2] = 0; 

    Rx[1][0] = 0; 

    Rx[1][1] = Math.cos( sign*constants.dTheta );

    Rx[1][2] = -Math.sin( sign*constants.dTheta );

    Rx[2][0] = 0; 

    Rx[2][1] = Math.sin( sign*constants.dTheta );

    Rx[2][2] = Math.cos( sign*constants.dTheta );



    this.multi(Rx); // If P is the set of surface points, then this method performs the matrix multiplcation: Rx * P

    this.erase(); // Note that one could use two canvases to speed things up, which also eliminates the need to erase.

    this.draw();

  }



  // -----------------------------------------------------------------------------------------------------



  Surface.prototype.yRotate = function(sign)

  /*

    Assumes "sign" is either 1 or -1, which is used to rotate the surface "clockwise" or "counterclockwise".

  */      

  {

    var Ry = [ [0, 0, 0],

               [0, 0, 0],

               [0, 0, 0] ]; // Create an initialized 3 x 3 rotation matrix.



    Ry[0][0] = Math.cos( sign*constants.dTheta );

    Ry[0][1] = 0; // Redundant but helps with clarity.

    Ry[0][2] = Math.sin( sign*constants.dTheta );

    Ry[1][0] = 0; 

    Ry[1][1] = 1;

    Ry[1][2] = 0; 

    Ry[2][0] = -Math.sin( sign*constants.dTheta );

    Ry[2][1] = 0; 

    Ry[2][2] = Math.cos( sign*constants.dTheta );



    this.multi(Ry); // If P is the set of surface points, then this method performs the matrix multiplcation: Rx * P

    this.erase(); // Note that one could use two canvases to speed things up, which also eliminates the need to erase.

    this.draw();

  }



  // -----------------------------------------------------------------------------------------------------



  Surface.prototype.zRotate = function(sign)

  /*

    Assumes "sign" is either 1 or -1, which is used to rotate the surface "clockwise" or "counterclockwise".

  */      

  {

    var Rz = [ [0, 0, 0],

               [0, 0, 0],

               [0, 0, 0] ]; // Create an initialized 3 x 3 rotation matrix.



    Rz[0][0] = Math.cos( sign*constants.dTheta );

    Rz[0][1] = -Math.sin( sign*constants.dTheta );        

    Rz[0][2] = 0; // Redundant but helps with clarity.

    Rz[1][0] = Math.sin( sign*constants.dTheta );

    Rz[1][1] = Math.cos( sign*constants.dTheta );

    Rz[1][2] = 0;

    Rz[2][0] = 0

    Rz[2][1] = 0;

    Rz[2][2] = 1;



    this.multi(Rz); // If P is the set of surface points, then this method performs the matrix multiplcation: Rx * P

    this.erase(); // Note that one could use two canvases to speed things up, which also eliminates the need to erase.

    this.draw();

  }



  // -----------------------------------------------------------------------------------------------------



  function processKeyDown(evt)

  {                    

    if (evt.ctrlKey)

    {

      switch (evt.keyCode)

      {

        case constants.upArrow: 

          // No operation other than preventing the default behavior of the arrow key.

          evt.preventDefault(); // This prevents the default behavior of the arrow keys, which is to scroll the browser window when scroll bars are present. The user can still scroll the window with the mouse.              

          break;

        case constants.downArrow:

          // No operation other than preventing the default behavior of the arrow key.

          evt.preventDefault();

          break;

        case constants.leftArrow:

          // console.log("ctrl+leftArrow");

          surface.zRotate(-1); // The sign determines if the surface rotates "clockwise" or "counterclockwise". 

          evt.preventDefault(); 

          break;

        case constants.rightArrow:

          // console.log("ctrl+rightArrow");

          surface.zRotate(1);

          evt.preventDefault(); 

          break;

      }

      return; // When the control key is pressed, only the left and right arrows have meaning, no need to process any other key strokes (i.e., bail now).

    }



    // Assert: The control key is not pressed.



    switch (evt.keyCode)

    {

      case constants.upArrow:

        // console.log("upArrow");

        surface.xRotate(1);

        evt.preventDefault(); 

        break;

      case constants.downArrow:

        // console.log("downArrow");

        surface.xRotate(-1); 

        evt.preventDefault(); 

        break;

      case constants.leftArrow:

        // console.log("leftArrow");

        surface.yRotate(-1);  

        evt.preventDefault(); 

        break;

      case constants.rightArrow:

        // console.log("rightArrow");

        surface.yRotate(1);   

        evt.preventDefault(); 

        break;

    }

  }



  // -----------------------------------------------------------------------------------------------------
  Surface.prototype.plot = function(x, y, z)
  /*
    add the point (x, y, z)  (in 3 x 1 vector format) to the surface.
  */
  {
        this.points.push(point(x, y, z)); // Store a surface point
        var x=0;
        for (var x = constants.xMin; x <= constants.xMax; x += constants.xDelta)
        {
        this.points.push(point(x, 0, 0));

        }

        /*for (var x = constants.xMax+1; x <= constants.xMax+2; x += constants.xDelta)
        {
        this.points.push(point(11, 0, 0))
        }*/
        for (var x = constants.xMin; x <= constants.xMax; x += constants.yDelta)
        {
        this.points.push(point(0, x, 0));   
        }
        for (var x = constants.xMin; x <= constants.xMax; x += constants.yDelta)
        {
        this.points.push(point(0,0,x)); 
        }
  }


  function onloadInit()

  {

    appendCanvasElement(); // Create and append the canvas element to the DOM.

    surface.draw(); // Draw the surface on the canvas.

    document.addEventListener('keydown', processKeyDown, false); // Used to detect if the control key has been pressed.

  }



  // -----------------------------------------------------------------------------------------------------




  //surface.generate(); // Creates the set of points reprsenting the surface. Must be called before color().
   surface.plot(1,1,1);
  surface.color(); // Based on the min and max z-coordinate values, chooses colors for each point based on the point's z-ccordinate value (i.e., height).

  window.addEventListener('load', onloadInit, false); // Perform processing that must occur after the page has fully loaded.

  </script>

  </head>

  <body>


  <p>The z-axis extends out from the center of the screen.<br>

    To rotate about the x-axis, press the up/down arrow keys.
    To rotate about the y-axis, press the left/right arrow keys.
    To rotate about the z-axis, press the ctrl+left/ctrl+down arrow keys.
     Note that pressing an arrow key down continuously will not rotate the surface. The surface is rotated once per key press.</p>

  <!-- The canvas element is append to the DOM here. -->

    </body>

  </html>

推荐答案

文本是在矩形平面上绘制的.设左上角的坐标为 (xtl,ytl,ztl)到右上角是 (xtr,ytr,ztr) 和左下角是 (xbl,ybl,zbl) 然后必须对这些坐标应用任何变换,然后必须计算 2D 投影到画布上的坐标.这将生成一个平行四边形,文本可以绘制成该平行四边形,但也需要转换.

Text is drawn on a rectangular plane. Let the co-ordinates of top left be (xtl,ytl,ztl) to right corner be (xtr,ytr,ztr) and bottom left be (xbl,ybl,zbl) then any transformations have to be applied to these coordinates and then the coordoinates for the 2D projection onto the canvas have to be calculated. This will produce a parallelogram into which the text can be drawn but would also need to be transformed.

最简单的方法是计算左上角变换并在该点绘制标准文本,可能会根据 z 减小文本大小.

The simplest would be to calculate the top left corner transformation and draw standard text at that point, perhaps reducing text size depending on z.

这篇关于画布 3d 图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆