heptagono
heptagono

Reputation: 69

Graphics in html5 canvas: Bizarre and undesired optical effects

This program runs. But, as you can, see there is an artifact when the square turns over. The matrix values must be represented, and this representation should also bee seen depending on an angle. Is there any way to archieve good visualization. Why is this happening to my code?

    var canvas=document.getElementById('canvas');
    var ctx=canvas.getContext('2d');

    var x=100;
    var y=100;
    var width=200;
    var height=200;
    var radianAngle=0;


    Rotar();


    var array = new Array2D(200,200);


    function Array2D(NumOfRows,NumOfCols)
    {
    var k=new Array(NumOfRows);
    for (i = 0; i < k.length; ++i)
    k[i] = new Array(NumOfCols);
    return k; 
    }

    function Rotar(){
    //Borramos 
    ctx.clearRect(0,0,canvas.width,canvas.height); 
    //Salvamos el estado
    ctx.save();
    // Transladamos su centro de gravedad
    ctx.translate(x+width/2,y+height/2); 
    //Otra mⳍ
    ctx.rotate(radianAngle);    



    var array = new Array2D(200,200);  
    for(i=0; i<200; i++)
    {
      for(j=0;j<200; j++)
      {
      array[i][j]=i+j;
      var r,g,b;
      r = array[i][j];
      g=50;
      b=50;
      //La parte de dibujo
      ctx.fillStyle = "rgba("+r+","+g+","+b+",100)";
      ctx.fillRect( i, j, 1, 1 ); 



     }
     }

  ctx.restore();    
      }

    $("#test").click(function(){

  radianAngle+=Math.PI/60;


  // call rotateSquare
  Rotar();
});
 body {
            background: #dddddd;
         }

         #canvas {
            background: #eeeeee;
            border: thin solid #aaaaaa;
         }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvas" height="500" width="500"></canvas>
<button id="test">Rotate</button><br>

Rotar(); });

Upvotes: 1

Views: 135

Answers (1)

GameAlchemist
GameAlchemist

Reputation: 19294

This is a typical rounding issue : You rotate the context, then iterate on (x,y) in [0,199]. But while drawing the small one pixel wide rectangles, they won't fit perfectly one pixel, so the renderer has to 'diffuse' the color on several real device pixels, and that goes with a rounding error since r,g,b are only stored on 8 bits. Add to this the error on the coordinates of the tiny rectangles -that will be rasterized on up to 4 pixels-, and you have the grid you're seing.

When doing such transform, the rule is to rasterize : iterate the pixels of the destination and find where they originate in the source, not the other way around.
So a simple way do that : find the bounding box of the rotated rect, iterate in this BBox, and if a point is in the rect compute its color.
Or build an algorithm that rasterize the rect (most easy would be to use triangles, see here for an example of a triangle rasterization i played with : http://jsfiddle.net/gamealchemist/5cnkr2s5/ )

But... for what you are drawing here most simple and way faster is to build a linear gradient, use it as the fillStyle, and draw the whole rectangle in a single fillRect call. In other words : let the canvas (and, behind the scene, the GPU) do the math for you.

var grad = ctx.createLinearGradient(0,0,200,200);
grad.addColorStop(0,'#000');
grad.addColorStop(1,'#F00');
ctx.fillStyle = grad;

//
ctx.save();
ctx.clearRect( ... );
ctx.translate ( the upper left point) ;
ctx.rotate ( some angle );
ctx.fillRect(0, 0, 200, 200);
ctx.restore();

most simple example here (click 'run' several times, angle is randomized ):

http://jsfiddle.net/gamealchemist/j57msxr5/11/

(Rq : You can have an idea of the speed up of using the context2D by comparing the triangle rasterization i mentioned above with the same work done by the context2D : http://jsfiddle.net/gamealchemist/zch3gdrx/ )

Upvotes: 2

Related Questions