introducción a la graficación por computadora unidad ii

64
Introducción a la Graficación por Computadora Unidad II

Upload: arturo-cuellar

Post on 23-Jan-2015

29 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Introducción a la Graficación por Computadora Unidad II

Introducción a la Graficación por Computadora

Unidad II

Page 2: Introducción a la Graficación por Computadora Unidad II

Transformaciones

Page 3: Introducción a la Graficación por Computadora Unidad II

Fundamentos de programación Gráfica

Java y la programación orientada a objetos

Page 4: Introducción a la Graficación por Computadora Unidad II

Programación Orientada a Objetos Java soporta el enfoque de Programación Orientada a Objetos

(OOP). OOP se enfoque en el diseño de los tipos de datos más que los

algoritmos denominados objetos y clases. ¿Por qué esta es una distinción importante?

Los objetos son sujetos y los algoritmos son los verbos.

¿Por qué es una tarea más fácl? Se identifican las acciones que se aplican a un sujeto dado Se identifican esos objetos con las acciones que le pueden ser aplicadas.

¿Por qué es más fácil? Identificando clases de objetos similares Especificando algoritmos de propósito general

Ventajas de OOP Promueve el diseño de abstracciones (ocultando el detalle) Promueve la reutilización del código Consolida la especificación y encapsulamiento del interface

Page 5: Introducción a la Graficación por Computadora Unidad II

Ejemplo Diseño Orientado a Objetos

Similar a las clases de objetos Interfaces comunes Utilización común Reutilización de código – herencia Aplaza la implementación y

algoritmos de decisión.

Diseño Procedural Centrado en los algoritmos –

forza a las implementaciones tempranas y algoritmos de decisión

Expone mayor detalle Dificulta la extensión Dificulta el mantenimiento

Page 6: Introducción a la Graficación por Computadora Unidad II

Polimorfismo El polimorfismo permite que los algoritmos sean

especificados en términos del tipo de objeto más general (y/o razonable)void processShape(Shape s) { s.setColor(Color.red); // ... Hacer algo s.draw();

}

Circle c = new Circle(x, y, r);

Point2D v1 = new Point2D(x1, y1);Point2D v2 = new Point2D(x2, y2);Point2D v3 = new Point2D(x3, y3);Triangle t = new Triangle(v1, v2, v3);

Point2D c1 = new Point2D(u1, v1);Point2D c2 = new Point2D(u2, v2);Triangle r = new Rectangle(c1, c2);

processShape(c);processShape(t);processShape(r);

Page 7: Introducción a la Graficación por Computadora Unidad II

Puntos específicos de Java Un programa fuente de java está compuesto de:

Espacios en blanco, comentarios, declaraciones y sentencias.

Los espacios en blanco son una parte importante de cualquier programa en Java No tienen efecto en el significado del código. Enfatiza la lectura y comunicación de estructuras Includes, espacios, tabuladores y líneas nuevas. Tipicamente, tabuladores o identación establece bloques de

objetos. Las nuevas líneas separan las sentancias

No hay reglas estrictas de cómo codificar, pero por lo menos

Adoptar un estilo consistente!

Page 8: Introducción a la Graficación por Computadora Unidad II

Comentarios Al lado de los espacios en blanco, la parte

más importante de cualquier programa en Java son sus comentarios.

Comentarios en la linea:answer = 42; // valid independent of the input

Bloques de comentarios/*

Metodo: paint.

Objetivo: dibujar el gráfico que será representado en el área denominada lienzo

Parametros: Objeto tipo Graphics

*/

Page 9: Introducción a la Graficación por Computadora Unidad II

Comentarios especiales Java provee una forma especial de comentarios para generar

documentación automática y páginas web. Este utiliza una herramienta denominada javadoc. Con estos bloques de comentarios especiales diversas variables especiales son reconocidas indicadas por un símbolo de @ al inicio.

Comentarios de documentación:/**

* Epecifica el método interfaz …

*

* @see java.graf

* @author Genaro Mendez

* @version 1.0

*/

public abstract interface ClaseI {

public final static int KARMA = Math.PI + Math.E;

}

Page 10: Introducción a la Graficación por Computadora Unidad II

Tipos de datos en Java Las variables en Java deben ser objetos, arreglos o uno de los tipos

de datos primitivos. Java tiene 8 tipos de datos primitivos:

(boolean, byte, short, int, long, double, float, char)

Notas: Todos los tipos de datos son con signo (por ello no está la palabra

reservada unsigned) A diferencia de C y C++ que es de 8 bits, las variables char en java son de

