Javascript episódio 1

Como quase tudo, eu tenho tentado aprender javascript na tentativa e erro, mas acho que já esta na hora de começar a ler e entender melhor o que estou fazendo, melhor que tentativa e erro para progredir mais um pouco, e acabei que achei um livro que achei tanto simples como legal. Essa foi a lição do capítulo 1.

Bem aqui a gente vai apenas fazer um canvas com javascript do zero, para começar, vamos fazer somente um arquivo html, e ele vai ter um elemento no corpo que vai ser um canvas.

1
2
3
4
5
6
7
8
9
10
11
<!doctype html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
    <title>Isto é um canvas html</title>
  </head>
  <body>
    <h1>Isto é um canvas html</h1>
    <canvas id="asteroids" width="400" height="400"></canvas>
  </body>
</html>

Isso gera o seguinte no navegador.

Então temos um canvas de 400 por 400, mas na verdade a gente não vê nada, porque tudo, tanto a página quanto o canvas tem tudo a mesma cor, mas ele está ali, abaixo do texto. Agora vamos desenhar algo nele, com um script de javascript.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!doctype html>                                                                                                                                                                                             
<html>                                                                                                                                                                                                      
  <head>                                                                                                                                                                                                    
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">                                                                                                                                
    <title>Isto é um canvas html</title>                                                                                                                                                                    
  </head>                                                                                                                                                                                                   
  <body>                                                                                                                                                                                                    
 
    <h1>Isto é um canvas html</h1>                                                                                                                                                                          
    <canvas id="asteroids" width="400" height="400"></canvas>                                                                                                                                               
 
    <script>                                                                                                                                                                                                
      var canvas = document.getElementById("asteroids");                                                                                                                                                    
      var context = canvas.getContext("2d");                                                                                                                                                                
      context.strokeStyle = 'dimgrey';                                                                                                                                                                      
      context.lineWidth = 5;                                                                                                                                                                                
      context.rect(75, 75, 250, 250);                                                                                                                                                                       
      context.stroke();                                                                                                                                                                                     
      // Comentário                                                                                                                                                                                         
    </script>                                                                                                                                                                                               
 
  </body>                                                                                                                                                                                                   
</html>

E vemos o seguinte.

Bem, primeiro a gente tem que se ligar que javascript é uma linguagem scriptada, então assim como o R, ele vai rodando linha por linha, e so para se a gente fizer algo errado, até la ele roda, e ele roda na ordem, por isso o script esta depois do elemento canvas no html, porque se tiver antes, ele vai tentar rodar num elemento de html que ainda não existe.
Outra coisa, é que ele é orientado a objetos, então a primeira linha

1
      var canvas = document.getElementById("asteroids");

Apenas instancia um objeto, que esta ligado ao elemento asteroids, que é o id que demos no nosso canvas ali no html

1
<canvas id="asteroids" width="400" height="400"></canvas>

Depois disso criamos um contexto. Apesar do elemento canvas ser usado para desenhar, plotar, etc, através de scripts, ele não tem métodos de desenho, então quando a gente usa o método getContext(), ele retorna o objeto que tem os métodos para desenho, no caso o “2d” é para desenhos em 2d, como retas, formas, texto entre outras coisas.

Agora podemos mexer no contexto, que é o que realmente desenha, o strokeStyle seta a cor, lineWidth qual a espessura da linha a ser desenhada em pixels, rect faz um retangulo, que no nosso caso é um quadrado, os dois primeiros valores são o ponto onde ele vai começar, onde vai ser ancorado essa forma, e depois temos o tamanho dele, 250 de largura e altura.

Até essa linha, nada foi desenhado, so depois que rodamos o método stroke, que tudo realmente é desenhado. Mas não da para ver onde é o canvas, porque precisamos mexer no estilo da página, para colocar outra cor no canvas, no nosso elemento, para diferencias ele, então precisamos de um pouco de CSS, que podemos colocar direto aqui.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!doctype html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
    <title>Isto é um canvas html</title>
 
    <style media="screen">
      body {
      text-align: center;
      font-family: sans-serif;
      }
      canvas {
      background-color: black;
      }
    </style>
 
  </head>
  <body>
 
    <h1>Isto é um canvas html</h1>
    <canvas id="asteroids" width="400" height="400"></canvas>
 
    <script>
      var canvas = document.getElementById("asteroids");
      var context = canvas.getContext("2d");
      context.strokeStyle = 'dimgrey';
      context.lineWidth = 5;
      context.rect(75, 75, 250, 250);
      context.stroke();
      // Comentário
    </script>
 
  </body>
