Plotagem de Gráfico com HTML 5



Maiores duvidas consulte a referência, os textos são originais. Os códigos são simples, é um bom começo para o estudo da plotagem de funções matemáticas utilizando JavaScript e Canvas do HTLM 5.

Note: If you omit the DOCTYPE element at the beginning of the page, then the graphics might not show up in IE browsers: without the DOCTYPE element, IE may switch to the backward compatibility mode and treat canvas as an unsupported tag.

Reference:
http://diveintohtml5.info/canvas.html#shapes
http://www.javascripter.net/faq/plotafunctiongraph.htm
http://www.html5canvastutorials.com/labs/html5-canvas-graphing-an-equation/

1) Canvas Coordinates
The canvas is a two-dimensional grid. The coordinate (0, 0) is at the upper-left corner of the canvas. Along the X-axis, values increase towards the right edge of the canvas. Along the Y-axis, values increase towards the bottom edge of the canvas.

a set of off-white vertical lines
a set of off-white horizontal lines
two black horizontal lines
two small black diagonal lines that form an arrow
two black vertical lines
two small black diagonal lines that form another arrow
the letter “x”
the letter “y”
the text “(0, 0)” near the upper-left corner
the text “(500, 375)” near the lower-right corner
a dot in the upper-left corner, and another in the lower-right corner

Source code:

<!DOCTYPE html>
<html>
<body>

<canvas id="c" width="500" height="375"></canvas>

<script>

var c_canvas = document.getElementById("c");
var context = c_canvas.getContext("2d");

// cria as retas verticais
for (var x = 0.5; x < 500; x += 10) {
  context.moveTo(x, 0);
  context.lineTo(x, 375);
}

// cria as retas horizontais
for (var y = 0.5; y < 375; y += 10) {
  context.moveTo(0, y);
  context.lineTo(500, y);
}


context.strokeStyle = "#eee";

// executa o contexto de uma vez 
context.stroke();

// caminho do contexto, eixo x
context.beginPath();
context.moveTo(0, 40);
context.lineTo(240, 40);
context.moveTo(260, 40);
context.lineTo(500, 40);
context.moveTo(495, 35);
//seta do eixo x
context.lineTo(500, 40);
context.lineTo(495, 45);

// caminho do contexto, eixo y
context.moveTo(60, 0);
context.lineTo(60, 153);
context.moveTo(60, 173);
context.lineTo(60, 375);
context.moveTo(65, 370);
//seta eixo y
context.lineTo(60, 375);
context.lineTo(55, 370);

context.strokeStyle = "#000";
context.stroke();

context.font = "bold 12px sans-serif";
context.fillText("x", 248, 43);
context.fillText("y", 58, 165);


context.font = "bold 12px sans-serif";
context.fillText("x", 248, 43);
context.fillText("y", 58, 165);

context.textBaseline = "top";
context.fillText("( 0 , 0 )", 8, 5);

context.textAlign = "right";
context.textBaseline = "bottom";
context.fillText("( 500 , 375 )", 492, 370);

context.fillRect(0, 0, 3, 3);
context.fillRect(497, 372, 3, 3); 

</script>

</body>
</html>

Click view

Obs:
1) // é usada para comentários em JavaScript.
2) A função for  executa o seu primeiro loop com o primeiro valor da variável utilizada para para contar os loops, (x no primeiro for e y no segundo for), após a execução do bloco {...} o contador recebe o acréscimo de 10, o novo ciclo é realizado com esse novo valor do contador.
3) O código acima mostra facilmente que para cada valor de i será executado o loop for interno que começa com j = 0 (zero) e termina com o tamanho da coluna.
4) O valor de lineWidth ele é por padrão 1px.

5) A unidade de medida na tela é o pixel, portanto a menor dimensão possível para desenho é 1 pixel. Utiliza-se números inteiros para especificar as coordenadas do pixel, a tela desenha utilizando uma mediatriz onde o valor a ser plotado será colocado nos dois lados dessa mediatriz.

Por exemplo, se você tentar desenhar a linha de (1, 0) para (1, 3), o navegador irá desenhar a linha cobrindo 0.5 pixels da tela em ambos os lados x=1. A tela não consegue exibir meio pixel, então irá expandir a linha para cobrir um total de dois pixels.