16 bits. Los tipos de datos primitivos son pasados por valor

boolean like6837 = true; // boolean tiene valores {true, false}

byte teeth = 32; // bytes van desde -128 to 127

float pi = 3.141592f; // se necesita forzosamente especificar “f”

short classes = 0xbad; // hexadecimal

Page 11: Introducción a la Graficación por Computadora Unidad II

Arreglos en Java Los arreglos en java caen entre un tipo de dato primitivo y un objeto Propiedades de los arreglos de java:

Pueden contener objetos, tipos primitivos, u otros arreglos Los contenidos son accesibles vía enteros positivos basados encerrados en paréntesis rectangulares La declaración y creación de los arreglos son operaciones distintas Los arreglos son creados con el operador new Los arreglos sin inicializar tienen el valor null Tienen una sola variable de instancia denominada length Como parámetros, de métodos, se pasan como referencia

byte buffer[]; // declaración del arreglo (buffer = null)

buffer = new byte[1024]; // creación del arreglo (contenido inicializado con ceros)

int table[] = new int[10]; // declaración y creación combinados

int sqrs[] = {0, 1, 4, 9, 16, 25, 36, 49, 64, 81 }; // con un inicializador

buffer[5] = sqrs[sqrs.length-1]; // referencias a arreglos

int triangle[][] = new int[3][];

triangle[0] = new int[3];

triangle[1] = new int[2];

triangle[2] = new int[1];

Page 12: Introducción a la Graficación por Computadora Unidad II

Objetos en Java Los objetos son contenedores agregados a los tipos primitivos, arreglos y

otros objetos. Las clases tienen las siguientes propiedades: Todo el código Java está contenido dentro de objetos Los objetos son pasados a los métodos por referencia Definiciones, declaraciones y creación de objetos son distintos. Las instancias de objetos son creados con el operador “new” y tienen un valor de null. Los objetos tienen cuatro formas: la clase, la clase abstracta, la clase interior, y la interfase.

class Circle { // definición del objeto

static double pi = 3.141592f; // variable

double radius; // variable

public Circle(double r) { // constructor

radius = r;

}

public double circumference() { return 2*pi*radius; }

public double area() { return pi*radius*radius; }

}

Circle c; // object declaration

c = new Circle(4.0); // object creation

double a = c.area();

Page 13: Introducción a la Graficación por Computadora Unidad II

Sentencias en Java Las sentencias es todo lo que resta.

Las sentencias individuales terminan en punto y coma Los bloques son sentencias agrupadas utilizando corchetes La sentencia más simple es la expresión

Una expresión es una cadena de variables y constantes separadas por operadores.

Java permite el siguiente conjunto de operadores dentro de sus sentencias.

. [] () ++ -- ! ~ instanceof

* / % + -

<< >> >>> < > <= >=

== != &

^ |

&& ||

?: = *= /= %= += -= <<= >>= >>>= &= ^= |=

Page 14: Introducción a la Graficación por Computadora Unidad II

Sentencias de control El segundo tipo de sentencias son las sentencias de control. La

siguiente lista ilustra algunas de las sentencias de control disponibles en Java:

if (BooleanExpression) Statement

if (BooleanExpression) Statement else Statement

while (BooleanExpression) Statement

do Statement while (BooleanExpression)

for (Expression; BooleanExpression; Expression) Statement

break <Label>

continue <Label>

return Expression

Label: Statement

switch (IntegerExpression) {

case IntegerValue:

Statements

default:

Statements

}

Page 15: Introducción a la Graficación por Computadora Unidad II

Construyendo un applet Iniciaremos escribiendo lo que se denomina un applet de Java. Esta

es una forma de un programa en Java que puede ser incrustado en un documento HTML y accesible desde el web. Un ejemplo de un documento HTML que llama a nuestro applet ejemplo es el siguiente:

<HTML>

<HEAD>

<TITLE>Demo Applet</TITLE>

</HEAD>

<BODY>

<H1>Demo Applet</H1>

<P>Mi clase favorita es Graficación</P>

<HR>

<CENTER>

<APPLET code="Demo.class" width=200 height = 200>

</APPLET>

</CENTER>

<HR>

</BODY>

</HTML>

Page 16: Introducción a la Graficación por Computadora Unidad II

El ejemplo Java A continuación, el código fuente del archivo demo.java:

import java.applet.*;

import java.awt.*;

