Ir para o conteúdo

Figuras Geométricas

FIXME este tutorial precisa de revisão para estar de acordo com as Regras de Estilo e Formatação.

FIXME Faltam explicar melhor o código, adicionar comentarios ao longo do código.

Código que desenha algumas figuras geométricas (circunferências e rectângulos) e linhas (suporta cores). Grava as imagens como bmp ou xmb, ou mostra-as com o OpenGL.

O código pode ser compilado através do comando g++ -lm -lglut geometria.cc -o geometria, e já trás alguns exemplos.

Tem também um membro write_to_buffer que permite buffering, que dá muito jeito para qualquer programa com output "pessado".

geometria.cc:

/*
 *  GPL
 *
 *  geometria - Written by Diogo Sousa aka orium
 *  Copyright (C) 2007 Diogo Sousa
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <GL/gl.h>
#include <GL/glut.h>

#define BUFFER_SIZE 1024*50 /*50KB*/

void randomize();

typedef unsigned char rgbcolor;

class graphic
{
        private:
                int **matrix;

                void memcheck(void *) const;
                void write_to_buffer(int fd, char *buffer, const char *bytes, size_t size, int *counter) const;
                void dump_buffer(int fd, char *buffer, int size) const;
                float angle_to_rad(float angle) const;

                void froml(int wert, char *bopuffer) const;
                void froms(int wert, char *bopuffer) const;
        public:
                int xresolution;
                int yresolution;

                graphic(int xresolution, int yresolution);
                ~graphic();

                bool is_valid_point(int x, int y) const;

                int get_int_by_rgb(rgbcolor red, rgbcolor green, rgbcolor blue) const;
                void get_rgb_by_int(int rgb, rgbcolor *red, rgbcolor *green, rgbcolor *blue) const;

                /*Input*/
                int set_point(int x, int y, int color);
                void unset_point(int x, int y);
                void draw_rectangle(int x, int y, int l, int h, int color);
                void draw_circle(int x, int y, int r, int color);
                void draw_line(int x, int y, int fx, int fy, int color);

                /*Output*/
                int get_point(int x, int y) const;
                void print_graphic_tab(void) const;
                int write_xbm(const char *filename) const;
                int write_bmp(const char *filename) const;
                void show_graphic() const;
};

void graphic::memcheck(void *pointer) const
{
        if (pointer == NULL)
        {
                fprintf(stderr,"Erro na alocação de memórian");
                exit(-1);
        }
}

graphic::graphic(int xresolution, int yresolution)
{
        int counter=0;

        this->xresolution=xresolution;
        this->yresolution=yresolution;

        matrix=(int **)malloc(xresolution*sizeof(*matrix)); /*Reserva memória para resolution apontadores*/

        memcheck(matrix);

        while (counter < xresolution)
        {
                matrix[counter]=(int *)malloc(yresolution*sizeof(**matrix));

                memcheck(matrix[counter]);

                memset(matrix[counter],0,yresolution*sizeof(**matrix));

                counter++;
        }
}

graphic::~graphic()
{
        int counter=0;

        while (counter < xresolution)
        {
                free(matrix[counter]);
                counter++;
        }

        free(matrix);
}

float graphic::angle_to_rad(float angle) const
{
        return angle*M_PI/180.0;
}

int graphic::get_int_by_rgb(rgbcolor red, rgbcolor green, rgbcolor blue) const
{
        int ret;

        ret=blue;
        ret|=green << 8;
        ret|=red << 16;

        return ret;
}

void graphic::get_rgb_by_int(int rgb, rgbcolor *red, rgbcolor *green, rgbcolor *blue) const
{
        *red=rgb >> 16;
        *green=(rgb >> 8) & 255;
        *blue=rgb & 255;
}

void graphic::print_graphic_tab(void) const
{
        int x=0;
        int y;
        int color;

        /*Lê coluna a coluna, da esquerda para a direita*/

        for (x=0; x < xresolution; x++)
                for (y=0; y < yresolution; y++)
                        if ((color=get_point(x,y)))
                                printf("x=%d y=%d rgb=%dn",x,y,color);
}