</html>

Essa modificação no estilo, também direto dentro do html, colocamos o elemento style la no cabeçalho, no head, e mudamos o plano de fundo dos elementos, e centralizamos na página também.

Agora da pra ver melhor o canvas, vamos mexer no nosso desenho melhor.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<!doctype html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
    <title>Isto é um canvas html</title>
 
    <style media="screen">
      body {
      text-align: center;
      font-family: sans-serif;
      }
      canvas {
      background-color: black;
      }
    </style>
 
  </head>
  <body>
 
    <h1>Isto é um canvas html</h1>
    <canvas id="asteroids" width="400" height="400"></canvas>
 
    <script>
      var canvas = document.getElementById("asteroids");
      var context = canvas.getContext("2d");
      context.strokeStyle = 'lightgrey';
      context.fillStyle = 'dimgrey';
      context.lineWidth = 5;
      context.rect(75, 50,canvas.width - 150, canvas.height - 100);
      context.stroke();
      context.fill();
      // Comentário
    </script>
 
  </body>
</html>

Isso vira o seguinte.

O que a gente fez aqui, primeiro veja que temos agora uma cor para a borda do elemento e uma cor de preenchimento, que é o fillStyle, outra coisa, veja que mudamos agora as medidas e o ponto de referencia do retângulo que fazemos com rect, estamos fazendo ele baseado nas medidas do canvas, veja que podemos acessar esses valores a partir do objeto canvas que criamos, ele tem esses atributos, e agora usamos o stroke para desenhar o e fill para preencher, antes de fazer isso, nada acontece, podemos comentar essas linhas para ir testando o comportamento dos métodos.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<!doctype html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
    <title>Isto é um canvas html</title>
 
    <style media="screen">
      body {
      text-align: center;
      font-family: sans-serif;
      }
      canvas {
      background-color: black;
      }
    </style>
 
  </head>
  <body>
 
    <h1>Isto é um canvas html</h1>
    <canvas id="asteroids" width="400" height="400"></canvas>
 
    <script>
      var canvas = document.getElementById("asteroids");
 
      //Retângulo
      var context = canvas.getContext("2d");
      context.strokeStyle = 'lightgrey';
      context.fillStyle = 'dimgrey';
      context.lineWidth = 5;
      context.rect(75, 50,canvas.width - 150, canvas.height - 100);
      context.stroke();
      context.fill();
 
      // Texto
      context.font = "34px Arial";
      context.strokeStyle = '#FF2222';
      context.fillStyle = '#FFAAAA';
      context.lineWidth = 0.75;
      context.textAlign="center";
      let msg = "Desenhando 2d"
      context.fillText(msg, canvas.width / 2, 100);
      context.strokeText(msg, canvas.width / 2, 100);
    </script>
 
  </body>
</html>

Agora adicionando o elemento de texto.

Bem, os comentários são como no c++, com duas barras, podemos usar eles para dar uma organizada no texto, bem agora vamos mexer no contexto, veja que a cor e o preenchimento são uma propriedade, no caso nos mudamos ela de novo depois que fizemos o retângulo, pegamos cores novas, mudamos a grossura da linha de desenho com lineWidth e vamos desenhar texto, alinhando ele centralizado a partir do ponto onde desejamos escrever, declaramos um string com let, a diferença de let e var é sutil, os dois declaram variáveis, mas tem diferentes escopo e eu não sei explicar isso direito ainda, mas tudo bem. Ai assim como o retângulo, usamos o fillText para preencher as letras, e o strokeText para o contorno, veja que mandamos para ele o texto e o ponto onde ele deve ser ancorado, sendo o meio do canvas, na altura 100 pixel, lembrando que é um desenho numa tela, então não contamos os eixos como num gráfico, mas ao contrario, o canto superior esquerdo é onde começa a tela.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<!doctype html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
    <title>Isto é um canvas html</title>
 
    <style media="screen">
      body {
      text-align: center;
      font-family: sans-serif;
      }
      canvas {
      background-color: black;
      }
    </style>
 
  </head>
  <body>
 
    <h1>Isto é um canvas html</h1>
    <canvas id="asteroids" width="400" height="400"></canvas>
 
    <script>
      var canvas = document.getElementById("asteroids");
 
      //Retângulo
      var context = canvas.getContext("2d");
      context.strokeStyle = 'lightgrey';
      context.fillStyle = 'dimgrey';
      context.lineWidth = 5;
      context.rect(75, 50,canvas.width - 150, canvas.height - 100);
      context.stroke();
      context.fill();
 
      // Texto
      context.font = "34px Arial";
      context.strokeStyle = '#FF2222';
      context.fillStyle = '#FFAAAA';
      context.lineWidth = 0.75;
      context.textAlign="center";
      let msg = "Desenhando 2d"
      context.fillText(msg, canvas.width / 2, 100);
      context.strokeText(msg, canvas.width / 2, 100);
 
      // Bonequinho
      context.strokeStyle = '#FFFFFF';
      context.lineWidth = 2;
      context.beginPath();
      context.arc(200, 140, 20, 0, Math.PI * 2);
      context.moveTo(200, 160);
      context.lineTo(200, 220);
      context.moveTo(180, 300);
      context.lineTo(185, 260);
      context.lineTo(200, 220);
      context.lineTo(215, 260);
      context.lineTo(220, 300);
      context.moveTo(240, 130);
      context.lineTo(225, 170);
      context.lineTo(200, 170);
      context.lineTo(175, 180);
      context.lineTo(170, 220);
      context.stroke();
 
      // Mais texto
      let msg2 = "bem simples!!!";
      context.font = "24px Arial";
      context.strokeStyle = '#FF2222';
      context.lineWidth = 0.75;
      context.fillText(msg2, canvas.width / 2, 330);
      context.strokeText(msg2, canvas.width / 2, 330);
    </script>
 
  </body>