Mas se você tentar desenhar uma linha de (1.5, 0) para (1.5, 3), o navegador irá desenhar a linha cobrindo 0.5 pixels da tela em ambos os lados x=1.5, o que resulta na verdade em uma linha de 1 pixel de largura.

Note inglese:


stroke (stroʊk): s. 1. at one stroke/at a stroke: de um só golpe, All our problems were solved at one stroke. Todos os nossos problemas foram resolvidos de um só golpe. 2. derrame (cerebral), 3. braçada. 4. estilo (em natação). 5. on the stroke of six/seven etc. às seis/sete etc. em ponto. 6. a stroke of luck/genius uma grande sorte/uma ideia genial. v. afagar

path (pæθ):  s. 1. trilha. 2. passagem. to make a path for sb: abrir caminho para alguém. The crowd moved aside to make a path for him. A multidão se afastou para abrir-lhe caminho. 3. trajetória. 4. caminho. Ohe path to freedom/happiness etc. O caminho para a liberdade/felicidade etc.

join (dʒɔɪn):  v. 1. associar-se, entrar para, tornar-se sócio de (um clube). 2. filiar-se, filiar-se a (um partido, um sindicato). 3. ingressar em (uma empresa, uma organização). 4  to join the army/navy etc. alistar-se no Exército/na Marinha etc. 5. participar de (uma campanha, as conversações de paz, etc.). 6. juntar. To join sth to sth. Ligar algo a algo.

off-white: adj. branco "sujo".
arrow (ˈæroʊ): s. flexa, seta.
increase: s. (ˈɪŋkris): s. aumento; v. (ɪnˈkris) aumentar.
towards (tɔrdz): prep. em direção a.
edge (ɛdʒ): s. borda, margem, fio de faca.
along (əˈlɔŋ): prep. ao longo de; adv. come along, brought ... along, etc.
grid (grɪd): grade (de metal), rede de rua, grade (de mapa).
axis (ˈæksɪs):  s. eixo (linha), plural: axes (ˈæksiz).


2) Algorithm to draw circles and ellipses

This algorithm is based on the parametric form of the circle equation. For more see Parametric equation of a circle.
Recall that this looks like:

x  =  h + r cosθ
y  =  k + r sinθ

Where r is the radius of the circle, and h, k are the coordinates of the center.

Note the pictures:


General Formula fo Translation of Ellipse


3) Other forms of the equation
Using the Pythagorean Theorem to solve the triangle in the figure above we get the more common form of the equation of a circle.

For (h,k) = (0,0)
(I) sen θ = y/r   cos θ = x/r
y = r sen θ    x = r cos θ
 
(II) sen² θ + cos² θ = 1

(III) r² = x² + y²  = r² (Pythagorean)




General Formula fo Translation of Circle

x  =  h + r cosθ
y  =  k + r sinθ

What these equation do is generate the x,y coordinates of a point on the circle given an angle θ (theta). The algorithm starts with theta at zero, and then loops adding an increment to theta each time round the loop. It draws straight line segments between these successive points on the circle. The circle is thus drawn as a series of straight lines. If the increment is small enough, the result looks like a circle to the eye, even though in strict mathematical terms is is not.

The algorithm
Below is the algorithm in pseudocode showing the basic idea:
theta = 0;  // angle that will be increased each loop
h = 12      // x coordinate of circle center
k = 10      // y coordinate of circle center
step = 15;  // amount to add to theta each time (degrees)

repeat until theta >= 360; // 2*Math.PI = 360, ou seja, 2¶Rd = 360º
    { x = h + r*cos(theta)
      y = k + r*sin(theta)
      draw a line to x,y
      add step to theta
    }

The decision about how big to make the step size is a tradeoff. If it is very small, many lines will be drawn for a smooth circle, but there will be more computer time used to do it. If it is too large the circle will not be smooth and be visually ugly.

Example
Below is the algorithm written in Javascript using the HTML5 canvas element to draw into.

    var ctx = canvas.getContext("2d");
    var step = 2*Math.PI/20;   // Math.PI = 3.141592653589793
    var h = 150;
    var k = 150;
    var r = 50;

    ctx.beginPath();  //tell canvas to start a set of lines

    for(var theta=0;  theta < 2*Math.PI;  theta+=step)
     { var x = h + r*Math.cos(theta);
       var y = k - r*Math.sin(theta);    //note 2.
       ctx.lineTo(x,y);
     }

    ctx.closePath();     //close the end to the start point
    ctx.stroke();        //actually draw the accumulated lines


