Transcript Unidad II

Introducción a la Graficación por
Computadora
Unidad II
Transformaciones
Fundamentos de programación
Gráfica
Java y la programación orientada a objetos
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
Ejemplo

Diseño Orientado a Objetos

Similar a las clases de objetos

Interfaces comunes

Utilización común



Diseño Procedural

Centrado en los algoritmos – forza a
las implementaciones tempranas y
algoritmos de decisión
Reutilización de código – herencia

Expone mayor detalle
Aplaza la implementación y algoritmos de
decisión.

Dificulta la extensión

Dificulta el mantenimiento
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
Point2D v2
Point2D v3
Triangle t
=
=
=
=
new
new
new
new
Point2D(x1, y1);
Point2D(x2, y2);
Point2D(x3, y3);
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);
Puntos específicos de Java

Un programa fuente de java está compuesto de:


Los espacios en blanco son una parte importante de cualquier programa en
Java






Espacios en blanco, comentarios, declaraciones y sentencias.
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!
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
*/
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;
}
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;
especificar “f”
// se necesita forzosamente
short classes = 0xbad;
// hexadecimal
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];
int triangle[][] = new int[3][];
triangle[0] = new int[3];
triangle[1] = new int[2];
triangle[2] = new int[1];
// referencias a arreglos
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();
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
*/%
+<< >> >>>
< > <= >=
== !=
&
^
|
&&
||
?:
= *= /= %= += -= <<= >>= >>>= &= ^= |=
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
}
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>
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;
}
}
Graficación en Java
El proceso de rasterización
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.
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
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.
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.*;
Los pixeles se almacenan como un arreglo
de int denominado pixel[]
class Raster {
•Cada pixel consta de 32 bits
public int width, height;
public int pixel[];
:
:
•Cuatro bytes uno para cada color primario y
un valor Alpha
•Cuando Alpha es 0 es transparente
•Cuando Alpha es 255 es opaco
:
Descarga el código fuente de la clase Raster (Raster.java)
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];
}
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
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));
}
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;
}
Ejemplo de uso Rastest.java
import 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;
}
}
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>
Sprites
Sprites, PixBlt
Sprites

Los sprites son objetos gráficos que se mueven sobre la
imagen de fondo denominada escenario o “playfield”.
Un Sprite es un Raster
class 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.
Un Sprite animado es un sprite
class 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.
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( );
}
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.
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.
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);
}
}
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?
Por suerte, ya contamos con algo

En nuestra clase Raster
public 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());
}
}
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
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);
}
}
}
Algoritmos de dibujo de líneas
Optimización, rapidez y exactitud
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
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.
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);
}
}
}
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);
}
}
}
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

Algoritmo modificado

ver demostración
Este método de dibujo de línea se denomina “Digital Differential Analyzer” o DDA
public 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)) {
float m = (float) dy / (float) dx;
// inclinacion < 1
// 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 {
float m = (float) dx / (float) dy;
// inclinación >= 1
// 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);
}
}
}
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
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.
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; }
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)
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)
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
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.
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;
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;
}
}
}
raster.pixel[x0+y0] = pix;
Mas algoritmos
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.

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
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)
Dibujo de polígonos
El triángulo
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.
Convexo
No-Convexo
Dibujo de triángulos

Los triángulos son matemáticamente muy simples.


Las formas arbitrarias pueden aproximarse con
triángulos.


La matemática relacionada con los triángulos involucra
solamente ecuaciones de primer grado.
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.
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.
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.
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;
}
}
El objeto EdgeEqn
class 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);
}
}