</html>

Agora basicamente é isso, com mais algumas funções, fazemos um bonequinho usando path, funciona assim, a gente começa um path com beginPath, veja que ele usa os parâmetros de cor, tamanho ja determinados, ai usamos outra função que é fazer um arco, para fazer a cabeça, um arco é um circulo, então temos que ter um ponto onde ele começa, onde ele ancora, depois o tamanho do raio e onde ele começa e onde termina, no caso 2 \pi é uma volta completa, dai temos um circulo, o moveTo move para onde queremos começar a desenhar, e lineTo para da onde está o último moveTo até o ponto que indicamos, e antes de terminar, temos que desenhar o que queremos, bem vamos fazer mais uma mensagem, e sempre lembrando que precisamos tomar cuidado pois mudamos as cores, as propriedades do texto.

Bem é isso ai, estava olhando javascript hoje e resolvi posta o que fiz, o script vai estar la no repositório de javascript, e se eu escrevi alguma bobeira, algo errado, deixe um comentário corrigindo ou mande um e-mail.

Referência:
Graeme Stuart 2017 – Introducing JavaScript Game Development Build a 2D Game from the Ground Up. Apress 221pp

Método de Monte Carlo e o experimento da Agulha de Buffon

Desde que eu comecei a ler sobre estatística bayesiana (nossa já faz alguns anos), eu comecei a sempre olhar como funcionava os algorítimos dela, que sempre caia no MCMC. Um MC desses é do Monte Carlo, que é um método de amostragem bem legal, já falamos dele aqui quando estimamos o valor de pi, mas agora vamos ver outro experimento com ele bem legal, o experimento das Agulhas de Buffon.

Bem biólogos sempre estão enfiados no meio da análise de dados, desde o Fisher fazendo anova a muitos outros nomes, e não é que no Monte Carlo a gente vê isso também. O Georges-Louis Leclerc, conde de Buffon era um botânico que propôs e estudou esse experimento.

O experimento é o seguinte, ele jogava várias agulhas no chão de madeira.

Então, depois contava quantas dessas agulhas encostavam nas divisões das madeiras.
Como ninguém tem muitas agulhas e as vezes não tem piso de madeira, podemos simular o experimento no R. Fazemos nosso piso.

E agora é so jogar Agulhas nele.

Jogamos uma agulhinha ali, e ela caiu no meio do piso, ou seja, ela não acertou a junção do piso. Mas se jogarmos várias.

Algumas vão acertar, e outras não. E agora é só contar. As agulhas ficaram legais né, são uma reta com um pontinho na ponta hehehe. Mas como eu fiz as agulhas? Foi com o seguinte código.

1
2
b=1
a=0.3

Bem primeiro definimos duas coisas, o tamanho das tábuas e o tamanho das agulhas, as tábuas aqui são de 1 unidade de tamanho e a agulha de 0.3, chamamos de unidade porque se for metro ou centímetro não tem real interesse aqui na simulação. Depois jogamos a agulha.

