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:
- classe GL: contém métodos estáticos
para desenho na tela, usando a biblioteca OpenGL (padrão da indústria
para sistemas gráficos, veja link). Essa classe não deve ser alterada;
-
classe Renderer: possui um método
paint, que deve ser alterado para desenhar os objetos na tela, ou
solicitar que outro objeto o faça.
-
classes Figura: classe base para derivar a classe equivalente a cada figura;
-
classes Quadrado e Circulo: classes
derivadas de Figura, cujo método draw deve ser alterado para que a
figura seja efetivamente desenhada na tela. Para tanto, use os métodos
de desenho documentados em GL.h (ver abaixo).
-
programa principal: inicializa o sistema gráfico, preenche a estrutura de dados e ativa o sistema gráfico.
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.