public class Demo extends Applet {

int count;

public void init()

{

count = 1;

}

public void paint(Graphics g)

{

g.setColor(Color.red);

for (int y = 15; y < size().height; y += 15) {

int x = (int) (size().width/2 + 30*Math.cos(Math.PI*y/75));

g.drawString("Hola", x, y);

}

showStatus("Paint invovado " + count + " veces" + ((count > 1) ? "s" : ""));

count += 1;

}

}

Page 17: Introducción a la Graficación por Computadora Unidad II

Graficación en Java

El proceso de rasterización

Page 18: Introducción a la Graficación por Computadora Unidad II

Revisión de los Displays Rasterizados

El Display se sincroniza con el barrido del CRT Se emplea una memoria especial para la actualización

de la pantalla. Los Pixeles son los elementos discretos desplegados Generalmente, las actualizaciones son visibles.

Page 19: Introducción a la Graficación por Computadora Unidad II

Sistema de Display Gráfico avanzado

Agrega un segundo frame buffer Se intercambia durante el barrido

vertical Las actualizaciones son invisibles Es más costoso

Page 20: Introducción a la Graficación por Computadora Unidad II

Implementando un objeto de Rasterización de Memoria

Mantiene una copia de la pantalla (o parte de esta) en memoria.

Depende de una copia rápida Las actualizaciones son casi invisibles Modelo conceptual de un objeto físico.

Page 21: Introducción a la Graficación por Computadora Unidad II

Un modelo en Java de la Memoria Raster Es posible realizar gráficos empleando el paquete de clases

estándar Java2D y Java3D, pero efectos didácticos y así lograr comprender el funcionamiento del proceso de graficación, iniciaremos el trabajo desde cero. Para empezar crearemos el modelo de memoria del rasterizado.

import java.awt.*;

import java.awt.image.*;