Like most graphics systems, the canvas element differs from the usual mathematical coordinate plane:

The origin is in the top left corner. The code above compensates by assuming that h, and k are actually relative to the top left.
The y axis is inverted. Positive y is down the screen, not up. To correct for this, the k variable (the y coordinate of the center) must be positive to place the center some way down the screen. Also the y calculation has to subtract the sin(x) term instead of add.
Marked in the code as Note 1.
Note 2. The step size is set to an exact division of 2π to avoid gaps or over-runs in the circle. This code divides the circle into exactly 20 segments. Note too that as in most computer languages, the trig functions operate in radians, not degrees.
360° = 2π radians.
Ellipses
The circles can be made into ellipses by simply "squashing" them in one direction or the other. For an ellipse that is wider than it is tall, be divide the y coordinate by a number (here 2) to reduce its height. The two inner calculations would then look like:

       var x = h + r*Math.cos(theta) ;
       var y = k - 0.5 * r*Math.sin(theta) ; 
Changing the 0.5 will alter how much the vertical size is squashed. Multiply the y term this way will make an ellipse that is tall and narrow.

Source code:

<!DOCTYPE html>
<html>
<body>

<canvas id="c" width="500" height="375"></canvas>

<script>

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

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

    var step = 2*Math.PI/20;  // see note 1
    var h = 150;
    var k = 150;
    var r = 50;

    ctx.beginPath();  //tell canvas to start a set of lines

    for(var theta=0;  theta < 2*Math.PI;  theta+=step)
     { var x = h + r*Math.cos(theta);
       var y = k - r*Math.sin(theta);    //note 2.
       ctx.lineTo(x,y);
     }

    ctx.closePath();     //close the end to the start point
    ctx.stroke();

</script>

</body>
</html>

Click view

3) How do I plot a graph of a mathematical function using JavaScript?


To plot a graph of a function on your webpage, use the canvas element, as shown in the example below. The canvas element is supported in all major browsers: Firefox, Opera, Safari, Google Chrome, and Microsoft Internet Explorer 9 or newer.

Example: Here is a graph of the functions sin x (green) and cos 3x (blue).


The graph has been created using the following JavaScript code:

function fun1(x) {return Math.sin(x);  }
function fun2(x) {return Math.cos(3*x);}

function draw() {
 var canvas = document.getElementById("canvas");
 if (null==canvas || !canvas.getContext) return;

 var axes={}, ctx=canvas.getContext("2d");
 axes.x0 = .5 + .5*canvas.width;  // x0 pixels from left to x=0
 axes.y0 = .5 + .5*canvas.height; // y0 pixels from top to y=0
 axes.scale = 40;                 // 40 pixels from x=0 to x=1
 axes.doNegativeX = true;

 showAxes(ctx,axes);
 funGraph(ctx,axes,fun1,"rgb(11,153,11)",1);
 funGraph(ctx,axes,fun2,"rgb(66,44,255)",2);
}

function funGraph (ctx,axes,func,color,thick) {
 var xx, yy, dx=4, x0=axes.x0, y0=axes.y0, scale=axes.scale;
 var iMax = Math.round((ctx.canvas.width-x0)/dx);
 var iMin = axes.doNegativeX ? Math.round(-x0/dx) : 0;
 ctx.beginPath();
 ctx.lineWidth = thick;
 ctx.strokeStyle = color;

 for (var i=iMin;i<=iMax;i++) {
  xx = dx*i; yy = scale*func(xx/scale);
  if (i==iMin) ctx.moveTo(x0+xx,y0-yy);
  else         ctx.lineTo(x0+xx,y0-yy);
 }
 ctx.stroke();
}

function showAxes(ctx,axes) {
 var x0=axes.x0, w=ctx.canvas.width;
 var y0=axes.y0, h=ctx.canvas.height;
 var xmin = axes.doNegativeX ? 0 : x0;
 ctx.beginPath();
 ctx.strokeStyle = "rgb(128,128,128)";
 ctx.moveTo(xmin,y0); ctx.lineTo(w,y0);  // X axis
 ctx.moveTo(x0,0);    ctx.lineTo(x0,h);  // Y axis
 ctx.stroke();
}
It is safe to call the draw() function after the entire body of the webpage is loaded. The source code of an HTML page containing the canvas element may look like this:

Source code:

<!DOCTYPE html>
<html>
<head><title>Canvas code example</title>
<script type="text/javascript">
function fun1(x) {return Math.sin(x);  }
function fun2(x) {return Math.cos(3*x);}

function draw() {
 var canvas = document.getElementById("canvas");
 if (null==canvas || !canvas.getContext) return;

 var axes={}, ctx=canvas.getContext("2d");
 axes.x0 = .5 + .5*canvas.width;  // x0 pixels from left to x=0
 axes.y0 = .5 + .5*canvas.height; // y0 pixels from top to y=0
 axes.scale = 40;                 // 40 pixels from x=0 to x=1
 axes.doNegativeX = true;

 showAxes(ctx,axes);
 funGraph(ctx,axes,fun1,"rgb(11,153,11)",1);
 funGraph(ctx,axes,fun2,"rgb(66,44,255)",2);
}

function funGraph (ctx,axes,func,color,thick) {
 var xx, yy, dx=4, x0=axes.x0, y0=axes.y0, scale=axes.scale;
 var iMax = Math.round((ctx.canvas.width-x0)/dx);
 var iMin = axes.doNegativeX ? Math.round(-x0/dx) : 0;
 ctx.beginPath();
 ctx.lineWidth = thick;
 ctx.strokeStyle = color;

 for (var i=iMin;i<=iMax;i++) {
  xx = dx*i; yy = scale*func(xx/scale);
  if (i==iMin) ctx.moveTo(x0+xx,y0-yy);
  else         ctx.lineTo(x0+xx,y0-yy);
 }
 ctx.stroke();
}

function showAxes(ctx,axes) {
 var x0=axes.x0, w=ctx.canvas.width;
 var y0=axes.y0, h=ctx.canvas.height;
 var xmin = axes.doNegativeX ? 0 : x0;
 ctx.beginPath();
 ctx.strokeStyle = "rgb(128,128,128)";
 ctx.moveTo(xmin,y0); ctx.lineTo(w,y0);  // X axis
 ctx.moveTo(x0,0);    ctx.lineTo(x0,h);  // Y axis
 ctx.stroke();
}
// JavaScript source code goes here
</script>
</head>
<body onload="draw()">
<canvas id="canvas" width="502" height="108"></canvas>
</body>
</html>

Click view

Source code:
Reference: http://www.html5canvastutorials.com/labs/html5-canvas-graphing-an-equation/

