Curso de graficación I
Contenido
• Dibujo de puntos
• Algoritmos comunes de trazo de rectas
• Algoritmos comunes de trazo de círculos
• Otras primitivas
• Bibliotecas gráficas en C y Java
• Uso de primitivas de biblioteca
• Principios de animación
• Mejora de la animación
La pantalla gráfica
0 Máxima x – 1
x = 0, y = 0
Máxima y – 1
x = Máxima x – 1
y = Máxima y – 1
Dibujo de puntos en C y Java
C
La función putpixel(int x, int y, int color) dibuja un
punto en la coordenada x,y con el color especificado.
Ejemplo: putpixel(50,25,7) dibuja un punto color gris en la
coordenada 50,25.
Java
En Java se dibuja en un objeto de la clase Graphics. No tiene una función
para dibujar puntos pero puede usarse:
Graphics g;
g.drawLine(50,25,50,25);
Ejemplo 1
#include <iostream>
#include <graphics.h>
y2 y1
m b y1 m x1
x2 x1
Para cualquier intervalo Dx de x a lo largo de la recta,
se puede calcular el Dy correspondiente como:
Dy m Dx
yi 1 yi m
Las rectas con pendiente mayor que 1, se invierten
los papeles de x y de y.
void dda(int x1,int y1,int x2,int y2,int color){
int dx,dy,steps,k;
float x_increment,y_increment,x,y;
dx = x2-x1;
dy = y2-y1;
if(abs(dx)>abs(dy))
steps = abs(dx);
else
steps = abs(dy);
if(steps==0)
steps = 1;
x_increment = (float)dx/steps;
y_increment = (float)dy/steps;
x = x1;
y = y1;
putpixel((int)x,(int)y,color);
for(k = 1;k <=steps ;k++){
x = x+x_increment;
y = y+y_increment;
putpixel((int)x,(int)y,color);
}
}
Algoritmo de línea de Bresenham
x , y
y +2
i
i i y +1
i
y
De aquí se tiene que: i
y m xi 1 b x
i
x +1 x +2
i i
Definimos:
d1 y yi d 2 y i 1 y
m xi 1 b y i y i 1 m x i 1 b
la diferencia es
d2 d1 2m xi 1 2 yi 2b 1
Definimos pi como:
pi Dx ( d1 d 2 )
2 Dy xi 2 Dx yi c
donde c es:
c 2Dy Dx 2b 1
Obtenemos pi+1 de pi como:
pi 1 2Dy xi 1 2Dx yi 1 c
Restando pi+1 y pi:
Simplificando:
p1 2Dy Dx
1. De como entrada los extremos de la línea. Almacene el punto
del extremo izquierdo en (x1, y1) y el derecho en (x2, y2).
2. El primer punto que se selecciona para desplegar es el punto
del extremo izquierdo(x1, y1).
3. Calcule Dx = x2 - x1, Dy = y2 - y1 y p1 = 2 Dy - Dx. Si p1 = 0, el
siguiente punto será (x1 +1, y1), sino será (x1 +1, y1 +1).
4. Incremente x en 1. Se seleccionará yi o yi +1 dependiendo si pi
0 o pi 0. En el primer caso
pi 1 pi 2Dy
x, y
Proyectos
Hacer las siguientes primitivas con funciones en C. Utilice las
primitivas de línea de graphics.h.
Algoritmos de generación de
circunferencias
La ecuación de la circunferencia
en coordenadas rectangulares es x xc2 y yc 2 r2
y yc r x xc
2
2
xc
Función en C
void PlotPoint(int xc, int yc, void CircleSimple(int xc, int yc,
int x, int y,int c) int r,int c){
{ int x,y;
putpixel(xc + x,yc + y,c); double yr;
putpixel(xc - x,yc + y,c); x = 0;
putpixel(xc + x,yc - y,c); y = r;
putpixel(xc - x,yc - y,c); yr = r;
putpixel(xc + y,yc + x,c); PlotPoint(xc,yc,x,y,c);
putpixel(xc - y,yc + x,c); /* se cicla hasta trazar todo un
putpixel(xc + y,yc - x,c); octante */
putpixel(xc - y,yc - x,c); while (x < yr){
} x = x + 1;
yr = sqrt(r*r-x*x);
y = (int)round(yr);
PlotPoint(xc,yc,x,y,c);
}
}
Círculo básico en Java
void CircleSimple(Graphics g, int xc, int yc, int r){
int x,y;
double yr;
x = 0;
y = r;
yr = r;
PlotPoint(x,y);
/* se cicla hasta trazar todo un octante */
while (x < yr){
x = x + 1;
yr = Math.sqrt(r*r-x*x);
y = (int)Math.round(yr);
PlotPoint(x,y);
}
}
Algoritmo de circunferencia de
Bresenham
Se supone (xi, yi) la posición más próxima a la trayectoria, la
siguiente posición es por tanto (xi+1, yi) o bien (xi+1, yi-1).
x x +1 x +2
i i i
y
i
y -1
i
y -2
i
2 2 2
x + y = r
Una medida de la diferencia de coordenadas puede definirse
como:
d1 y i2 y 2
y
y i2 r 2 x i 1
2 i
y d
1
d
d 2 y y i 1
2 2
2 y - 1
i
r x i 1 y i 1
2 2 2
x + 1
i
pi d1 d 2
2 x i 1 y y i 1 2r 2
2 2 2
i
El valor de pi+1 es:
pi1 2 xi 1 1 y yi1 1 2r
2 2
2 2
i 1
Simplificando
p1 3 2r
1. Seleccione la primera posición como
x , y 0, r
1 1
pi 1 pi 4 xi 6
y en caso contrario
pi1 pi 4 xi yi 10
si pi+1 <0 el siguiente punto será(xi+2, yi+1). De lo contrario es (xi+2, yi+1 –
1). La coordenada y es yi+1=yi, si pi <0 o bien yi+1= yi–1 si pi 0.
4. Repita el paso 3 hasta que x y y sean iguales.
Algoritmo de punto medio para la
circunferencia
El método de trazo del punto medio de la circunferencia se basa en la
definición de la función circunferencia:
f circunferencia x, y x 2 y 2 r 2
x k 1 y k 1 2 r 2
2 2
x k 1 1 y k 1 1 2 r 2
2 2
o
pk 1 pk 2 xk 1 yk21 yk2 yk 1 yk 1
main(){
arccoordstype arco;
initwindow(300,300);
circle(100,100,50);
ellipse(200,100,45,270,50,100);
arc(200,200,0,135,50);
getarccoords(&arco);
cout << "x=" << arco.x << "\n";
cout << "y=" << arco.y << "\n";
cout << "xinicio=" << arco.xstart << "\n";
cout << "yinicio=" << arco.ystart << "\n";
cout << "xfin=" << arco.xend << "\n";
cout << "yfin=" << arco.yend << "\n";
getch();
return 0;
}
Dibujo de una compuerta and
Centro de la compuerta: x, y
x – 2*tamanio
y – 2*tamanio x + 2*tamanio
y – 2*tamanio
x – 4*tamanio
y – tamanio Arco con centro en: x + 2*tamanio, y
Radio de:2*tamanio de 0 a 90 grados.
x – 2*tamanio
y – tamanio Arco con centro en: x + 2*tamanio, y+1
Radio de:2*tamanio de 270 a 360 grados.
x + 4*tamanio
y + tamanio x + 6*tamanio, y
x + 2*tamanio
y + tamanio x + 4*tamanio, y
x – 2*tamanio x +2 *tamanio
y + 2*tamanio y +2 *tamanio
Dibujo de una compuerta and
void dibujaAnd(int x, int y, int size){
int x1 = x-2*size;
int y1 = y-2*size;
line(x1,y1,x1,y1+4*size);
line(x1,y1,x1+4*size,y1);
line(x1,y1+4*size,x1+4*size,y1+4*size);
line(x+4*size,y,x+5*size,y);
line(x-2*size,y+size,x-3*size,y+size);
line(x-2*size,y-size,x-3*size,y-size);
arc(x+2*size,y,0,90,2*size);
arc(x+2*size,y,270,360,2*size);
}
Dibujo de una compuerta or
Circulo con centro en: x-4*tqamanio, y
Radio de:4*tamanio.
Centro de la compuerta: x, y
viewport(x-2*tamanio,y-2*tamanio,
x+6*tamanio,y+2*tamanio)
x – 2*tamanio
y – 2*tamanio x,y – 2*tamanio
x, y
w
h
Otras primitivas
Rectángulos rellenos: Los rectángulos rellenos pueden
generarse fácilmente haciendo un barrido de líneas de rastreo
desde la primera coordenada y a la segunda. El siguiente
código hace este trabajo:
void Rectangulo(int x1,int y1,int x2,int
y2){
int i;
for(i = y1;i<=y2; i++)
line(x1, i, x2, i);
}
Relleno de polígonos: el relleno opera calculando los tramos que
se hallan entre la arista de la izquierda y la derecha del polígono.
El algoritmo requiere conservar una lista ordenada respecto a y
de las aristas activas en cada fase del proceso.
Recorte de círculos: Se puede recortar todo el círculo respecto a
un rectángulo. Si el círculo lo intercepta, se divide en cuadrantes
y se aplica la prueba de aceptación o rechazo trivial para cada
uno. También se aceptar y rechazar a nivel de píxel..
Texto: el texto puede definirse mediante mapas de bits para cada
conjunto de caracteres. Se dibuja usando la función CopyPixel
del sistema.
Otras primitivas en C
void bar (int left, int top, int right, int bottom);
void bar3d (int left, int top, int right, int bottom,
int depth, int topflag);
void drawpoly (int numpoints, int *polypoints);
void fillellipse (int x, int y, int xradius, int
yradius);
void fillpoly (int numpoints, int *polypoints);
void floodfill (int x, int y, int border);
void pieslice (int x, int y, int stangle, int endangle,
int radius);
void rectangle (int left, int top, int right, int
bottom);
void sector (int x, int y, int stangle, int endangle,
int xradius, int yradius);
void setfillpattern (char *upattern, int color);
void setfillstyle (int pattern, int color);
void setlinestyle (int linestyle, unsigned upattern,
int thickness);
Algoritmo pastel
Algoritmo para dibujar un diagrama de pastel.
1. Iniciar ang = 0
2. Sum = suma de valores a representar
3. Para todos los valores hacer
4. Poner color del sector
5. Dibujar sector desde ang/suma*360 hasta
(ang + valor) /suma*360
6. Incrementar ang en valor
7. Fin para
Ejemplo, diagrama de pastel
#include <graphics.h>
void pastel(int n, float *a, int x, int y, int r){
float suma = 0,ang = 0;
int i;
for(i = 0; i<n; i++)
suma +=a[i];
for(i = 0; i<n; i++){
setfillstyle(1,i+1);
sector(x,y,(int)(ang/suma*360),(int)((ang+a[i])/suma*360),r,r);
ang += a[i];
}
}
main(){
float a[]={25.3,35.2,56.1,48.7,13.6};
initwindow(200,200);
pastel(5,a,100,100,60);
getch();
return 0;
}
Primitivas de texto en C
Despliega una cadena de texto en la posición del CP
void outtext (char *textstring);
Despliega una cadena en la coordenada x,y
void outtextxy (int x, int y, char *textstring);
Define el tipo de justificación para el texto
void settextjustify (int horiz, int vert);
Define la fuente, dirección y el tamaño del texto
void settextstyle (int font, int direction, int
charsize);
Define el tamaño del texto
void setusercharsize (int multx, int divx, int multy,
int divy);
Regresa el alto de una cadena de texto
int textheight (char *textstring);
Regresa el ancho de una cadena de texto
int textwidth (char *textstring);
Primitivas de texto en C cont.
Constantes de texto
Justificación vertical:
Justificación horizontal:
Fuentes:
std::ostringstream bgiout;
Igual a outtext:
outstream(std::ostringstream& out=bgiout);
Igual a outtextxy:
char pattern[] =
{0x66,0x99,0x81,0x81,0x42,0x24,0x18,0x00};
setfillpattern( pattern, 15 );
bar(200,200,300,300);
Actividad
Defina los siguientes patrones de línea y dibuje
algunas figuras con estos patrones:
Funciones de manejo de ventanas
Se pueden crear varias ventanas de despliegue. La
función initwindow(), regresa un entero que
permite identificar cada ventana.
Para establecer la ventana actual se utiliza
setcurrentwindow(int window);
Para determinar la ventana que está en uso se utiliza:
getcurrentwindow();
Actividad
Dibujar
Esperar N ms
Borrar
Actualizar
Animación #1
main(){
initwindow(400,300);
int x=rand()%400,y = rand()%50, vx = 5,vy = 5;
while(true){
setcolor(WHITE);
setfillstyle(1,WHITE); Dibujar
fillellipse(x,y,5,5);
delay(30);
setcolor(BLACK); Esperar N ms
setfillstyle(1,BLACK);
fillellipse(x,y,5,5);
}
x += vx; Borrar
y += vy;
if(x>400 || x<0) vx =-vx;
if(y>300 || y<0) vy =-vy; Actualizar
}
getch();
return 0;
}
Actividad
(x, y) (x + 3s, y)
(x – 3s, y)
(x – 2s, y + s/3) (x + 2s, y + s/3)
Dibujo de un carro
void carro(int x, int y, int size){
moveto(x-3*size,y-size);
linerel(size,0);
linerel(size,-size);
linerel(2*size,0); (x, y)
linerel(size,size);
linerel(size,0);
linerel(0,size);
linerel(-6*size,0);
linerel(0,-size);
circle(x-2*size,y+size/2,2*size/3);
circle(x+2*size,y+size/2,2*size/3);
}
Actividad
Usando la técnica anterior con linerel haga una
función para dibujar alguna de las siguientes figuras.
La figura debe poder dibujarse de cualquier tamaño y
en cualquier posición.
Animación con un fondo
Borrar pantalla
Dibujar fondo
Dibujar
Esperar N ms
Actualizar
void carro(int x, int y, int size){
moveto(x-3*size,y-(size));
linerel(size,0);
linerel(size,-size);
linerel(2*size,0);
linerel(size,size);
linerel(size,0);
linerel(0,size);
linerel(-6*size,0);
linerel(0,-size);
circle(x-2*size,y+size/2,2*size/3);
circle(x+2*size,y+size/2,2*size/3);
}
void fondo(){
int x = 0;
int d[] = {30,60,50,80,30,30,50,30};
int h[] = {70,100,140,30,80,30,60,70};
for(int i=0;i<9; i++){
setfillstyle(1,i+3);
bar(x,200,x+d[i],200-h[i]);
x += d[i];
}
}
main(){
initwindow(400,400);
int x=0,d=3;
char c;
while(true){
cleardevice();
setcolor(WHITE);
fondo();
carro(x,200,8);
delay(30);
x += d;
if(x <0 || x>400) d =-d;
if(kbhit()){
c = (char)getch();
switch(c){
case 27:return 0;
default: d =-d;
}
}
}
}
Dibujo de un carro relleno
void carro(int x, int y, int size){
Se requiere usar
int p[16]; fillpoly para
setcolor(WHITE);
setfillstyle(SOLID_FILL,RED);
rellenar el carro.
p[0] = x-3*size; p[1] = y-size;
p[2] = x-2*size; p[3] = y-size;
p[4] = x-size; p[5] = y-2*size; (x, y)
p[6] = x+size; p[7] = y-2*size;
p[8] = x+2*size; p[9] = y-size;
p[10] = x+3*size; p[11] = y-size;
p[12] = x+3*size; p[13] = y;
p[14] = x-3*size; p[15] = y;
fillpoly(8,p);
setcolor(BLACK);
setfillstyle(SOLID_FILL,BLACK);
fillellipse(x-2*size,y+size/3,2*size/3,2*size/3);
fillellipse(x+2*size,y+size/3,2*size/3,2*size/3);
}
Actividad
Modifique la figura de la actividad anterior para que se
dibuje con un relleno.
Dibujo de edificios
void fondo(){
int x = 0;
int d[] = {30,60,50,80,30,30,50,30};
int h[] = {70,100,140,30,80,30,60,70};
for(int i=0;i<9; i++){
setfillstyle(1,i+3);
bar(x,200,x+d[i],200-h[i]);
x += d[i];
}
}
Fondo #1
while(true){
cleardevice();
setcolor(WHITE);
Borrar pantalla
fondo();
carro(x,200,8);
delay(30); Dibujar fondo
x += d;
if(x <0 || x>400) d =-d;
if(kbhit()){ Dibujar
c = (char)getch();
switch(c){
case 27:return 0; Esperar N ms
default: d =-d;
}
} Actualizar
}
Mejora de animación con un fondo
Dibujar fondo
Dibujar
Esperar N ms
Borrar
Actualiza
Fondo #2
while(true){
setcolor(WHITE);
fondo();
carro(x,200,8);
setcolor(BLACK);
delay(30);
carro(x,200,8);
x += d;
if(x <0 || x>400) d =-d;
if(kbhit()){
c = (char)getch();
switch(c){
case 27:return 0;
default: d =-d;
}
}
}
Doble buffer
El doble buffer hace uso de dos ventanas de salida, una visible
y otra no visible. Las funciones en C son:
Poner página
activa y visible
Dibujar fondo
Dibujar
Intercambiar
páginas
Actualiza
while(true){
//dibuja en la página oculta
setvisualpage(1-p); Poner página
setactivepage(p); activa y visible
cleardevice();
fondo();
carro(x,200,8); Dibujar fondo
delay(10);
swapbuffers( );
Dibujar
p = 1-p;
x += d;
if(x <0 || x>400) d =-d;
if(kbhit()){ Intercambiar
c = (char)getch(); páginas
switch(c){
case 27:return 0;
default: d =-d; Actualiza
}
}
}
Implementación en C
main(){
initwindow(400,400);
setactivepage(1);
/* lee la imagen desde un archivo y la
dibuja en la pantalla invisible*/
readimagefile("PGS_img01.jpg",0,0,400,400);
size = imagesize(0, 0, 400, 400);
/* reserva memoria para la imagen */
img = malloc(size);
/* guarda le imagen en memoria */
getimage(0,0,400,400,img);
// fondo();
int x=0,d=6,p=0,size=10;
char c;
while(true){
setcolor(WHITE);
//dibuja en la página oculta
setvisualpage(1-p);
setactivepage(p);
setbkcolor(WHITE);
cleardevice();
fondo();
carro(x,200,size);
delay(30);
swapbuffers( );
p = 1-p;
x += d;
if(x <0 || x>400) d =-d;
}
}