void graphic::dump_buffer(int fd, char *buffer, int size) const
{
        if (size)
                write(fd,buffer,size);
}

void graphic::write_to_buffer(int fd, char *buffer, const char *bytes, size_t size, int *counter) const
{
        int bytecount=0;

        while (size--)
        {
                if (*counter >= BUFFER_SIZE)
                {
                        dump_buffer(fd,buffer,*counter);

                        *counter=0;
                }

                buffer[(*counter)++]=bytes[bytecount++];
        }
}

int graphic::write_xbm(const char *filename) const
{
        int fd;
        int counter=0;
        int bcounter=0;
        int buffcount=0;
        char *tmp;
        int byte;
        int x;
        int y;
        const char *hex="0123456789abcdef";
        char *buffer;

        fd=open(filename,O_WRONLY | O_CREAT | O_TRUNC,0700);

        if (fd == -1)
        {
                fprintf(stderr,"Erro ao abrir o ficheiron");

                return -1;
        }

        asprintf(&tmp,"%d",xresolution);

        memcheck(tmp);

        write(fd,"#define graphic_width ",22);
        write(fd,tmp,strlen(tmp));

        free(tmp);
        asprintf(&tmp,"%d",yresolution);

        write(fd,"n#define graphic_height ",24);
        write(fd,tmp,strlen(tmp));

        free(tmp);

        write(fd,"nstatic char graphic_bits[] = {n   ",33);

        buffer=(char *)malloc(BUFFER_SIZE);

        memcheck(buffer);

        y=yresolution-1;

        /*De cima para baixo, da esquerda para a direita*/
        /*
                Cada byte contêm 8 pixeis:
                        0xaa == 0b00000010 == BRANCO PRETO (...)

                Nota:
                        Quando acabar a linha, deve ser imediatamente escrito o byte, pois este não e' somado aos pixeis seguintes
        */
        while (y >= 0)
        {
                x=0;

                byte=0;
                bcounter=0;

                while (x < xresolution)
                {
                        if (get_point(x,y))
                                byte+=((int)exp2((float)bcounter));

                        bcounter++;

                        if (bcounter == 8)
                        {
                                bcounter=0;

                                write_to_buffer(fd,buffer,"0x",2,&buffcount);
                                write_to_buffer(fd,buffer,&hex[byte/16%16],1,&buffcount);
                                write_to_buffer(fd,buffer,&hex[byte%16],1,&buffcount);
                                write_to_buffer(fd,buffer,", ",2,&buffcount);

                                counter++;
                                byte=0;

                                if (counter == 12)
                                {
                                        write_to_buffer(fd,buffer,"n   ",4,&buffcount);
                                        counter=0;
                                }
                        }

                        x++;
                }

                if (bcounter)
                {

                        write_to_buffer(fd,buffer,"0x",2,&buffcount);
                        write_to_buffer(fd,buffer,&hex[byte/16%16],1,&buffcount);
                        write_to_buffer(fd,buffer,&hex[byte%16],1,&buffcount);
                        write_to_buffer(fd,buffer,", ",2,&buffcount);

                        counter++;

                        if (counter == 12)
                        {
                                write_to_buffer(fd,buffer,"n   ",4,&buffcount);
                                counter=0;
                        }
                }

                y--;
        }

        dump_buffer(fd,buffer,buffcount);

        free(buffer);

        lseek(fd,-2,SEEK_CUR);

        write(fd,"};",2);

        close(fd);

        return 0;
}

void graphic::froml(int wert, char *bopuffer) const /* Copiado do gimp 2.3.5 /plug-ins/bmp/bmpwrite.c */
{
  bopuffer[0] = (wert & 0x000000ff)>>0x00;
  bopuffer[1] = (wert & 0x0000ff00)>>0x08;
  bopuffer[2] = (wert & 0x00ff0000)>>0x10;
  bopuffer[3] = (wert & 0xff000000)>>0x18;
}