1
2
3
4
5
6
7
8
9
10
11
    xcenter <- runif(1,0,4*b)
    ycenter <- runif(1,0,4*b)
 
    phi <- runif(1,0,2*pi)
    x0 <- xcenter-(a/2)*cos(phi)
    y0 <- ycenter-(a/2)*sin(phi)
    x1 <- xcenter+(a/2)*cos(phi)
    y1 <- ycenter+(a/2)*sin(phi)
 
    points(x=c(x0,x1),y=c(y0,y1),type="l")
    points(x=x0,y=y0,type="p",pch=19,cex=0.6)

Eu sorteio onde é o meio da agulha, depois calculo onde ela começa no x0 e y0 e onde ela termina, o x1 e y1, que é bem fácil usando seno e cosseno, dai no gráfico eu ploto uma linha e uma bolinha na ponto, para dar um charme. Agora para testar se a agulha encosta borda da do piso, podemos começar a pensar de forma mais simplificada o problema. Veja que temos três variáveis que realmente interessam.

Uma variável é onde estão as ranhuras da madeira, que como a tábuas tem tamanho b, será de b em b na nossa área de amostragem, e onde as agulhas caem, que pode ser representado pelo ponto central onde ela caiu e o ângulo \phi dela em relação ao chão, e o tamanho da agulha que é a.

As agulhas não interagem umas com as outras, nem rolam para dentro das junções do piso, além que de as tábuas são simétricas, então x_{center} \leftrightarrow b-x_{center} e \phi \leftrightarrow -\phi.

Então de forma geral, podemos considerar um plano reduzido de amostragem, simplificado na verdade que é:

 0  \textless \phi   \textless \frac{\pi}{2}

e,

 0  \textless x_{center}   \textless \frac{b}{2}

Dessa forma, podemos testar se a agulha encosta no canto da madeira se x_{tip} \textless 0 onde x_{tip} = x_{center} - \frac{a}{2} cos (\phi).

Confesso que fiquei meio perdido quanto a isso no começo, mas veja que o centro da agulha sempre vai estar entre  0  \textless x_{center}   \textless \frac{b}{2} e temos a fração de b porque passou de \frac{b}{2} temos o outro lado da madeira, lembre-se da simetria x_{center} \leftrightarrow b-x_{center}. O cosseno vem da inclinação da agulha, estamos tipo projetando ela no eixo x, porque o eixo y não interessa aqui. Assim como temos sempre algo entre 0 e \frac{b}{2}, a “sombra” da agulha no eixo x vai ser negativo quando tivermos passado o canto do piso.

No caso das agulhas que estamos fazendo no meu código, podemos sempre avaliar se o tamanho dela ta certinho com:

1
sqrt((x1-x0)^2+(y1-y0)^2)

E para fazermos o teste dessa forma, usamos o modulo em b e em pi para ficarmos nos intervalos acima, e usar essa simetria

1
xcenter%%(b/2)-(a/2)*cos(phi%%(pi/2))

Agora vamos fazer um experimento dessa forma mais simples, duas mil vezes, para ver algo legal, e contar quantas vezes acertamos os cantos do piso

1
2
3
4
5
6
7
8
9
10
11
12
n <- 2000
b <- 1
a <- 0.7
nhits <- 0
for(i in 1:n){
    xcenter <- runif(1,0,b/2)
    phi <- runif(1,0,pi/2)
    xtip <- xcenter-(a/2)*cos(phi)
    if(xtip<0){
        nhits <- nhits+1
        }
}

Que vai resultar em:

> nhits [1] 877

Nesse caso acertamos 877 vezes, que da

> nhits/n [1] 0.4385

Mais ou menos 44 porcento, que é mais ou menos

> ((a/b)*(2/pi)) [1] 0.445633

Hã? O que está acontecendo? Bem, não é de se admirar que o valor encontrado está relacionado a pi, mas veja que a é o tamanho da agulha, e intuitivamente sabemos que quanto mair agulha, mais chance ela tem de encostar no canto do piso certo? Mas quanto maior o piso, menor a chance da agulha encostar no canto, pois tem mais piso para ela cair sem encostar em nada. Veja que podemos olhar, a partir da nossa fórmula para testar se caiu ou no canto, o valor de N_{hit} o que ta acontecendo quando variamos essas quantidades.

Primeiro da agulha em relação a N_{hit} para vários tamanhos de pisos.

E depois o contrário

Veja que dependemos dessas duas variáveis, na verdade, podemos plotar todos os casos juntos