class Raster {

public int width, height;

public int pixel[];

: : :

Descarga el código fuente de la clase Raster (Raster.java)

Los pixeles se almacenan como un arreglo de int denominado pixel[]

•Cada pixel consta de 32 bits•Cuatro bytes uno para cada color primario y un valor Alpha

•Cuando Alpha es 0 es transparente•Cuando Alpha es 255 es opaco

Page 22: Introducción a la Graficación por Computadora Unidad II

Los métodos de la Clase Raster ///////////////////////// Constructores //////////////////////

/*

* Constructor sin argumentos,

*/

public Raster() {

}

/**

* Este constructor crea un objeto Raster sin inicializar

* de las dimensiones dadas por w (width) y h (height).

*/

public Raster(int w, int h)

{

width = w;

height = h;

pixel = new int[w*h];

}

Page 23: Introducción a la Graficación por Computadora Unidad II

Los pixeles de la clase Raster Como se mencionó antes los pixeles son almacenados en enteros de 32

bits.

/**

* Este constructor crea un Raster inicializado con

* el contenido de una imagen

*/

public Raster(Image img){

try {

PixelGrabber grabber = new PixelGrabber(img, 0, 0, -1, -1, true);

if (grabber.grabPixels()) {

width = grabber.getWidth();

height = grabber.getHeight();

pixel = (int []) grabber.getPixels();

}

} catch (InterruptedException e) {

}

}

http://www.permadi.com/tutorial/javaGetImagePixels/index.html

Page 24: Introducción a la Graficación por Computadora Unidad II

Métodos de rasterizado////////////////////////// Metodos //////////////////////////

/* Retorna el número de pixeles */

public final int size( ){

return pixel.length;

}

/* Rellena el objeto Raster con un color solido */

public final void fill(Color c){

int s = size();

int rgb = c.getRGB();

for (int i = 0; i < s; i++)

pixel[i] = rgb;

}

/* Convierte la imagen rasterizada a un objeto Image */

public final Image toImage(Component root){

return root.createImage(new MemoryImageSource(width, height, pixel, 0, width));

}

Page 25: Introducción a la Graficación por Computadora Unidad II

Más métodos de rasterizado

/* Obtiene un color desde un Raster */

public final Color getColor(int x, int y){

return new Color(pixel[y*width+x]);

}

/* Establece un pixel en un valor dado */

public final boolean setPixel(int pix, int x, int y){

pixel[y*width+x] = pix;

return true;

}

/* Establece un pixel en un color dado */

public final boolean setColor(Color c, int x, int y){

pixel[y*width+x] = c.getRGB();

return true;

}

Page 26: Introducción a la Graficación por Computadora Unidad II

Ejemplo de uso Rastest.javaimport java.applet.*;

import java.awt.*;

import Raster;

public class Rastest extends Applet {

Raster raster;

public void init() {

String filename = getParameter("image");

raster = new Raster(getImage(getDocumentBase(), filename));

}

public void paint(Graphics g) {

Image output = raster.toImage(this);

g.drawImage(output, 0, 0, this);

}

public void update(Graphics g) {

paint(g);

}

public boolean mouseUp(Event e, int x, int y) {

int s = raster.size();

for (int i = 0; i < s; i++) {

raster.pixel[i] ^= 0x00ffffff;

}

repaint();

return true;

}

}

Page 27: Introducción a la Graficación por Computadora Unidad II

Para probar el programa Código HTML de la página que incrustará el applet.<HTML>

<HEAD>

<TITLE>Prueba Applet Raster</TITLE>

</HEAD>

<BODY>

<H1>Prueba Applet Raster</H1>

<HR>

<CENTER>

<APPLET code="Rastest.class" width=160 height = 160>

<param name="image" value=“cualquiera.jpg">

</APPLET>

</CENTER>

<HR>

</BODY>

</HTML>

Page 28: Introducción a la Graficación por Computadora Unidad II

Sprites

Sprites, PixBlt

Page 29: Introducción a la Graficación por Computadora Unidad II

Sprites Los sprites son objetos gráficos que se

mueven sobre la imagen de fondo denominada escenario o “playfield”.

Page 30: Introducción a la Graficación por Computadora Unidad II

Un Sprite es un Rasterclass Sprite extends Raster {

int x, y; // posicion actual del sprite en el playfield

int width, height; // tamaño del cuado del sprite

public Sprite(Image image); // inicializa el sprite con una imagen

public void Draw(Raster bgnd); // Dibuja el sprite en el Raster

}

Cosas que hay que considerar

El método Draw() debe manejar la transparencia de pixeles, y debe también manejar todos los casos donde el sprite sale del escenario.

Page 31: Introducción a la Graficación por Computadora Unidad II

Un Sprite animado es un spriteclass AnimatedSprite extends Sprite {

int frames; // cuadros en un sprite “frames”

// otras variables privadas

public AnimatedSprite(Image images, int frames);

public void addState(int track, int frame, int ticks, int dx, int dy);

public void Draw(Raster bgnd);

public void nextState();

public void setTrack();

}

Un track es una serie de “frames”. El frame se despliega por pequeños periodos de tiempo denominados “ticks”. La posición del sprite es entonces movida (dx,dy) pixeles.

Page 32: Introducción a la Graficación por Computadora Unidad II

Un “PlayField” es un Sprite y tiene Sprites Animados (Animated Sprites)class Playfield extends Raster {

Raster background; // imagen del playfield

AnimatedSprite sprite[]; // lista de sprites en el playfield

public Playfield(Image bgnd, int numSprites);

public void addSprite(int i, AnimatedSprite s);

public void Draw( );

}

Page 33: Introducción a la Graficación por Computadora Unidad II

Imágenes en Java (Images) El paquete java.awt.image, es un estándar un poco

confuso. Hay tres interfaces claves para imágenes en Java

ImageProducer genera los pixeles ImageConsumer agrupa los pixeles en imágenes ImageObserver monitorea el progreso de las imágenes.

Page 34: Introducción a la Graficación por Computadora Unidad II

Métodos getImage() Crea objetos Image imageProducers se alinea a quienes comprenden

el formato del pixel con el que se basa la imagen. Establece un imageConsumer para guardar la

imagen de los datos. Pobremente documentado.

Page 35: Introducción a la Graficación por Computadora Unidad II

Ejemplo getImage()import java.awt.*;

import java.applet.*;

public class ImageTest extends Applet {

Image jpgimg, gifimg;

public void init() {

jpgimg = getImage(getCodeBase(),

"MITdome.jpg");

gifimg = Toolkit.

getDefaultToolkit().

getImage("cowicon.gif");

}

public void paint(Graphics g) {

g.drawImage(jpgimg, 0, 0, this);

g.drawImage(gifimg, 180, 5, this);

}

}

Page 36: Introducción a la Graficación por Computadora Unidad II

Otros formatos de imagen? ¿Qué pasa si queremos leer otros formatos de

imagen? .bmp, .ppm, .tif, .tga, .rgb, .ras, .psd

Debemos implementar un imageProducer para leer pixeles, un imageConsumer para crear la imagen y mantener actualizado al imageObserver

¿Donde?¿Cómo?

Page 37: Introducción a la Graficación por Computadora Unidad II

Por suerte, ya contamos con algo En nuestra clase Rasterpublic final Image toImage(Component root){

return root.createImage(new MemoryImageSource(width, height, pixel, 0, width));

}

El método MemoryImageSource es un imageProducer(root es un imageObserver)

public class ppmDecoder {

Raster imgRaster; // declara un Raster

public ppmDecoder() { }

public Image getppmImage(URL url) {

// abre el url y lee la cabecera de la imagen

.

// crea imgRaster

.

// copia los datos desde el archivo hacia imgRaster

return (imgRaster.toImage());

}

}

Page 38: Introducción a la Graficación por Computadora Unidad II

PixBlts Los PixBlts son métodos de rasterización para mover y lipiar

sub-bloques de pixeles desde una región de un raster a otra.

Muy utilizado por sistemas de ventanas

•Moviendo ventanas •Realizando scroll de texto•Copiando y limpiando

Page 39: Introducción a la Graficación por Computadora Unidad II

Parece fácil Aquí un método PixBlt

public void PixBlt(int xs, int ys, int w, int h, int xd, int yd){

for (int j = 0; j < h; j++) {

for (int i = 0; i < w; i++) {

this.setPixel(raster.getPixel(xs+i, ys+j), xd+i, yd+j);

}

}

}

Page 40: Introducción a la Graficación por Computadora Unidad II

Algoritmos de dibujo de líneas

Optimización, rapidez y exactitud

Page 41: Introducción a la Graficación por Computadora Unidad II

Algoritmos de dibujo de líneas Conversión de escaneo

Conversión de escaneo o rasterización Depende de la naturaleza de escaneo de los displays de

rasters Los algoritmos son fundamentales tanto para la

graficación por computadora en 2D y en 3D Transformando lo continuo en lo discreto (sampling) El dibujo de líneas era una tarea fácil para los displays

vectorizados. La mayoría de los algoritmos de dibujo de líneas fueron

desarrollados inicialmente para los plotters de plumas La mayoría de los algoritmos de conversión

desarrollados para plotters se atribuyen a Jack Bresenham

Page 42: Introducción a la Graficación por Computadora Unidad II

En la búsqueda de la Linea Ideal Lo mejor que se puede hacer es una

aproximación discreta de una linea ideal.

Cualidades importantes de la línea Apariencia continua Grosor y brillantes continua Encendido de los pixeles más cercanos a la línea

ideal Que tan rápido se puede generar la línea.

Page 43: Introducción a la Graficación por Computadora Unidad II

Linea simple Basado en el algoritmo simple del algebra.

y = mx + b Ver demostración

public void lineaSimple(int x0, int y0, int x1, int y1, Color color) {

int pix = color.getRGB();

int dx = x1 - x0;

int dy = y1 - y0;

raster.setPixel(pix, x0, y0);

if (dx != 0) {

float m = (float) dy / (float) dx;

float b = y0 - m*x0;

dx = (x1 > x0) ? 1 : -1;

while (x0 != x1) {

x0 += dx;

y0 = Math.round(m*x0 + b);

raster.setPixel(pix, x0, y0);

}

}

}

Page 44: Introducción a la Graficación por Computadora Unidad II

Optimizando lineaSimple ver demostración Problema: lineaSimple() no da resultados satisfactorios para inclinaciones mayores a uno

(>1). Solución: Simetría

public void lineaMejorada(int x0, int y0, int x1, int y1, Color color) {

int pix = color.getRGB();

int dx = x1 - x0; int dy = y1 - y0;

raster.setPixel(pix, x0, y0);

if (Math.abs(dx) > Math.abs(dy)) { // inclinacion < 1

float m = (float) dy / (float) dx; // calcular inclinacion

float b = y0 - m*x0;

dx = (dx < 0) ? -1 : 1;

while (x0 != x1) {

x0 += dx;

raster.setPixel(pix, x0, Math.round(m*x0 + b));

}

} else

if (dy != 0) { // inclinacion >= 1

float m = (float) dx / (float) dy; // Calcular inclinacion

float b = x0 - m*y0;

dy = (dy < 0) ? -1 : 1;

while (y0 != y1) {

y0 += dy;

raster.setPixel(pix, Math.round(m*y0 + b), y0);

}

}

}

Page 45: Introducción a la Graficación por Computadora Unidad II

Optimizando ciclos internos Se optimizan éstos fragmentos de código donde el algoritmo

pierde la mayor parte de su tiempo. Remover la invocación a métodos que sean innecesarios.

Reemplazamos Math.round(m*x0 + b)

con (int) (m * y0 + b + 0.5)

Utilizar cálculos incrementales.

Considerar la expresión

y = (int) (m * y0 + b + 0.5)

El valor de y es conocido en x0 (por ejemplo es y0 + 0.5) Valores futuros de y pueden ser expresados en términos de sus

valores anteriores con una ecuación de diferencias.

yi+1 = yi + m o yi+1 = yi - m

Page 46: Introducción a la Graficación por Computadora Unidad II

Algoritmo modificado ver demostración Este método de dibujo de línea se denomina “Digital Differential Analyzer” o DDApublic void lineaDDA(int x0, int y0, int x1, int y1, Color color) {

int pix = color.getRGB();

int dy = y1 - y0; int dx = x1 - x0;

float t = (float) 0.5; // desplazamiento a redondear

raster.setPixel(pix, x0, y0);

if (Math.abs(dx) > Math.abs(dy)) { // inclinacion < 1

float m = (float) dy / (float) dx; // calcular inclinacion

t += y0;

dx = (dx < 0) ? -1 : 1;

m *= dx;

while (x0 != x1) {

x0 += dx; // paso al siguiente valor x

t += m; // agregar la inclinación al valor y

raster.setPixel(pix, x0, (int) t);

}

} else { // inclinación >= 1

float m = (float) dx / (float) dy; // calcular la inclinación

t += x0;

dy = (dy < 0) ? -1 : 1;

m *= dy;

while (y0 != y1) {

y0 += dy; // paso al siguiente valor y

t += m; // agregar inclinación al valor x

raster.setPixel(pix, (int) t, y0);

}

}

}

Page 47: Introducción a la Graficación por Computadora Unidad II

Comparación de algoritmos Los compiladores modernos implementan estos tipos

de optimizaciones. Dilema: ¿Es mejor mantener un código legible y depender de un

compilador para que realice implícitamente las optimizaciones o codificamos la optimización explícitamente perdiendo algo de legibilidad?

La respuesta no es muy clara. En general, se debe codificar lo más legiblemente posible. Los comentarios pueden resultar muy largos al explicar el código optimizado. Por otra parte rara vez se negocia la portabilidad por la elegancia. De ésta manera, si la aplicación depende de un algoritmo rápido, se prefiere codificar directamente que esperar a que el compilador realice el trabajo, y más aún si se desconoce el resultado debido a las diferentes versiones y plataformas de los compiladores.

Ver demostración de comparación de Algoritmos

Page 48: Introducción a la Graficación por Computadora Unidad II

Optimizaciones de bajo nivel Las optimizaciones de bajo nivel son problemáticas, debido

a que dependen del detalle específico de la máquina. Sin embargo, un conjunto de reglas generales que son mas

o menos consistentes en todas las máquinas, incluye: La adición y substracción son generalmente más rápidas que la

multiplicación La Multiplicación es generalmente más rápida que la división Utilizar tablas para evaluar funciones discretas es más rápido que

calcularlas. Los cálculos de números enteros son más rápidos que los cálculos

de números de punto flotante. Evitar los cálculos innecesarios probando varios casos especiales. Las pruebas o comparaciones intrínsecas en la mayoría de las

máquinas son “mayor que”, “menor que”, “mayor o igual “, “menor o igual” a cero.

Page 49: Introducción a la Graficación por Computadora Unidad II

Aplicación de optimizaciones de bajo nivel Observamos que la inclinación siempre es racional

(un radio de dos enteros)m = (y1 – y0) / (x1 – x0)

Observamos también que la parte incremental del algoritmo nunca genera un nuevo y que sea más que una unidad, en relación a la anterior (debido a que la inclinación siempre es menor a uno)

yi+1 = yi + m

Así, si mantenemos solo una parte fraccional de y podremos estar dibujando la línea hasta notar que la fracción ya excede la unidad. Si inicializamos la fracción con 0.5, entonces manejaremos el redondeo correctamente en la rutina DDA

fraccion += m;if (fraccion >= 1) { y = y + 1; fraccion -= 1; }

Page 50: Introducción a la Graficación por Computadora Unidad II

Mas optimizaciones de bajo nivel “y” ahora es un entero. ¿Podremos representar la fracción como un

entero?

Después de que se dibuja el primer pixel (lo cual sucede fuera del ciclo principal) la fracción correcta es:

fraction = ½ + dy/dx Si escalamos la fracción por 2*dx resulta en la

siguiente expresión:

scaledFraction = dx + 2*dy Y la actualización incremental se convierte en:

scaledFraction = 2*dy Y nuestra prueba debe ser modificada para reflejar la

nueva escala.

if (scaledFraction >= 2*dx)

Page 51: Introducción a la Graficación por Computadora Unidad II

Mas optimizaciones de bajo nivel (2) Esta prueba se puede hacer contra el valor cero si el valor

inicial de scaledFraction y tiene restado 2*dx de éste. Dando, fuera del ciclo:

OffsetScaledFraction = dx + 2*dy – 2*dx = 2*dy – dx

En el interior del ciclo se convierte en:

OffsetScaledFraction += 2*dy;

if (OffsetScaledFraction >= 0) {

y = y + 1;

fraction -= 2 * dx

} Podemos también duplicar los valores de dy y dx (este puede ser

completado tanto con la suma o con corrimientos fuera del ciclo)

Page 52: Introducción a la Graficación por Computadora Unidad II

El método resultante se conoce como Algoritmo de Bresenham

public void lineaBresenham(int x0, int y0, int x1, int y1, Color color) {

int pix = color.getRGB();

int dy = y1 - y0; int dx = x1 - x0;

int stepx, stepy;

if (dy < 0) { dy = -dy; stepy = -1; } else { stepy = 1; }

if (dx < 0) { dx = -dx; stepx = -1; } else { stepx = 1; }

dy <<= 1; dx <<= 1 // dy es ahora 2*dy y dx es ahora 2*dx

raster.setPixel(pix, x0, y0);

if (dx > dy) {

int fraction = dy - (dx >> 1); // igual que 2*dy - dx

while (x0 != x1) {

if (fraction >= 0) {

y0 += stepy; fraction -= dx; // igual que fraction -= 2*dx

}

x0 += stepx; fraction += dy; // igual que fraction -= 2*dy

raster.setPixel(pix, x0, y0);

}

} else {

int fraction = dx - (dy >> 1);

while (y0 != y1) {

if (fraction >= 0) {

x0 += stepx; fraction -= dy;

}

y0 += stepy;

fraction += dx; raster.setPixel(pix, x0, y0);

}

}

}

ver demostración

Page 53: Introducción a la Graficación por Computadora Unidad II

Cuestión de Infraestructura Existe todavía una multiplicación oculta dentro del ciclo

interno. /**

* Sets a pixel to a given value

*/

public final boolean setPixel(int pix, int x, int y)

{

pixel[y*width+x] = pix;

return true;

}

La siguiente optimización del algoritmo de Bresenham elimina incluso ésta multiplicación.

Page 54: Introducción a la Graficación por Computadora Unidad II

Algoritmo Bresenham más rápido public void lineFast(int x0, int y0, int x1, int y1, Color color) {

int pix = color.getRGB();

int dy = y1 - y0; int dx = x1 - x0; int stepx, stepy;

if (dy < 0) { dy = -dy; stepy = -raster.width; } else { stepy = raster.width; }

if (dx < 0) { dx = -dx; stepx = -1; } else { stepx = 1; }

dy <<= 1; dx <<= 1;

y0 *= raster.width; y1 *= raster.width; raster.pixel[x0+y0] = pix;

if (dx > dy) {

int fraction = dy - (dx >> 1);

while (x0 != x1) {

if (fraction >= 0) {

y0 += stepy; fraction -= dx;

}

x0 += stepx; fraction += dy;

raster.pixel[x0+y0] = pix;

}

} else {

int fraction = dx - (dy >> 1);

while (y0 != y1) {

if (fraction >= 0) {

x0 += stepx; fraction -= dy;

}

y0 += stepy; fraction += dx;

raster.pixel[x0+y0] = pix;

}

}

}

Page 55: Introducción a la Graficación por Computadora Unidad II

Mas algoritmos

El algoritmo de “Two Step” (2-pasos) toma el interesante enfoque de tratar el dibujo de una línea como un autómata o máquina de estados finitos.

Si observamos la posible configuración de los siguientes dos pixeles de una línea, es fácil saber que solo existe un conjunto finito de posibilidades.

El algoritmo de “Two step” también explota la simetría del dibujo de líneas dibujando simultáneamente desde los extremos hacia el punto medio.

El código es un poco largo para que pueda se expuesto de forma completa en una lámina, por lo cual éste puede ser visto en la siguiente liga: lineaTwoStep

Con tantos libros haciendo referencia al método Bresenham, podríamos pensar que el desarrollo de algoritmos de dibujo, terminó con el famoso algoritmo de Bresenham. Sin embargo, ha habido un trabajo significativo desde entonces. El siguiente algoritmo denominado “Two Step”, desarrollado por Xiaolin Wu.

Page 56: Introducción a la Graficación por Computadora Unidad II

Algunos puntos que considerar Puntos extremos en coordenadas no enteras

(ocurre frecuentemente en procesos de dibujo de lineas 3D)

¿Qué sucede si los extremos de la línea están fuera del área de visualización?

¿Cómo se maneja el grosor de las líneas? Optimizaciones para segmentos de líneas

conectadas. Las líneas se muestran en lugares extraños

(considerar aspecto de la imagen)

Page 57: Introducción a la Graficación por Computadora Unidad II

Dibujo de polígonos

El triángulo

Page 58: Introducción a la Graficación por Computadora Unidad II

Dibujo de triángulos Los triángulos son quizás la primitiva de

dibujo más importante. Los triángulos son el polígono mínimo. Están

determinados por solo 3 puntos o 3 ejes.

Triángulos son siempre polígonos convexos.

No-ConvexoConvexo

Page 59: Introducción a la Graficación por Computadora Unidad II

Dibujo de triángulos Los triángulos son matemáticamente

muy simples. La matemática relacionada con los triángulos

involucra solamente ecuaciones de primer grado.

Las formas arbitrarias pueden aproximarse con triángulos. Cualquier figura bidimensional puede

aproximarse con un polígono utilizando aproximación lineal a la superficie. Para mejorar la calidad de ajuste solo se necesita incrementar el número de lados.

Cualquier polígono puede ser fraccionado en triángulos.

Page 60: Introducción a la Graficación por Computadora Unidad II

Estrategias de dibujo de triángulos Hay dos estrategias comunes para el dibujo de triángulos

rellenos. La primera utiliza un seguimiento alrededor de los ejes y la segunda por ecuaciones de ejes.

Seguimiento de ejes: Ordena los vértices tanto “x” y “y”. Determina si el vértice de en medio, se encuentra a la

derecha o izquierda del polígono. Determina el eje derecho o izquierdo para cada línea de

seguimiento Ventajas y Desventajas

Cargado de casos especiales De difícil exactitud Requiere del cálculo de desplazamientos

fraccionales cuando se interpolan parámetros Generalmente muy rápido.

Page 61: Introducción a la Graficación por Computadora Unidad II

Método por ecuaciones de ejes Otro enfoque para el dibujo de triángulos

utiliza las ecuaciones de ejes para determinar cuales pixeles se deben rellenar.

Una ecuación de ejes segmenta una región en tres partes, un limite y dos mitades de espacio. El límite está identificado por puntos donde la ecuación del eje es igual a cero. Las dos mitades están distinguidas por las diferencias en los signos de la ecuación de los ejes.

Características Calcular las ecuaciones de ejes desde los vértices. Calcular una limite alrededor de la figura. Hacer un seguimiento de los pixeles dentro del limite

de la figura evaluando las ecuaciones de los ejes. Cuando todos dan un valor positivo se dibuja el pixel.

Page 62: Introducción a la Graficación por Computadora Unidad II

Implementación Antes de empezar es necesario definir unos

cuantos objetos útiles.public class Vertex2D {

public float x, y; // coordenada oe vertex

public int argb; // color de vertex

public Vertex2D(float xval, float yval, int cval)

{

x = xval;

y = yval;

argb = cval;

}

}

Page 63: Introducción a la Graficación por Computadora Unidad II

El objeto EdgeEqnclass EdgeEqn {

public final static int FRACBITS = 12;

public int A, B, C;

public int flag;

public EdgeEqn(Vertex2D v0, Vertex2D v1){

double a = v0.y - v1.y;

double b = v1.x - v0.x;

double c = -0.5f*(a*(v0.x + v1.x) + b*(v0.y + v1.y));

A = (int) (a * (1<&ltFRACBITS));

B = (int) (b * (1<&ltFRACBITS));

C = (int) (c * (1<&ltFRACBITS));

flag = 0;

if (A >= 0) flag += 8;

if (B >= 0) flag += 1;

}

public void flip(){

A = -A;

B = -B;

C = -C;

}

public int evaluate(int x, int y){

return (A*x + B*y + C);

}

}

Page 64: Introducción a la Graficación por Computadora Unidad II