void graphic::froms(int wert, char *bopuffer) const /* Copiado do gimp 2.3.5 /plug-ins/bmp/bmpwrite.c */
{
  bopuffer[0] = (wert & 0x00ff)>>0x00;
  bopuffer[1] = (wert & 0xff00)>>0x08;
}

int graphic::write_bmp(const char *filename) const
{
        int fd;
        int buffcount=0;
        int xcounter;
        int ycounter;
        char *buffer;
        char hibuff[40];

        fd=open(filename,O_WRONLY | O_CREAT | O_TRUNC,0700);

        if (fd == -1)
        {
                fprintf(stderr,"Erro ao abrir o ficheiron");

                return -1;
        }

        buffer=(char *)malloc(BUFFER_SIZE);

        memcheck(buffer);

        /*header*/

        /*
         *      0x36 = Numero de bytes do "BM" + header + info
         */
        froml(0x36+yresolution*xresolution,&hibuff[0x00]);    /*Tamanho do ficheiro (em pixels (c/ header))*/
        froms(0,&hibuff[0x04]);                               /*????*/
        froms(0,&hibuff[0x06]);                               /*????*/
        froml(0x36,&hibuff[0x08]);                            /*Offset do inicio da imagem*/
        froml(40,&hibuff[0x0C]);                              /*Tamanho do Info*/

        write_to_buffer(fd,buffer,"BM",2,&buffcount);

        write_to_buffer(fd,buffer,hibuff,16,&buffcount);

        /*info*/
        froml(xresolution,&hibuff[0x00]);                               /*Colunas*/
        froml(yresolution,&hibuff[0x04]);                               /*Linhas*/
        froms(1,&hibuff[0x08]);                                         /*color planes????*/
        froms(24,&hibuff[0x0A]);                                        /*Bits por pixel*/
        froml(0,&hibuff[0x0C]);                                         /*Tipo de compressão (0=none)*/
        froml(xresolution*24/8*yresolution,&hibuff[0x10]);              /*Tamanho do ficheiro (em bytes)*/
        froml(0,&hibuff[0x14]);                                         /*Colunas (pixeis por metro)*/
        froml(0,&hibuff[0x18]);                                         /*Linhas (pixeis por metro)*/
        froml(0,&hibuff[0x1C]);                                         /*Nº de cores (0 em caso se use 24 bits de profundidade de cor)*/
        froml(0,&hibuff[0x20]);                                         /*Nº de cores, blah blah blah...*/

        write_to_buffer(fd,buffer,hibuff,36,&buffcount);

        /*Gravar a imagem propriamente dita*/

        for (ycounter=0; ycounter < yresolution; ycounter++)
        {
                for (xcounter=0; xcounter < xresolution; xcounter++)
                {
                        rgbcolor r;
                        rgbcolor g;
                        rgbcolor b;

                        get_rgb_by_int(get_point(xcounter,ycounter),&r,&g,&b);

                        write_to_buffer(fd,buffer,(const char *)&r,1,&buffcount);
                        write_to_buffer(fd,buffer,(const char *)&g,1,&buffcount);
                        write_to_buffer(fd,buffer,(const char *)&b,1,&buffcount);
                }
        }

        dump_buffer(fd,buffer,buffcount);
        free(buffer);
        close(fd);

        return 0;
}

int graphic::set_point(int x, int y, int color)
{
        if (!is_valid_point(x,y))
                return -1;

        matrix[x][y]=color;

        return 0;
}

void graphic::unset_point(int x, int y)
{
        matrix[x][y]=0;
}

void graphic::draw_rectangle(int x, int y, int l, int h, int color)
{
        int counter=y;

        /*Desenha linhas paralelas verticais com altura h*/
        while (counter <= y+h)
        {
                set_point(x,counter,color);
                set_point(x+l,counter,color);

                counter++;
        }

        /*Desenha linhas paralelas horizontais com comprimento l*/
        counter=x;
        while (counter <= x+l)
        {
                set_point(counter,y,color);
                set_point(counter,y+h,color);

                counter++;
        }
}