Então ((a/b)*(2/pi)) nada mais é que essa área azul, a integral disso, infelizmente eu não sei cálculo muito bem, ainda mais com duas variáveis, então nem vou tentar, mas a intuição é essa do resultado do experimento, se essa figura é o total das possibilidades, a área azul é a porcentagem desse total que são quando a agulha encosta no canto. Da até um pouco de vergonha que em 1700 o cara já dava conta de calcular isso, sem computador, e hoje eu apanho para entender. Pior ainda o fato do cosseno, que devia ser bem complicado sem computador.

Bem é isso ai, MCMC, como quase tudo em estatística, tem sempre uns biólogos perdidos no meio, acho que principalmente porque precisamos de problemas para resolver, e biólogo sempre observa mais problema na natureza para resolver o script vai estar la no repositório recologia, e se eu escrevi alguma bobeira, algo errado, deixe um comentário corrigindo ou mande um e-mail e bom natal a todos 🙂

Referência:
Werner Krauth 2006 – Statistical Mechanics: Algorithms and Computations Oxford University Press 342pp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
##Ilustrando o problema de buffon
b=1
a=0.3
 
 
plot(0,0,type="n",xlim=c(0,4*b),ylim=c(0,4*b),frame=F,xlab="",ylab="",main="Piso",xaxt="n",yaxt="n")
abline(v=seq(0,3*b,b))
polygon(x=c(0,1,1,0),y=c(-1,-1,5,5),col="brown3")
polygon(x=c(2,3,3,2),y=c(-1,-1,5,5),col="brown3")
 
 
for(i in 1:100){
    xcenter <- runif(1,0,4*b)
    ycenter <- runif(1,0,4*b)
 
    phi <- runif(1,0,2*pi)
    x0 <- xcenter-(a/2)*cos(phi)
    y0 <- ycenter-(a/2)*sin(phi)
    x1 <- xcenter+(a/2)*cos(phi)
    y1 <- ycenter+(a/2)*sin(phi)
 
    points(x=c(x0,x1),y=c(y0,y1),type="l")
    points(x=x0,y=y0,type="p",pch=19,cex=0.6)
}
 
##Tamanho das agulhas
sqrt((x1-x0)^2+(y1-y0)^2)
 
##Testando se ela encostou no canto
xcenter%%(b/2)-(a/2)*cos(phi%%(pi/2))
 
 
##Experimento simplificado
n <- 2000
b <- 1
a <- 0.7
nhits <- 0
for(i in 1:n){
    xcenter <- runif(1,0,b/2)
    phi <- runif(1,0,pi/2)
    xtip <- xcenter-(a/2)*cos(phi)
    if(xtip<0){
        nhits <- nhits+1
        }
}
 
##Porcentagem de hits
nhits/n
 
##Estimativa de quanto deve ser a porcentagem de hits
((a/b)*(2/pi))
 
 
##Avaliando a relação entre tamanho do piso, tamanho da agulha e hits
b_vetor<- seq(0.1,b,length.out = 6)
a_vetor<- seq(0,1,0.001)
 
par(mfrow=c(3,2))
for(i in 1:length(b_vetor)){
    plot(a_vetor, ((a_vetor/b_vetor[i])*(2/pi)),type="l",xlab="Valores de a",ylab="N hits",main=paste("b=",round(b_vetor[i],2),sep=""),ylim=c(0,5))
}
 
b_vetor<- seq(0.1,b,0.001)
a_vetor<- seq(0.1,1,length.out = 6)
par(mfrow=c(3,2))
for(i in 1:length(a_vetor)){
    plot(b_vetor, ((a_vetor[i]/b_vetor)*(2/pi)),type="l",xlab="Valores de b",ylab="N hits",main=paste("a=",round(a_vetor[i],2),sep=""),ylim=c(0,5))
}
 
 
##Plotando as duas variáveis juntas
angulo <- seq(0,pi/2,length.out = 500)
vetor <- seq(0,b/2,length.out = 500)
casos <- expand.grid(angulo=angulo,b=vetor)
casos$cor <- ifelse(casos$b<a/2 & casos$angulo < acos(casos$b/(a/2)),"lightblue","brown1")
 
 
plot(casos$b,casos$angulo,col=casos$cor,pch=19,cex=0.4,xaxt="n",yaxt="n",xlab="",ylab="",frame=F)
axis(1,at=c(0,a/2,b/2),labels=c(0,"a/2","b/2"))
axis(2,at=c(0,pi/4,pi/2),labels=c(0,expression(phi),expression(frac(pi,2))),las=2)
text(a/4,pi/8,expression('N'['hits']*'=1'))
text(a/2,pi/3.5,expression('N'['hits']*'=0'))