PUCRS
Faculdade de Informática
Programação em C/C++


Exercício sobre Polimorfismo
Criado com base em material do
professor Marcelo Cohen


Atenção: este exercício deve ser obrigatoriamente realizado no Linux!

O objetivo é praticar os conceitos de herança e polimorfismo, bem como de uma aplicação gráfica interativa.

Neste exercício, iremos criar uma aplicação gráfica para desenhar figuras cujos dados são informados em um vetor de objetos. Para tanto, alguns módulos já foram criados:

Para a compilação do programa, baixe e descompacte este arquivo. A seguir, carregue no Code::Blocks o projeto figuragrafica.cbp.

Da forma como está, o programa abre uma janela gráfica e desenha um sistema de eixos (x e y), um quadrado, um círculo e aguarda uma tecla(veja figura abaixo). Ao pressionar-se a tecla ESC, o programa é finalizado.


Figura - Exemplo de Tela Gráfica


Os desenhos desta tela são construídos na classe Renderer. Abra o arquivo
Renderer.cpp e observe o método void Renderer::paint(), copiado abaixo. Todos os desenhos devem ser comandados a partir deste método. Os comandos de desenho estão todos na classe GL, descrita a seguir.

Observe a utilização dos métodos estáticos da classe GL. Para obter detalhes destes métodos, abra o arquivo GL.cpp.
Note que a área de desenhos da janela tem coordenadas que vão de (-10, -10), no canto inferior esquerdo, até (+10, +10) no canto superior direito.

// Este método deve desenhar os objetos, usando os métodos
// estáticos da classe GL
//
// Ele é chamado toda vez que a aplicação precisa redesenhar
// a tela gráfica. NÃO DEVE SER CHAMADO MANUALMENTE!
void Renderer::paint() {
    //
    // Use os métodos da classe GL para desenhar os objetos da lista
    // Ou melhor, cada objeto deve ser capaz de se auto-desenhar
    // (implemente o método draw() em cada classe derivada de figura)
    //
    // Dica: basta pedir que a lista desenhe todos os objetos...

    cout << "Renderer::paint" << endl;

    // Desenha uma grade
    // Cinza bem claro, para outras cores, veja exemplos em /etc/X11/rgb.txt
    GL::setColor(200,200,200);
    // Ajusta largura da linha (1.0 = 1 pixel de largura)
    GL::setLineWidth(1.0);
    for(int x=-10; x<=10; x+=1) {
        GL::drawLine(x,-10,x,10);
        GL::drawLine(-10,x,10,x);
    }

    // Desenha o sistema de eixos (não alterar)
    // Cinza, para outras cores, veja exemplos em /etc/X11/rgb.txt
    GL::setColor(60,60,60);
    // Linha mais larga
    GL::setLineWidth(3.0);
    GL::drawLine(-10,0,10,0);
    GL::drawLine(0,-10,0,10);

    // Volta ao normal
    //GL::setLineWidth(1.0);

    //
    // Exemplos de uso (retirar após implementar os métodos draw nas classes)
    //
    lista->drawAll();

    // Desenha um retângulo verde centrado em 0,0
    GL::setColor(0,200,0);        // verde claro
    GL::drawRect(-3,-3,3,3);

    // Desenha um círculo vermelho no quadrante superior direito
    GL::setColor(200,0,0);        // vermelho claro
    GL::drawCirc(5,5,3);
}



Classe GL

A classe GL possui os seguintes métodos de desenho, que devem ser utilizados da forma descrita (GL::draw... ou GL::fill), pois são métodos de classe (static).

GL::drawRect(float cx,float cy,float w,float h) - desenha um retângulo cujo centro é (cx,cy) e cuja largura e altura são w e h.

GL::drawCirc(float cx,float cy,float r) - desenha um círculo cujo centro é (cx,cy) e cujo raio é r.

GL::drawLine(float x1,float y1,float x2,float y2) - desenha uma linha de (x1,y1) até (x2,y2).

GL::drawTri(float x1, float y1, float x2, float y2, float x3, float y3) - desenha um triângulo definido pelos 3 pontos.

GL::setColor(GLubyte r, GLubyte g, GLubyte b) - especifica a cor a ser utilizada a partir deste momento (muda a cor de desenho e a de pintura). Cada parâmetro corresponde a uma componente de cor no sistema RGB (veja o arquivo /usr/share/X11/rgb.txt para ver muitos exemplos).

GL::setLineWidth(float w) - especifica a largura das linhas a serem desenhadas a partir deste ponto. O parâmetro w é um float que representa a largura desejada (1.0 = 1 pixel).

Obs: para desenhar figuras pintadas, troque draw por fill nas chamadas acima.


Classe Figura

A classe Figura é uma classe que serve como base para sejam derivadas as classes Quadrado e Circulo. Além da constutora que recebe como parâmetro um ponto, a classe possui um método virtual draw, que deverá ser sobreescrito nas classes derivadas, a fim de desenhar cada objeto, da maneira apropriada.

class Figura {
    public:
        Figura (float x, float y);
        virtual void draw();
    protected:
        float x,y;
};

Classe ListaDeFigurasPolim

A classe ListaDeFigurasPolim, é capaz de armazenar ponteiros para objetos da classe Figura. Como as classes
Quadrado e Circulo são derivadas da classe figura, logo obnjetos destas duas últimas classe também podem ser armazenados na lista.

O método que permite o armazenamento de um objeto na lista se chama add e está descrito abaixo.
O
parâmetro do método é um ponteiro para o objeto a ser colocado na lsita.

void ListaDeFigurasPolim::add(Figura *f) {
    if(total == MAX) return;
    figuras[total++] = f;
}



Tarefa 1:
A primeira tarefa é implementar os métodos de desenho (draw) em cada classe derivada de Figura. Após isso, no programa principal, deve-se adicionar manualmente alguns objetos. Com isso, será possível visualizar os objetos na execução do programa. Cuide as coordenadas: o sistema de eixos vai de -10 até 10, tanto para x como para y. Ou seja, as coordenadas (0,0) correspondem ao centro da tela.



Tarefa 2:
A segunda tarefa é criar uma classe Cor e incluir um objeto Cor na classe Figura.

A classe Cor representa uma cor em RGB (vermelho, verde, azul). O construtor de Cor deve receber 3 parâmetros, cada um como GLubyte (ou seja, bytes sem sinal). Deve haver métodos para retornar o R, G ou B armazenados. Depois, acrescente um método getCor e um setCor na classe Figura, de forma que seja possível obter ou alterar a cor do objeto. No método de desenho da classe Renderer, você deve obter a cor de cada objeto e utilizá-la para pintar cada objeto (troque as chamadas de drawRect por fillRect, por exemplo).



Tarefa 3:
A terceira tarefa é fazer com que os dados dos objetos sejam lidos de um arquivo-texto, por exemplo, objetos.txt. Considere que cada linha do arquivo começa com Q(uadrado) ou C(irculo), seguido dos parâmetros na ordem do construtor.


Tarefa 4:
Se sobrar tempo, você pode implementar outros tipos de figuras: retângulos e linhas são duas boas opções.


FIM.