void graphic::draw_circle(int x, int y, int r, int color)
{
        float angle_inc;
        float angle;
        float co;
        float ca;
        int h;
        int l;

        angle_inc=(360.0/(pow((double)r,2.0)*M_PI))*1.1;

        /*printf("Incrementar angulo: %fºn",angle_inc);*/

        angle=angle_inc;

        set_point(x+r,y,color);
        set_point(x-r,y,color);
        set_point(x,y+r,color);
        set_point(x,y-r,color);

        for (angle=0.0; angle <= 90.0; angle+=angle_inc)
        {
                ca=cos(angle_to_rad(angle))*r;
                co=tan(angle_to_rad(angle))*ca;

                /*printf("Angulo=%f Cateto adjacente=%f Cateto oposto=%fn",angle,ca,co);*/

                h=(int)nearbyint(co);
                l=(int)nearbyint(ca);

                /*
                set_point(x+l,y+h,color);
                set_point(x-l,y+h,color);
                set_point(x+l,y-h,color);
                set_point(x-l,y-h,color);
                */


                set_point(x+l,y+h,get_int_by_rgb(0,(rand()%155)+100,0));
                set_point(x-l,y+h,get_int_by_rgb(0,(rand()%155)+100,0));
                set_point(x+l,y-h,get_int_by_rgb(0,(rand()%155)+100,0));
                set_point(x-l,y-h,get_int_by_rgb(0,(rand()%155)+100,0));
        }
}

void graphic::draw_line(int x, int y, int fx, int fy, int color)
{
        int xcounter;
        int ycounter;
        int h;
        float angle;
        float co;

        if (x > fx && y < fy
            || x > fx && y > fy)
        {
                int tmp;

                tmp=fx;
                fx=x;
                x=tmp;

                tmp=fy;
                fy=y;
                y=tmp;
        }

        set_point(x,y,color);

        /* Isto esta' tão mau... */
        if (fx > x && fy > y)
        {
                angle=(float)(fy-y+1)/(float)(fx-x+1);

                for (xcounter=x+1; xcounter <= fx; xcounter++)
                {
                        co=angle*(float)(xcounter-x);

                        h=(int)nearbyint(co);

                        set_point(xcounter,h+y,color);
                }
        }

        if (fx > x && fy < y)
        {
                angle=(float)(y-fy+1)/(float)(fx-x+1);

                for (xcounter=x+1; xcounter <= fx; xcounter++)
                {
                        co=angle*(float)(y-xcounter);

                        h=(int)nearbyint(co);

                        set_point(xcounter,h+y,color);
                }
        }

        if (fx == x)
        {
                if (fy < y)
                {
                        int tmp;

                        tmp=y;
                        y=fy;
                        fy=tmp;
                }

                for (ycounter=y+1; ycounter <= fy; ycounter++)
                        set_point(x,ycounter,color);
        }

        if (fy == y)
        {
                if (fx < x)
                {
                        int tmp;

                        tmp=x;
                        x=fx;
                        fx=tmp;
                }

                for (xcounter=x+1; xcounter <= fx; xcounter++)
                        set_point(xcounter,y,color);
        }
}


int graphic::get_point(int x, int y) const
{
        if (is_valid_point(x,y))
                return matrix[x][y];

        return -1;
}

bool graphic::is_valid_point(int x, int y) const
{
        return (x < xresolution 
                 && y < yresolution
                 && x >= 0 
                 && y >= 0);
}

graphic *gl_graphic;

void gl_draw_graphic(void)
{
        int xcounter;
        int ycounter;
        int color;
        rgbcolor r;
        rgbcolor g;
        rgbcolor b;

        glBegin(GL_POINTS);
                for (xcounter=0; xcounter < gl_graphic->xresolution; xcounter++)
                        for (ycounter=0; ycounter < gl_graphic->yresolution; ycounter++)
                                if ((color=gl_graphic->get_point(xcounter,ycounter)))
                                {
                                        gl_graphic->get_rgb_by_int(color,&r,&g,&b);
                                        glColor3f(r/255.0,g/255.0,b/255.0);
                                        glVertex3f((float)xcounter,(float)ycounter,0.0);
                                }
        glEnd();
}

