La descarga está en progreso. Por favor, espere

La descarga está en progreso. Por favor, espere

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

Presentaciones similares


Presentación del tema: "Introducción a la Graficación por Computadora Unidad II."— Transcripción de la presentación:

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

2 Transformaciones

3 Fundamentos de programación Gráfica Java y la programación orientada a objetos

4 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

5 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

6 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);

7 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!

8 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 */

9 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 al inicio. Comentarios de documentación: /** * Epecifica el método interfaz … * java.graf Genaro Mendez 1.0 */ public abstract interface ClaseI { public final static int KARMA = Math.PI + Math.E; }

10 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 = f; // se necesita forzosamente especificar f short classes = 0xbad; // hexadecimal

11 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];

12 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 = f; // 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();

13 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 * / %+ - > >>> = == !=& ^| &&|| ?:= *= /= %= += -= >= >>>= &= ^= |=

14 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 continue return Expression Label: Statement switch (IntegerExpression) { case IntegerValue: Statements default: Statements }

15 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: Demo Applet Demo Applet Mi clase favorita es Graficación

16 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; }

17 Graficación en Java El proceso de rasterización

18 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.

19 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

20 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.

21 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

22 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]; }

23 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) { }

24 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)); }

25 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; }

26 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; }

27 Para probar el programa Código HTML de la página que incrustará el applet. Prueba Applet Raster Prueba Applet Raster

28 Sprites Sprites, PixBlt

29 Sprites Los sprites son objetos gráficos que se mueven sobre la imagen de fondo denominada escenario o playfield.

30 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.

31 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.

32 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( ); }

33 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.

34 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.

35 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); }

36 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?

37 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()); }

38 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

39 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); }

40 Algoritmos de dibujo de líneas Optimización, rapidez y exactitud

41 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 BresenhamJack Bresenham

42 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.

43 Linea simple Basado en el algoritmo simple del algebra. y = mx + b Ver demostraciónVer 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); }

44 Optimizando lineaSimple ver demostración 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); }

45 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 y ) Valores futuros de y pueden ser expresados en términos de sus valores anteriores con una ecuación de diferencias. y i+1 = y i + m o y i+1 = y i - m

46 Algoritmo modificado ver demostración 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)) { // 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); }

47 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

48 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.

49 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) y i+1 = y i + 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; }

50 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)

51 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)

52 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

53 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.

54 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; }

55 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 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.

56 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)

57 Dibujo de polígonos El triángulo

58 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

59 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.

60 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.

61 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.

62 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; }

63 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<<FRACBITS)); B = (int) (b * (1<<FRACBITS)); C = (int) (c * (1<<FRACBITS)); 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); }

64


Descargar ppt "Introducción a la Graficación por Computadora Unidad II."

Presentaciones similares


Anuncios Google