<!DOCTYPE HTML>
<html>
  <head>
    <style>
      body {
        margin: 0px;
        padding: 0px;
      }

    </style>
  </head>
  <body>
    <canvas id="myCanvas" width="578" height="300"></canvas>
    <script>
      function Graph(config) {
        // user defined properties
        this.canvas = document.getElementById(config.canvasId);
        this.minX = config.minX;
        this.minY = config.minY;
        this.maxX = config.maxX;
        this.maxY = config.maxY;
        this.unitsPerTick = config.unitsPerTick;

        // constants
        this.axisColor = '#aaa';
        this.font = '8pt Calibri';
        this.tickSize = 20;

        // relationships
        this.context = this.canvas.getContext('2d');
        this.rangeX = this.maxX - this.minX;
        this.rangeY = this.maxY - this.minY;
        this.unitX = this.canvas.width / this.rangeX;
        this.unitY = this.canvas.height / this.rangeY;
        this.centerY = Math.round(Math.abs(this.minY / this.rangeY) * this.canvas.height);
        this.centerX = Math.round(Math.abs(this.minX / this.rangeX) * this.canvas.width);
        this.iteration = (this.maxX - this.minX) / 1000;
        this.scaleX = this.canvas.width / this.rangeX;
        this.scaleY = this.canvas.height / this.rangeY;

        // draw x and y axis
        this.drawXAxis();
        this.drawYAxis();
      }

      Graph.prototype.drawXAxis = function() {
        var context = this.context;
        context.save();
        context.beginPath();
        context.moveTo(0, this.centerY);
        context.lineTo(this.canvas.width, this.centerY);
        context.strokeStyle = this.axisColor;
        context.lineWidth = 2;
        context.stroke();

        // draw tick marks
        var xPosIncrement = this.unitsPerTick * this.unitX;
        var xPos, unit;
        context.font = this.font;
        context.textAlign = 'center';
        context.textBaseline = 'top';

        // draw left tick marks
        xPos = this.centerX - xPosIncrement;
        unit = -1 * this.unitsPerTick;
        while(xPos > 0) {
          context.moveTo(xPos, this.centerY - this.tickSize / 2);
          context.lineTo(xPos, this.centerY + this.tickSize / 2);
          context.stroke();
          context.fillText(unit, xPos, this.centerY + this.tickSize / 2 + 3);
          unit -= this.unitsPerTick;
          xPos = Math.round(xPos - xPosIncrement);
        }

        // draw right tick marks
        xPos = this.centerX + xPosIncrement;
        unit = this.unitsPerTick;
        while(xPos < this.canvas.width) {
          context.moveTo(xPos, this.centerY - this.tickSize / 2);
          context.lineTo(xPos, this.centerY + this.tickSize / 2);
          context.stroke();
          context.fillText(unit, xPos, this.centerY + this.tickSize / 2 + 3);
          unit += this.unitsPerTick;
          xPos = Math.round(xPos + xPosIncrement);
        }
        context.restore();
      };

      Graph.prototype.drawYAxis = function() {
        var context = this.context;
        context.save();
        context.beginPath();
        context.moveTo(this.centerX, 0);
        context.lineTo(this.centerX, this.canvas.height);
        context.strokeStyle = this.axisColor;
        context.lineWidth = 2;
        context.stroke();

        // draw tick marks
        var yPosIncrement = this.unitsPerTick * this.unitY;
        var yPos, unit;
        context.font = this.font;
        context.textAlign = 'right';
        context.textBaseline = 'middle';

        // draw top tick marks
        yPos = this.centerY - yPosIncrement;
        unit = this.unitsPerTick;
        while(yPos > 0) {
          context.moveTo(this.centerX - this.tickSize / 2, yPos);
          context.lineTo(this.centerX + this.tickSize / 2, yPos);
          context.stroke();
          context.fillText(unit, this.centerX - this.tickSize / 2 - 3, yPos);
          unit += this.unitsPerTick;
          yPos = Math.round(yPos - yPosIncrement);
        }

        // draw bottom tick marks
        yPos = this.centerY + yPosIncrement;
        unit = -1 * this.unitsPerTick;
        while(yPos < this.canvas.height) {
          context.moveTo(this.centerX - this.tickSize / 2, yPos);
          context.lineTo(this.centerX + this.tickSize / 2, yPos);
          context.stroke();
          context.fillText(unit, this.centerX - this.tickSize / 2 - 3, yPos);
          unit -= this.unitsPerTick;
          yPos = Math.round(yPos + yPosIncrement);
        }
        context.restore();
      };

      Graph.prototype.drawEquation = function(equation, color, thickness) {
        var context = this.context;
        context.save();
        context.save();
        this.transformContext();

        context.beginPath();
        context.moveTo(this.minX, equation(this.minX));

        for(var x = this.minX + this.iteration; x <= this.maxX; x += this.iteration) {
          context.lineTo(x, equation(x));
        }

        context.restore();
        context.lineJoin = 'round';
        context.lineWidth = thickness;
        context.strokeStyle = color;
        context.stroke();
        context.restore();
      };

      Graph.prototype.transformContext = function() {
        var context = this.context;

        // move context to center of canvas
        this.context.translate(this.centerX, this.centerY);

        /*
         * stretch grid to fit the canvas window, and
         * invert the y scale so that that increments
         * as you move upwards
         */
        context.scale(this.scaleX, -this.scaleY);
      };
      var myGraph = new Graph({
        canvasId: 'myCanvas',
        minX: -10,
        minY: -10,
        maxX: 10,
        maxY: 10,
        unitsPerTick: 1
      });

      myGraph.drawEquation(function(x) {
        return 5 * Math.sin(x);
      }, 'green', 3);

      myGraph.drawEquation(function(x) {
        return x * x;
      }, 'blue', 3);

      myGraph.drawEquation(function(x) {
        return 1 * x;
      }, 'red', 3);

    </script>
  </body>
</html>

Click view