void gl_display(void)
{
        glClear(GL_COLOR_BUFFER_BIT);

        gl_draw_graphic();

        glFlush();
}

void gl_init(void)
{
        glClearColor(0.0,0.0,0.0,0.0);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(0.0,(float)gl_graphic->xresolution,0.0,(float)gl_graphic->yresolution,-1.0,1.0);
}

void gl_window(void)
{
        int argv=0;

        glutInit(&argv,NULL);
        glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
        glutInitWindowSize(gl_graphic->xresolution,gl_graphic->yresolution);
        glutInitWindowPosition(50,50);
        glutCreateWindow("...");
        gl_init();
        glutDisplayFunc(gl_display);

        glutMainLoop();
}

void graphic::show_graphic(void) const
{
        gl_graphic=(graphic *)this;
        gl_window();
}

void face(graphic *graphic)
{
        graphic->draw_line(200,200,50,10,graphic->get_int_by_rgb(255,0,0));
        graphic->draw_rectangle(300,200,300,50,graphic->get_int_by_rgb(255,255,255));
        graphic->draw_circle(300,600,150,graphic->get_int_by_rgb(0,255,0));

        graphic->draw_circle(300,600,75,graphic->get_int_by_rgb(0,0,255));

        graphic->draw_circle(600,600,75,graphic->get_int_by_rgb(150,150,150));

        graphic->draw_circle(400,400,380,graphic->get_int_by_rgb(255,255,0));

        graphic->draw_rectangle(380,320,100,300,graphic->get_int_by_rgb(250,190,190));
}

void randomize(void)
{
        struct timeval tm;

        gettimeofday(&tm,NULL);

        srand((unsigned)(tm.tv_usec*1000000+tm.tv_usec+getpid()));
}

void random_painting(graphic *graphic) /* Just for fun :) */
{
        int x;
        int y;

        for (x=0; x < graphic->xresolution; x++)
                for (y=0; y < graphic->yresolution; y++)
                {
                        //graphic->set_point(x,y,graphic->get_int_by_rgb(rand()%256,rand()%256,rand()%256));
                        graphic->set_point(x,y,graphic->get_int_by_rgb(0,(rand()%156)+100,0));
                }
}

void draw_sic_cos_func(graphic &graphic)
{
        graphic.draw_line(0,graphic.yresolution/2,graphic.xresolution-1,graphic.yresolution/2,graphic.get_int_by_rgb(255,255,255));

        graphic.draw_line(0,(graphic.yresolution*0.4)+graphic.yresolution/2.0,graphic.xresolution-1,
                          (graphic.yresolution*0.4)+graphic.yresolution/2.0,graphic.get_int_by_rgb(100,0,0));

        graphic.draw_line(0,-(graphic.yresolution*0.4)+graphic.yresolution/2.0,graphic.xresolution-1,
                          -(graphic.yresolution*0.4)+graphic.yresolution/2.0,graphic.get_int_by_rgb(100,0,0));

        for (int x=0; x < graphic.xresolution; x++)
        {
                graphic.set_point(x,cos(x/100.0)*(graphic.yresolution*0.4)+graphic.yresolution/2.0,graphic.get_int_by_rgb(0,255,0));
                graphic.set_point(x,sin(x/100.0)*(graphic.yresolution*0.4)+graphic.yresolution/2.0,graphic.get_int_by_rgb(0,0,255));
        }
}

int main(void)
{
        graphic graphic(1200,800);
#if 0
        randomize();

        //random_painting(&graphic);
        face(&graphic);

        graphic.write_bmp("/tmp/graphic.bmp");

        graphic.show_graphic();
#endif
        draw_sic_cos_func(graphic);

        graphic.show_graphic();

        return 0;
}