Cubo 3D usando HTML5 e Canvas: Parte 2

No artigo anterior vimos como representar os pontos de um cubo em três dimensões, vimos também como aplicar as transformações de translação, ampliação e rotação, e desenvolvemos uma simulação que mostrava os pontos do cubo em rotação. Nesta segunda parte da série, veremos como representamos as faces de um cubo. Com este novo conhecimento vamos melhorar a simulação feita no artigo anterior, desenhando o cubo com as suas arestas como é mostrado na simulação baixo.

Representação de Faces

Analisemos o cubo apresentado na figura abaixo. Temos 8 vértices (A,B,C,D,E,F,G,H) e 6 faces (ABCD, BEFC, GEFH, AGHD, GEBA, HFCD).

Cubo

Olhando com atenção para o cubo podemos notar que cada vértice é partilhado por 3 faces. Tomando o vértice B como exemplo, podemos verificar que este é partilhado pelas faces BEFC, ABCD e GEBA.

Para representar uma face devemos indicar os 4 vértices que a compõem. Evidentemente devemos começar pela definição dos 8 vértices do cubo antes de definir as faces. Representaremos as faces em forma de array com 4 elementos que são índices dos vértices que a compõem.

No código abaixo apresento um exemplo de definição de vértices e faces. Em primeiro lugar defino um array constituído pelos vértices do cubo. De seguida, defino as 6 faces do cubo. Cada face é um array com 4 índices que indicam os vértices que a compõem. Por exemplo a segunda face é [1,5,6,2] o que significa que ela é constituída pelos vértices nas posições 1, 5, 6 e 2.

        var vertices = [
            new Point3D(-1,1,-1),
            new Point3D(1,1,-1),
            new Point3D(1,-1,-1),
            new Point3D(-1,-1,-1),
            new Point3D(-1,1,1),
            new Point3D(1,1,1),
            new Point3D(1,-1,1),
            new Point3D(-1,-1,1)
        ];

        var faces = [[0,1,2,3],[1,5,6,2],[5,4,7,6],[4,0,3,7],[0,4,5,1],[3,2,6,7]]

Portanto, representamos uma face como um array que armazena os índices (ou posições) dos vértices (no array de vértices).

Com este conhecimento podemos construir a simulação do cubo em linhas apresentada no topo da página.

Código Completo da Simulação

Abaixo está o código completo da simulação desta segunda parte. A simulação funciona deste modo: primeiro definimos os vértices, e de seguida definimos as faces. Então, durante a animação primeiro transformamos todos os vértices e armazenamos num array temporário. Depois de transformados todos os pontos desenhamos as linhas (arestas) que compõem cada uma das faces. Para identificar as arestas duma face usamos o array de faces.

Deixe-me explicar como identificamos as arestas de uma face. Seja f uma face. Então f[0] e f[1] definem a primeira aresta da face; f[1] e f[2] definem a segunda aresta; f[2] e f[3] definem a terceira aresta; e f[3] e f[0] definem a quarta aresta.

<!DOCTYPE html>
<html>
<head>
    <title>Cubo 3D usando HTML5 e Canvas: Parte 1</title>
    <script type="text/javascript">
        window.onload = startDemo;

        function Point3D(x,y,z) {
            this.x = x;
            this.y = y;
            this.z = z;

            this.scale = function(k) {
                return new Point3D(x * k,y * k,z * k)
            }

            this.translate = function(tx,ty,tz) {
                return new Point3D(x + tx,y + ty,z + tz)
            }

            this.rotateX = function(angle) {
                var rad, cosa, sina, y, z
                rad = angle * Math.PI / 180
                cosa = Math.cos(rad)
                sina = Math.sin(rad)
                y = this.y * cosa - this.z * sina
                z = this.y * sina + this.z * cosa
                return new Point3D(this.x, y, z)
            }

            this.rotateY = function(angle) {
                var rad, cosa, sina, x, z
                rad = angle * Math.PI / 180
                cosa = Math.cos(rad)
                sina = Math.sin(rad)
                z = this.z * cosa - this.x * sina
                x = this.z * sina + this.x * cosa
                return new Point3D(x,this.y, z)
            }

            this.rotateZ = function(angle) {
                var rad, cosa, sina, x, y
                rad = angle * Math.PI / 180
                cosa = Math.cos(rad)
                sina = Math.sin(rad)
                x = this.x * cosa - this.y * sina
                y = this.x * sina + this.y * cosa
                return new Point3D(x, y, this.z)
            }

            this.project = function(viewWidth, viewHeight, fov) {
                var factor, x, y
                factor = fov / this.z
                x = this.x * factor + viewWidth / 2
                y = -this.y * factor + viewHeight / 2
                return new Point3D(x, y, this.z)
            }
        }

        var vertices = [
            new Point3D(-1,1,-1),
            new Point3D(1,1,-1),
            new Point3D(1,-1,-1),
            new Point3D(-1,-1,-1),
            new Point3D(-1,1,1),
            new Point3D(1,1,1),
            new Point3D(1,-1,1),
            new Point3D(-1,-1,1)
        ];

        var faces = [[0,1,2,3],[1,5,6,2],[5,4,7,6],[4,0,3,7],[0,4,5,1],[3,2,6,7]]

        var angle = 0;

        function startDemo() {
            canvas = document.getElementById("tutorial");
            if( canvas && canvas.getContext ) {
                ctx = canvas.getContext("2d");
                setInterval(loop,33);
            }
        }

        function loop() {
            var t = new Array();

            ctx.fillStyle = "rgb(0,0,0)";
            ctx.fillRect(0,0,400,200);

            for( var i = 0; i < vertices.length; i++ ) {
                var v = vertices[i];
                var r = v.rotateX(angle).rotateY(angle).rotateZ(angle).translate(0,0,5);
                var p = r.project(400,200,200);
                t.push(p)
            }

            ctx.strokeStyle = "rgb(255,55,255)"

            for( var i = 0; i < faces.length; i++ ) {
                var f = faces[i]
                ctx.beginPath()
                ctx.moveTo(t[f[0]].x,t[f[0]].y)
                ctx.lineTo(t[f[1]].x,t[f[1]].y)
                ctx.lineTo(t[f[2]].x,t[f[2]].y)
                ctx.lineTo(t[f[3]].x,t[f[3]].y)
                ctx.closePath()
                ctx.stroke()
            }

            angle += 2
        }
    </script>
</head>
<body>
    <canvas id="tutorial" width="400" height="200">
      O seu browser não suporta o elemento canvas do HTML5.
      Por favor, actualize o seu browser.
    </canvas>
</body>
</html>

Conclusão

Hoje vimos como representar faces, e com isso melhoramos a simulação da parte 1 desenhando o cubo com as suas arestas. Para desenhar as arestas usamos a informação das faces. No próximo artigo veremos como desenhar um cubo sólido. Assine o nosso feed para ficar actualizado sobre lançamentos de novos artigos.

Se gostou do artigo, por favor clique num dos botões abaixo para partilhar o artigo. Considere também deixar um comentário expressando o que achou sobre o artigo.

Leave a Comment

Notify me of followup comments via e-mail. You can also subscribe without commenting.