• No se han encontrado resultados

UNIDAD 3 TRABAJANDO CON UN API GRÁFICA... 3 CAPÍTULO 7 Fundamentos del API 2D de JAVA... 6

N/A
N/A
Protected

Academic year: 2021

Share "UNIDAD 3 TRABAJANDO CON UN API GRÁFICA... 3 CAPÍTULO 7 Fundamentos del API 2D de JAVA... 6"

Copied!
101
0
0

Texto completo

(1)

UNIDAD 3 TRABAJANDO CON UN API GRÁFICA ... 3

CAPÍTULO 7 Fundamentos del API 2D de JAVA ... 6

Lección 31 Características generales del API 2D de Java ... 6

Lección 32 Ubicación espacial, textos e imágenes ... 10

Lección 33 Rellenos, Filetes y Composiciones ... 15

Lección 34 Los paquetes del API 2D de Java ... 17

Lección 35 La clase Graphics ... 20

CAPÍTULO 8 API 3D de JAVA ... 24

Lección 36 Figuras básicas en Java 2D (Shape) ... 24

Lección 37 Áreas ... 36

Lección 38 Texto y Fuentes ... 40

Lección 39 Imágenes ... 47

Lección 40 Técnica de Doble Búfer ... 54

CAPÍTULO 9 API 3D de JAVA ... 61

Lección 41 Lo básico de Java 3D ... 62

Lección 42 Empezar con Java 3D ... 62

Lección 43 Un Ejemplo de la aplicación de la receta ... 76

Lección 44 Rotación de objetos ... 82

(2)

ASPECTOS DE PROPIEDAD INTELECTUAL Y VERSIONAMIENTO

El contenido didáctico del curso académico Computación Gráfica fue diseñado inicialmente en el año 2005 por Adriana rocío Lizcano Dallos, docente de la UNAD, Bogotá, ingeniera de sistemas, en su desempeño como tutora.

Se han realizado las siguientes actualizaciones al contenido didáctico:  Adecuación del diseño según plantilla material didáctico UNAD.

 Distribución de unidades, capítulos y lecciones según el estándar CORE.  Adición del capítulo 4 Requerimientos de software.

 Actualización de herramientas para el desarrollo, codificación y librerías.

Estos cambios fueron realizados por Oscar Javier Abaunza García, docente de la UNAD, CEAD Bucaramanga, ingeniero de sistemas, especialista en educación superior a distancia, en su desempeño como tutor y director del curso a nivel nacional.

(3)

UNIDAD 3 TRABAJANDO CON UN API GRÁFICA

Introducción

En los capítulos anteriores nos hemos acercado al mundo de la computación gráfica conociendo sus bases conceptuales y haciendo pequeños programas que permiten dibujar en la pantalla a partir de la unidad básica de dibujo: el píxel. En esta Unidad deseamos introducir el manejo de una API gráfico. Como se mencionaba en la primera unidad las API gráficas proporcionan un conjunto de primitivas independientes del hardware que le facilitan el proceso de dibujo computacional, algunas de ellas, como OpenGL son muy utilizadas por dos razones: su potencial y la libre distribución.

El lenguaje de programación Java proporciona como parte de su conjunto de clases fundamentales para la creación de interfaces gráficas de usuario, un conjunto de clases denominado Java 2D.

El API 2D de Java introducido en el JDK 1.2 proporciona gráficos avanzados en dos dimensiones, texto, y capacidades de manejo de imágenes para los programas Java a través de la extensión del AWT. Este paquete de dibujo soporta líneas artísticas, texto e imágenes en un marco de trabajo flexible y lleno de potencia para desarrollar interfaces de usuario, programas de dibujo sofisticados y editores de imágenes.

El API 2D de Java proporciona:

 Un modelo de renderizado uniforme para pantallas e impresoras.

 Un amplio conjunto de gráficos primitivos geométricos, como curvas, rectángulos, y elipses y un mecanismo para renderizar virtualmente cualquier forma geométrica.

 Mecanismos para detectar esquinas de formas, texto e imágenes.

 Un modelo de composición que proporciona control sobre cómo se renderizan los objetos solapados.

 Soporte de color mejorado que facilita su manejo.  Soporte para imprimir documentos complejos.

Durante el primer capítulo de esta Unidad didáctica se cubrirán los tópicos correspondientes a la graficación en 2D mediante el uso del API 2D de Java, a partir de la recopilación de dos documentos de internet que son magníficos puntos de referencia: El tutorial sobre gráficos en Java de Agustín Froufe y el curso de Java en Castellano traducido por Juan Antonio Palos (ozito).

(4)

En cuanto al dibujo (o renderizado) en 3D, El API Java 3D es un interface para escribir programas que muestran e interactúan con gráficos tridimensionales. Java 3D es una extensión estándar del JDK 2 de Java. El API Java 3D proporciona una colección de constructores de alto-nivel para crear y manipular geometrías 3D y estructuras para dibujar esta geometría. Java 3D proporciona las funciones para creación de imágenes, visualizaciones, animaciones y programas de aplicaciones gráficas 3D interactivas. En el segundo capítulo de esta Unidad se proporcionan los elementos fundamentales para asumir el estudio del API Java 3D, proporcionando algunos ejemplos básicos. Para el desarrollo de esta parte de la Unidad se ha recopilado nuevamente la información proporcionada por Juan Antonio Palos (ozito).

Esta unidad final del curso de Computación Gráfica quiere acercarlo aún más a la visualización de gráficos computacionales. Aunque se reconoce que la información que se proporciona es bastante limitada, los límites se los impone su creatividad y sus deseos de realizar nuevas cosas. En la sección de Enlaces relacionados podrá encontrar enlaces con una gran cantidad de información adicional, para ayudarlo a hacer volar su imaginación.

La implementación de buena parte de los ejemplos requiere que usted conozca con anticipación los fundamentos básicos de la clase Awt o Swing de Java, si quiere repasarlos, revise la sección de enlaces.

Intencionalidades Formativas

Propósitos

Introducir al estudiante en el conocimiento de los principales algoritmos y estructuras de datos utilizados en Computación Gráfica, especialmente en la generación de gráficos en dos dimensiones.

Proporcionar al estudiante los lineamientos tecnológicos básicos que le permitan utilizar API gráficas en conjunto con lenguajes de programación de alto nivel, para el proceso de modelamiento y construcción de escenarios gráficos en 2D y 3D.

Objetivos

Programar ejercicios de llenado de áreas y transformaciones de figuras en dos dimensiones, utilizando librerías gráficas y lenguajes de programación, partiendo de la conceptualización geométrica respectiva.

(5)

Emplear los fundamentos necesarios para la construcción de aplicaciones que presenten objetos gráficos en dos dimensiones, mediante la funcionalidad que proporciona el paradigma orientado a objetos y el API Java 2D.

Identificar algunas de las potencialidades para el dibujo y animación de escenarios gráficos en tres dimensiones, a partir de la programación de ejemplos básicos que utilizan el API Java 3D.

Competencias

El estudiante programa y emplea algoritmos que permiten dibujar las principales figuras geométricas en dos dimensiones.

El estudiante conoce los fundamentos matemáticos de la representación de gráficos computacionales en dos dimensiones y utiliza algoritmos que las realizan.

El estudiante utiliza las principales clases proporcionadas por el API 2D y 3D de Java, en la programación de ejercicios que exigen la representación de gráficos en dos y tres dimensiones.

Metas

Al finalizar esta unidad didáctica el estudiante estará en capacidad de:  Usar API gráficas para la construcción de gráficos computacionales.  Utilizar y construir algoritmos para dibujo de gráficos en 2D y 3D.

 Describir el funcionamiento matemático y geométrico que permite el dibujo de gráficos en el computador.

(6)

Productos de Aprendizaje

Individual

 Lectura autorregulada de la Unidad Didáctica realizando fichas textuales y mapas conceptuales para archivar en el portafolio.

 Consultar en internet sobre los temas de la unidad y publicar preguntas en el foro de la unidad correspondiente para que sean solucionadas por el tutor o los compañeros.

 Sustentación individual de los programas desarrollados en JAVA. Pequeño Grupo Colaborativo

Desarrollo de un proyecto de programación utilizando texturas e iluminación con el API 3D. Los enunciados serán entregados por el tutor indicando las fechas y condiciones de entrega.

Grupo de Curso

 Por cada tipo de algoritmo que se estudia se realizará una práctica en computadores, para repasar los conceptos básicos y probar los algoritmos codificados en JAVA.

 Socialización de los productos individuales y en pequeño grupo colaborativo.  Consolidación de conceptos, glosario técnico y mapa conceptual de la

unidad.

CAPÍTULO 7 Fundamentos del API 2D de JAVA

Lección 31 Características generales del API 2D de Java

31.1 Características generales del API 2D de Java

A continuación se presentan algunas de las características del API 2D de Java, a partir de la documentación proporcionada por Sun Microsystems para el J2SE. El API 2D de Java mejora las capacidades de gráficos, texto e imágenes de la Abstract Windowing Toolkit (AWT), haciendo posible el desarrollo de interfaces de usuario mejoradas y nuevos tipos de aplicaciones Java. Además de sus mejoras en gráficos, letra e imágenes, el API 2D de Java soporta mejoras para la definición y composición del color, además de la detección de formas y texto en formas

(7)

geométricas arbitrarias y un modelo de dibujado (rendering) para impresoras y dispositivos de visualización.

El API 2D de Java también hace posible la creación de librerías gráficas avanzadas, tales como librerías de CAD-CAM y de gráficos o librerías de efectos especiales para imágenes, así como la creación de imágenes y de filtros para archivos gráficos.

Cuando se usa en conjunto con al Java Media Framework y otras APIs de Java Media, el API 2D de Java puede utilizarse para crear y visualizar animaciones y otras presentaciones multimedia. Los APIs de Java Animation y Java Media Framework le proporcionan al API 2D de Java el soporte para el renderizado.

31.1.1 Mejoras de gráficos, texto e imágenes

Las últimas versionas del AWT proporcionaron un paquete simple para construir páginas comunes HTML, pero no contemplaban características completas suficientes para dibujar gráficos complejos, texto e imágenes. Como un paquete de dibujo simplificado, el AWT encapsuló casos específicos de conceptos de renderizado más general. El API 2D de Java provee una paquete de renderizado más flexible y con amplias características que expanden el AWT para soportar gráficos generales y operaciones de renderizado.

A través de la clase Graphics, es posible dibujar rectángulos, elipses y polígonos. El Graphics2D incorpora el concepto de renderizado geométrico proporcionando un mecanismo para dibujar virtualmente cualquier forma geométrica. Igualmente, con el API Java 2D es posible dibujar líneas con estilos, de cualquier ancho y formas geométricas rellenas con virtualmente cualquier textura.

Las formas geométricas se proporcionan a través de implementaciones de la interfaz Shape, por ejemplo las figuras Rectangle2D y Ellipse2D. Las curvas y los arcos también son implementaciones específicas de la interfaz Shape.

Los rellenos y estilos de lápiz (denominado Filete en múltiples aplicaciones de dibujo) se proporcionan a través de implementaciones de las interfaces Paint y Stroke, por ejemplo: BasicStroke, GradientPaint, TexturePaint y Color.

La implementación AffineTransform define transformaciones lineales de coordenadas 2D, incluyendo el escalado, rotación, traslación y recortado.

(8)

Las regiones de clipping se definen por las mismas implementaciones de la interface Shape que se usan para definir regiones generales de clipping, por ejemplo Rectangle2D y GeneralPath.

Las composiciones de color se proporcionan por implementaciones de la interfaz Composite, por ejemplo AlphaComposite. Un objeto Font se define como una colección de Glyphs, que a su vez está definida por Shapes individuales.

31.1.2 Modelo de Renderizado

El mecanismo de renderizado básico es el mismo que en las versiones anteriores del JDK. El sistema de dibujo o renderizado controla cuándo y cómo dibuja un programa. Cuando un componente necesita ser mostrado, se llama automáticamente a su método paint o update dentro del contexto Graphics apropiado.

Como ya se mencionó, el API 2D de Java presenta java.awt.Graphics2D, un nuevo tipo de objeto Graphics. Graphics2D desciende de la clase Graphics para roporcionar acceso a las características avanzadas de renderizado del API 2D de Java.

Para usar las características del API 2D de Java, tenemos que forzar el objeto Graphics pasado al método de dibujo de un componente a un objeto Graphics2D. Como se muestra en el siguiente código:

public void Paint (Graphics g) { Graphics2D g2 = (Graphics2D) g; ... }

Al conjunto de atributos de estado asociados con un objeto Graphics2D se le conoce como Contexto de Renderizado de Graphics2D. Para mostrar texto, formas o imágenes, podemos configurar este contexto y luego llamar a uno de los métodos de renderizado de la clase Graphics2D, como draw o fill. Cómo muestra la siguiente figura, el contexto de renderizado de Graphics2D contiene varios atributos.

El estilo de lápiz que se aplica al exterior de una forma. Este atributo stroke permite dibujar líneas con cualquier tamaño de punto y patrón de sombreado y aplicar finalizadores y decoraciones a la línea. Algunas aplicaciones de dibujo lo denominan filete.

(9)

El estilo de relleno que se aplica al interior de la forma. Este atributo paint permite rellenar formas con colores sólidos, gradientes o patrones.

El estilo de composición se utiliza cuando los objetos dibujados se solapan con objetos existentes.

La transformación que se aplica durante el dibujado para convertir el objeto dibujado desde el espacio de usuario a las coordenadas de espacio del dispositivo. También se pueden aplicar otras

transformaciones opcionales como la traducción, rotación escalado, recortado, a través de este atributo.

El Clip que restringe el dibujado al área dentro de los bordes de un Shape se utiliza para definir el área de recorte. Se puede usar cualquier forma para definir un clip.

La fuente se usa para convertir cadenas de texto.

Punto de Renderizado que especifican las preferencias en cuanto a velocidad y calidad. Por ejemplo, es posible especificar que se desea usar antialiasing, si está disponible.

Para configurar un atributo en el contexto de renderizado de Graphics2D, se usan los métodos setAttribute.

setStroke

setPaint

setComposite

setTransform

(10)

setFont

setRenderingHints

Cuando se configura un atributo, se el pasa al objeto el atributo apropiado. Por ejemplo, para cambiar el atributo paint a un relleno de gradiente azul-gris, deberíamos construir el objeto GradientPaint y luego llamar a setPaint.

gp = new GradientPaint(0f,0f,blue,0f,30f,green); g2.setPaint(gp);

Graphics2D contiene referencias a sus objetos atributos, no son clonados. Si se modifica un objeto atributo que forma parte del contexto Graphics2D, es necesario llamar al método set para notificarlo al contexto. La modificación de un atributo de un objeto durante el renderizado puede causar comportamientos impredecibles.

31.1.3 Métodos de renderizado de Graphics2D

Graphics2D proporciona los siguientes métodos generales de dibujado que pueden usarse para dibujar cualquier primitivo geométrico, texto o imagen.

draw Dibuja el exterior de una forma geométrica primitiva usando los

atributos stroke y paint.

fill Dibuja cualquier forma geométrica primitiva rellenado su interior

con el color o patrón especificado por el atributo paint.

drawString Dibuja cualquier cadena de texto. El atributo font se usa para

convertir la fuente a Glyphs que luego se rellenan con el color o patrón especificados por el atributo paint.

drawImage Dibuja la imagen especificada.

Además, Graphics2D soporta los métodos de renderizado de Graphics para formas particulares, como drawOval y fillRect.

Lección 32 Ubicación espacial, textos e imágenes

32.1 Sistema de coordenadas

(11)

 El espacio de usuario es un sistema de coordenadas lógicas independiente del dispositivo. Las aplicaciones usan este sistema de coordenadas exclusivamente, y este es el espacio en el que se especifican los gráficos primitivos del Java 2D.

 El espacio de dispositivo es el sistema de coordenadas para un dispositivo específico de salida, como una pantalla, una ventana o una impresora. En un ambiente multi-ventana con un escritorio virtual donde una ventana puede expandirse más allá de la pantalla del dispositivo físico, este escritorio virtual se adapta a todas las pantallas.

Aunque el sistema de coordenadas para una ventana o una pantalla podría ser muy distinto que para una impresora, estas diferencias son invisibles para los programas Java. Las conversiones necesarias entre el espacio de usuario y el espacio de dispositivo se realizan automáticamente durante el dibujado.

Espacio de usuario

Como se muestra en la Figura 55, el origen del espacio de usuario se localiza en la esquina superior izquierda del espacio, con los valores de x incrementando a la derecha y los valores de y incrementando hacia abajo.

Figura 55 Espacio de usuario

El espacio del usuario representa una abstracción uniforme de todas los posibles sistemas de coordenadas de dispositivos. El espacio de dispositivo para un dispositivo particular podría tener el mismo origen y dirección del espacio del usuario, o podrían ser diferentes. Sin embargo, las coordenadas del espacio del usuario son automáticamente transformadas en las apropiadas para el espacio del

(12)

dispositivo cuando se dibuja un objeto gráfico. Frecuentemente, la plataforma subyacente o driver del dispositivo se utilizan para desarrollar esta conversión.

Espacio de dispositivo

El API 2D de Java define tres niveles de información de configuración que se mantienen para permitir la conversión desde un espacio de usuario a un espacio de dispositivo. Esta información está encapsulada en tres clases:

 GraphicsEnvironment  GraphicsDevice

 GraphicsConfiguration

Entre ellas, representan toda la información necesaria para localizar un dispositivo de renderizado o fuente en la plataforma Java y para convertir las coordenadas del espacio de usuario al espacio de dispositivo. Una aplicación puede tener acceso a esta información, pero no necesita desarrollar modificaciones en ellas.

El GraphicsEnvironment describe la colección de dispositivos de renderizado disponible para una aplicación Java en un plataforma particular. Los dispositivos de renderizado incluyen pantallas, impresoras y búferes de imagen. El GraphicsEnvironment también incluye una lista de todas las fuentes disponibles en una plataforma.

El GraphicsDevice describe un dispositivo de renderizado visible para la aplicación, tal como una pantalla o impresora. Cada configuración posible de el dispositivo se representa por una GraphicsConfiguration. Por ejemplo, un dispositivo de visualización SVGA puede operara en varios modos: 640*480*16 colores, 640*480*256 colores y 800*600*256 colores. La pantalla SVGA está representada por un objeto GraphicsDevice y cada uno de los modos es representado por un objeto GraphicsConfiguration.

Un GraphicsEnvironment puede contener uno o más GraphicsDevice, a su vez, cada GraphicsDevice puede tener una o más GraphicsConfiguration.

Transformaciones

El API Java 2D ha unificado su modelo de transformación de coordenadas. Todas las transformaciones de coordenadas, incluyendo transformaciones desde el espacio del usuario al espacio del dispositivo, son representadas por objetos de la

(13)

clase AffineTransform, que define las reglas para manipular coordenadas usando matrices.

Es posible adicionar un AffineTransform al contexto gráfico para rotar, escalar, trasladar o recortar una figura geométrica, texto o imagen cuando se esta renderizando. La transformación adicional se aplica a cualquier objeto gráfico renderizado en ese contexto. La transformación se realiza cuando el espacio de coordenadas de usuario se convierte en espacio de coordenadas del dispositivo.

32.2 Fuentes

Una cadena se asume de manera normal, en términos de los caracteres que la conforman. Cuando se dibuja una cadena, su apariencia está determinada por la letra o fuente que está seleccionada. Sin embargo, las figuras que la fuente usa para mostrar la cadena no siempre corresponden con caracteres individuales, por ejemplo, en publicidad profesional, ciertas combinaciones de dos o más caracteres se reemplazan a menudo por una figura simple denominada ligature.

Las figuras que una fuente usa para representar los caracteres en las cadenas se denominan Glyphs. Una fuente puede representar un carácter como una a con tílde usando varios glyphs, o representar ciertas combinaciones de caracteres como la fi de final con un único glyph. En el API Java 2D, una glyph es simplemente un Shape que puede ser manipulado y dibujado en la misma forma que cualquier otro objeto Shape.

Una fuente puede ser entendida como una colección glyphs. Una única fuente puede tener muchas versiones, tales como heavy, médium, oblique, ghotic y regular. Estas diferentes versiones son llamadas caras (faces). Todas las caras de una fuente tienen un diseño tipográfico similar y pueden ser reconocidas como miembros de una misma familia. En otras palabras, una colección de glyphs con una forma particular de estilo conforma una font face, una colección de formas de font faces forman una font family, y una colección de font families conforma el grupo de fuentes disponible en un GraphicsEnvironment particular.

En el API Java 2D, las fuentes se especifican por un nombre que describe una particular font face (por ejemplo: Helvetica Bold) Es diferente a como se asume en el JDK 1.1, en las que las fuentes eran descritas por nombres lógicos que tomaban la forma de diferentes font face dependiendo de las fuentes disponibles en la plataforma particular. Para lograr compatibilidad el API Java 2D soporta la especificación de fuentes por su nombre lógico y también por su nombre de font face.

(14)

Usando el API 2D de Java es posible componer y dibujar cadenas que contienen múltiples fuentes, de diferentes familias, caras, tamaños e incluso lenguajes. La apariencia del texto está separada lógicamente de las características del texto. Los objetos Font se utilizan para describir la apariencia, y la información de características del texto se almacena en objetos TextLayout y TextAttibuteSet. Esta posibilidad hace más fácil de usar la misma fuente en texto con diferentes características.

32.3 Imágenes

Las imágenes son colecciones de píxeles organizados espacialmente. Un píxel define la apariencia de una imagen en una ubicación simple. Un arreglo bidimensional de píxeles se denomina una ráster.

La apariencia del píxel puede definirse directamente o como un índice en una tabla de color para una imagen. En imágenes que contienen muchos colores (más de 256), los píxeles usualmente representan directamente el color, alpha y otras características de cada localización de la pantalla. Tales imágenes tienden a ser muchos más grandes que las imágenes de color indexado (indexed-color

images), pero ellas son más realistas.

En una imagen de color indexado, los colores en la imagen están limitados a los colores especificados en una tabla de colores, a menudo, el resultado es que sólo es posible usar unos pocos colores en la imagen. Sin embargo, un índice requiere menos almacenamiento que un valor de color, por tanto el resultado es que las imágenes de colores indexados son más pequeñas. El formato de píxel es popular para las imágenes que contienen sólo 16 o 256 colores.

Las imágenes en el API 2D de java tiene dos componentes primarios:  Los datos de la imagen original (los píxeles)

 La información necesaria para interpretar los píxeles.

Las reglas para interpretar los píxeles están encapsuladas en un objeto

ColorModel (por ejemplo, los valores podrían ser interpretados de dos formas,

como colores directos o indexados). Para que un píxel pueda ser mostrado, debe estar relacionado con un modelo de color.

Una banda o canal (band) es un componente del espacio de color de una imagen. Por ejemplo, el Rojo (Red), Verde (Green)y Azul (Blue) son las bandas o canales de una imagen RGB. Un píxel en una imagen de modelo de color directo puede

(15)

tomarse de una colección de valores de las bandas para una localización en la pantalla.

El paquete java.awt.image contiene la implementación de varios ColorModel, incluyendo representaciones de pixeles empaquetados o comprimidos y de componente.

Una objeto ColorSpace encapsula las reglas que gobiernan la forma como un conjunto de valores numéricos corresponden a un color particular. La implementación del ColorSpace en el java.awt.color representa los espacios de color más popular, incluyendo RGB y escala de grises. Es importante aclarar que un espacio de color no es una colección de colores, el define las reglas como deberán ser interpretados los valores de colores individuales.

Al separar el espacio de color (ColorSpace) del modelo de color (ColorModel) se proporciona mayor flexibilidad para representar y convertir de una representación de color a otra.

Lección 33 Rellenos, Filetes y Composiciones

33.1 Rellenos y Filetes

Como ya se había mencionado, con el API 2D de Java es posible usar diferentes estilos de lápices (filetes) y patrones de relleno. Como el texto está en últimas representado por un conjunto de glyphs, a las cadenas de texto también se les puede aplicar atributos de filete y relleno.

Figura 56 Dibujo con diferentes tipos de filete

Los estilos de lápices están definidos por objetos que implementan la interfaz Stroke. El filete hace posible especificar diferentes anchos y patrones de diseño para líneas y curvas.

Los patrones de rellenos están definidos por objetos que implementan la interfaz

Paint. La clase Color, que está disponible en versiones anteriores de AWT, es un

tipo simple de un objeto Saint usado para definir rellenos de colores sólidos. El API 2D de Java proporciona dos implementaciones adicionales para Paint,

(16)

El TexturePaint define un patrón de relleno utilizando un simple fragmento de imagen que se repite uniformemente. El GradientPaint define un relleno como un gradiente entre dos colores.

Figura 57 Relleno de gradiente y de textura

En Java 2D, el renderizado de la línea exterior y el relleno de una figura son dos operaciones separadas:

Usando el método draw se dibuja el contorno (línea exterior) de la figura usando el estilo de lápiz especificado en el atributo Stroke y el patrón de relleno especificado por el atributo Paint.

Usado el método fill se rellena el interior de la figura con el patrón especificado en el atributo Paint.

Cuando se renderiza una cadena de texto, el atributo actual de Paint se aplica a los glyphs que forman la cadena. Sin embargo, drawString actualmente rellena los glyphs que están siendo renderizados. Para modificar el filete del contorno de los glyphs en una cadena de texto, es necesario enviar el contorno y renderizarlos como figuras usando el método draw.

33.2 Composiciones (composites)

Cuando se renderiza un objeto que se sobrepone con otro objeto existente, es necesario determinar como se deben combinar los colores del nuevo objeto von los colores que ya están ocupando el área donde se está dibujando. El API 2D de Java encapsula reglas para combinar los colores en el objeto Composite.

Los sistemas de renderizado primitivo proporcionan solamente operadores boléanos básicos para combinar los colores. Por ejemplo, una regla de composición booleana puede definir los valores de color de la fuente y el destino a partir de operaciones de And, OR y XOR. Este enfoque tiene varios inconvenientes, como lo poco intuitivo para el ser humano, además que este tipo de composiciones no permite la composición de colores en diferentes espacios de color, además de no considerar el caso de las imágenes de color indexado, ya que el resultado de la

(17)

operación booleana de dos valores de píxel en una imagen es la composición de dos índices, no de dos colores.

El API 2D de Java evita estos inconvenientes al implementar reglas de mezcla alfa (alpha-blending) que tienen en cuenta la información acerca del modelo de color al hacer las composiciones. El objeto alphaComposite incluye el modelo del color de los colores fuente y destino.

Lección 34 Los paquetes del API 2D de Java

Las clases del API Java 2D está organizada en los siguientes paquetes: java.awt java.awt.geom java.awt.font java.awt.color java.awt.image java.awt.image.renderable java.awt.print

El paquete java.awt contiene algunas clases e interfaces del API Java 2D, obviamente no todas las clases del java.awt son clases del Java 2D.

AlphaComposite BasicStroke Color

Composite CompositeContext Font

GradientPaint Graphics2D GraphicsConfiguration

GraphicsDevice GraphicsEnvironment Paint

PaintContext Rectangle Shape

Stroke TexturePaint Transparency

El paquete java.awt.geom contiene clases e interfaces relacionadas con la definición de primitivas geométricas.

(18)

Arc2D.Float Area CubicCurve2D CubicCurve2D.Double CubicCurve2D.Float Dimension2D

Ellipse2D Ellipse2D.Double Ellipse2D.Float

FlatteningPathIterator GeneralPath Line2D

Line2D.Double Line2D.Flota PathIterator

Point2D Point2D.Double Point2D.Float

QuadCurve2D QuadCurve2D.Double QuadCurve2D.Float

Rectangle2D Rectangle2D.Double Rectangle2D.Float

RectangularShape RoundRectangle2D RoundRectangle2D.Double RoundRectangle2D.Float

Muchas de las primitivas geométricas tienen sus correspondientes implementaciones .Float y .Double. Las implementaciones de doble precisión proporcionan mayor precisión de renderizado, pero a expensas del desempeño en algunas plataformas.

El paquete java.awt.font contiene clases e interfaces que se utilizan para proporcionar características al texto y la definición de fuentes.

FontRenderContext GlyphJustificationInfo GlyphMetrics

GlyphVector GraphicAttribute ImageGraphicAttribute

LineBreakMeasurer LineMetrics MultipleMaster

OpenType ShapeGrapicAttribute TextAttribute

TextHitInfo TextLayout TransformAttribute

El paquete java.awt.color contiene clases e interfaces para la definición de espacios de color y perfiles de color.

ColorSpace ICC_ColorSpace ICC_Profile

(19)

Los paquetes java.awt.image y java.awt.image.renderable contienen clases e interfaces para la definición y renderizado de imágenes.

AffineTransformOp BandCombineOp BandedSampleModel

BufferedImage BufferedImageFilter BufferedImageOp

ByteLookupTable ColorConvertOp ColorModel

ComponentColorModel ComponentSampleModel ConvolveOp

ContextualRenderedImageFactory DataBuffer

DataBufferByte DataBufferInt DataBufferShort

DataBufferUShort DirectColorModel IndexColorModel

Kernel LookupOp LookupTable

MultiPixelPackedSampleModel PackedColorModel

ParameterBlock PixelInterleavedSampleModel Ráster

RasterOp RenderableImage RenderableImageOp

RenderContext RenderableImageProducer RenderedImageFactory

RenderedImage RescaleOp SampleModel

ShortLookupTable TileObserver WritableRaster

WritableRenderedImage SinglePixelPackedSampleModel

El API Java 2D mejora las siguientes clases heredadas de la clase image de AWT ColorModel

DirectColorModel IndexColorModel

Estas clases de modelo de color recogen las características del java.awt.image para compatibilidad, y para mantener consistencia, las nuevas clases del modelo de color también están localizadas en el paquete java.awt.image.

(20)

El paquete java.awt.print contiene clases e interfaces que hacen posible la

impresión de todos los objetos gráficos basados en Java 2D, como texto, gráficos e imágenes.

Book Pageable PageFormat

Paper Printable PrinterGraphics

PrinterJob

Antes de comenzar en serio con los componentes gráficos que proporciona el API 2D de java, es necesario, hacer una pequeña revisión de la clase Graphics, superclase de Graphics2D y revisar algunos conceptos básicos acerca del contexto gráfico, que posteriormente permitirán comprender mucho mejor los ejemplos que se presentarán.

Lección 35 La clase Graphics

La clase Graphics es la clase base abstracta que proporciona toda, o al menos la mayoría, de la funcionalidad para poder pintar tanto sobre componentes como sobre imágenes fuera de pantalla.

Un objeto Graphics encapsula la siguiente información que será necesaria a la hora de las operaciones básicas de pintado.

El objeto de tipo Component sobre el que se pinta

Un origen de traslación para coordenadas de pintado y clipping La región actual ocupada por el componente

El color actual

La fuente de caracteres actual

La operación lógica actual para utilizar con pixeles (XOR o Paint) La actual alteración de color XOR

Un objeto Graphics describe un contexto gráfico. Un contexto gráfico define una zona de recorte, una zona a la que va a afectar; cualquier operación gráfica que se realice modificará solamente los pixeles que se encuentren dentro de los límites de la zona de recorte actual y el componente que fue utilizado para crear el objeto Graphics.

(21)

Cuando se pinta o escribe, ese dibujo o escritura se realiza en el color actual, utilizando el modo de dibujo actual y la fuente de caracteres actual.

Hay muchas otras clases, como la clase Rectangle y la clase Polygon, que utilizan como soporte a las operaciones que se pueden realizar con la clase Graphics. Para poder revisar esta clase, quizá una de las mejores formas sea a través de sus múltiples métodos, intentando agruparlos por funcionalidad, que es lo que se ha intentado aquí, aunque si el lector quiere una referencia completa y una descripción de los métodos de esta clase deberá recurrir a la documentación que JavaSoft proporciona sobre el AWT.

Hay que empezar hablando del constructor de la clase Graphics, que no tiene argumentos; aunque Graphics es una clase abstracta, por lo que las aplicaciones no pueden llamar a este constructor directamente. Se puede obtener un objeto de tipo Graphics a partir de otro objeto Graphics llamando al método getGraphics() sobre un componente. También se puede recibir un objeto Graphics como parámetro cuando se van a sobreescribir los métodos paint() o update().

35.1 Métodos generales de la clase Graphics

En esta categoría estarían incluidos los métodos útiles en general, sin una asignación específica de funcionalidad con respecto a acciones determinadas de dibujo. A continuación se enumeran algunos de los métodos considerados generales, para seguir con la descripción y uso de algunos de ellos en aplicaciones de ejemplo.

clearRect( int,int,int,int ), se le pasa un rectángulo y borra la zona con el color que

se haya establecido de fondo para la superficie donde se está pintando.

copyArea( int,int,int,int,int,int ), copia la zona rectangular del componente que se indica en los primeros cuatro parámetros, en otra posición del contexto gráfico desplazada las distancia indicada en los dos últimos parámetros.

create(), crea un nuevo objeto de tipo Graphics que es copia del objeto Graphics que ha invocado al método.

dispose(), elimina el contexto gráfico sobre el cual es invocado y devuelve al sistema todos los recursos que estaba utilizando, incluyendo todos los recursos, no solamente la memoria. Un objeto Graphics no se puede utilizar después de haber llamado a este método; y es importante que se eliminen estos objetos manualmente, bien sea creados directamente desde un componente o a partir de

(22)

otro objeto Graphics, cuando ya no se necesiten, en lugar de esperar a que se finalice la ejecución.

finalice(), elimina el contexto gráfico cuando ya no hay ninguna referencia sobre él.

getColor(), devuelve el color actual fijado para el contexto gráfico.

setColor( Color ), fija el color del contexto gráfico al color que se pasa como parámetro. Todas las operaciones gráfica siguientes que utilicen este contexto gráfico, utilizarán el color que se especifica en este método.

setPaintMode(), fija la forma de pintar del contexto gráfico de modo que se

sustituya lo que había con lo nuevo. Cualquier operación de pintado sobreescribirá lo que hubiese en la zona de destino con el color actual.

setXORMode( Color ), fija la forma de pintar del contexto gráfico a una alternancia

entre en color actual y el color de la zona de destino.

toString(), devuelve un objeto de tipo String representando el valor del objeto Graphics.

translate( int,int ), traslada el origen del contexto gráfico al punto que se pasa en

los dos parámetros en el sistema de coordenadas que se esté utilizando.

35.2 Obtener un contexto gráfico

La verdad es que se han escrito varias veces las palabras contexto gráfico, y no se ha proporcionado al lector una explicación concreta de lo que significan estos términos. Hay varias definiciones, para unos significa que la aplicación ha conseguido la habilidad para pintar o colocar imágenes sobre un componente que tiene la característica de soportar el pintado o visualización de imágenes. Otros autores prefieren decir que cada objeto Graphics representa una determinada superficie de dibujo, luego ese objeto Graphics define un contexto gráfico a través del cual se pueden manipular todas las actividades gráficas sobre esa superficie. Y otros autores indican que un objeto Graphics es la superficie última sobre la que se pueden colocar líneas, figuras y texto, por lo cual puede recibir también el nombre de contexto gráfico al aunar información sobre la zona de dibujo, más la fuente de caracteres, color y cualquier otro factor.

Ahora que ya se sabe lo que es un contexto gráfico, hay que ver cómo se consigue crear uno. Para empezar, esto no puede hacerse instanciando directamente un objeto de tipo Graphics, ya que la clase Graphics es abstracta y no puede ser

(23)

instanciada por el código de la aplicación, así que hay que recurrir a formas indirectas para conseguir el contexto gráfico.

Uno de estos caminos indirectos para obtener un contexto gráfico es invocar el método getGraphics() sobre otro objeto. Sin embargo, este método devuelve un contexto gráfico de una imagen, es decir, que solamente funciona para objetos de tipo Image creados en memoria a través del método createImage(), de la clase Component. Esta es una técnica utilizada normalmente cuando se están usando imágenes que se crean en memoria y luego se transfieren a la pantalla, es decir, se está pintando en el doble buffer.

Hay otros dos caminos para obtener un contexto gráfico y, son sorprendentemente simples, porque se hace automáticamente, y es cuando se sobreescriben los métodos paint() y update(), en los cuales Java pasa como parámetro el contexto gráfico del objeto al que pertenece el método.

Normalmente, el método paint() se sobreescribe cuando se quiere colocar algún tipo de material gráfico sobre la pantalla, y el método update() se sobreescribe en circunstancias especiales, como puede ser el caso de una animación o que se vaya a utilizar doble buffer. Lo normal es pues la presentación de información gráfica colocando el código encargado de ello en el método sobreescrito paint() y luego invocando al método repaint() para indicar al sistema que presente ese material en pantalla; aunque el método paint() también puede ser invocado por causas externas, sin control alguno por parte de la aplicación, como puede ser el redimensionamiento de la ventana en la que se está presentando la información gráfica.

Hay que tener en cuenta que el método repaint() pide al sistema que redibuje el componente tan pronto como sea posible, pero esto lo hará el método update() que se llame a continuación. No hay una relación uno a uno entre las llamadas a repaint() y update(), por lo que es posible que múltiples llamadas a repaint() puedan recogerse en una sola llamada a update().

El método update() es invocado automáticamente cuando se pide repintar un Componente. Si el componente no es ligero (lightweight), la implementación por defecto de update() borra el contexto gráfico rellenando el fondo en el color que se haya asignado como color de fondo, fijando de nuevo el color al color del primer plano y llamando a paint(). Si no se sobreescribe update() para hacer una animación, se verá siempre un parpadeo en el refresco del componente por causa de este borrado del fondo.

(24)

En síntesis, el método paint() es el que ofrece el sistema para poder pintar lo que se quiera sobre un determinado componente. En la clase base Component, este método no hace absolutamente nada. Normalmente, en el caso de applets, se sobreescribe para hacer presentar un rectángulo relleno con el color de fondo. Veamos un primer ejemplo básico de obtención del contexto gráfico y pintado de una cadena de texto en un objeto Frame en la Figura 58. Notará que se utiliza la clase Frame del AWT y no la JFrame del Swing, ya que se quieren asegurar la compatibilidad.

Figura 58 Primer ejemplo de dibujo utilizando Java 2D

Observe que en la línea 36 se está convirtiendo el contexto gráfico original g en un contexto gráfico 2D denominado g2, que es el que finalmente se utiliza para dibujar la cadena.

CAPÍTULO 8 API 3D de JAVA

Lección 36 Figuras básicas en Java 2D (Shape)

Las clases del paquete java.awt.geom definen gráficos primitivos comunes, como puntos, líneas, curvas, arcos, rectángulos y elipses.

Las clases en el paquete java.awt.geom son:

(25)

Area GeneralPath Rectangle2D

CubicCurve2D Line2D RectangularShape

Dimension2D Point2D RoundRectangle2D

Excepto para Point2D y Dimension2D, cada una de las otras clases geométricas implementa el interfaz Shape, que proporciona un conjunto de métodos comunes para describir e inspeccionar objetos geométricos bi-dimensionales.

Con estas clases podemos crear de forma virtual cualquier forma geométrica y dibujarlaa través de Graphics2D llamando al método draw o al método fill.

36.1 Formas Rectangulares

Los primitivos Rectangle2D, RoundRectangle2D, Arc2D, y Ellipse2D descienden de la clase RectangularShape, que define métodos para objetos Shape que pueden ser descritos por una caja rectangular. La geometría de un RectangularShape puede ser extrapolada desde un rectángulo que encierra completamente el exterior de la forma, como se muestra en la siguiente figura.

Figura 59 Formas rectangulares

36.2 GeneralPath

La clase GeneralPath permite crear una curva arbitraria especificando una serie de posiciones a lo largo de los límites de la forma.

Figura 60 Forma GeneralPath

Estas posiciones pueden ser conectadas por segmentos de línea, curvas cuadráticas o curvas cúbicas.

Esta figura puede ser creada con 3 segmentos de línea y una curva cúbica.

(26)

El siguiente ejemplo muestra el uso de algunos de estos objetos gráficos y además de las opciones para relleno y filete. El código original corresponde en un demo proporcionado en el tutorial de Java 2D de la documentación del J2SE.

Figura 61 Ejecución del renderizado de figuras primitivas

El código comentado se muestra a continuación. Como se dará cuenta es bastante extenso, por lo que se recomienda seguirlo cuidadosamente. Una vez lo ha

implementado y comprendido, intente realizar modificaciones en los parámetros de dibujo de las diferentes figuras.

/*

* Este es un ejemplo sobre el dibujo de Shapes, proporcionado por la * documentación de Java en la versión 1.2.

*/ import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import javax.swing.*; /*

(27)

* directa de la clase Frame, se tiene problemas al redimensionar el * Frame, ya que no se redibujan automáticamente las figuras. */

public class ShapesDemo2D extends JApplet { final static int maxCharHeight = 15;

final static int minFontSize = 6;

final static Color bg = Color.white; final static Color fg = Color.black; final static Color red = Color.red; final static Color white = Color.white;

//Definición de los tipos de filete

final static BasicStroke stroke = new BasicStroke(2.0f); final static BasicStroke wideStroke = new BasicStroke(8.0f);

final static float dash1[] = {10.0f};

final static BasicStroke dashed = new BasicStroke(1.0f, BasicStroke.CAP_BUTT,

BasicStroke.JOIN_MITER, 10.0f, dash1, 0.0f);

Dimension totalSize; FontMetrics fontMetrics;

public void init() { //Initialize drawing colors setBackground(bg); setForeground(fg); }

/* Permite redimensionar el tamaño de la letra a partir de un objeto de tipo Fontmetrics * dependiendo del tamaño del contexto gráfico, si se amplía el tamaño de la ventana, la * letra se ampliará, si se disminuye el tamaño del contexto la letra disminuirá

*/

FontMetrics pickFont(Graphics2D g2, String longString,

(28)

int xSpace) {

boolean fontFits = false; Font font = g2.getFont();

FontMetrics fontMetrics = g2.getFontMetrics(); int size = font.getSize();

String name = font.getName(); int style = font.getStyle();

while ( !fontFits ) {

if ( (fontMetrics.getHeight() <= maxCharHeight) && (fontMetrics.stringWidth(longString) <= xSpace) ) { fontFits = true; } else { if ( size <= minFontSize ) { fontFits = true; } else {

g2.setFont(font = new Font(name, style, --size)); fontMetrics = g2.getFontMetrics(); } } } return fontMetrics; }

public void paint(Graphics g) { Graphics2D g2 = (Graphics2D) g;

g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

/* Toma las dimensiones del contexto y lo divide para saber el ancho y alto de * cada una de las celdas de la cuadrícula donde dibujará

(29)

Dimension d = getSize(); int gridWidth = d.width / 6; int gridHeight = d.height / 2;

//Obtiene el tamaño de la letra a partir de la cadena más larga a escribir fontMetrics = pickFont(g2, "GeneralPath con relleno y contorno",

gridWidth);

//Dibuja el cuadrado general que sirve de fondo al applet Color fg3D = Color.lightGray;

g2.setPaint(fg3D);

g2.draw3DRect(0, 0, d.width - 1, d.height - 1, true); g2.draw3DRect(3, 3, d.width - 7, d.height - 7, false); g2.setPaint(fg);

int x = 5; int y = 7;

int rectWidth = gridWidth - 2*x;

int stringY = gridHeight - 3 - fontMetrics.getDescent(); int rectHeight = stringY - fontMetrics.getMaxAscent() - y - 2;

// Dibuja una línea con su respectivo texto inferior

g2.draw(new Line2D.Double(x, y+rectHeight-1, x + rectWidth, y)); g2.drawString("Line2D", x, stringY);

x += gridWidth;

// Asigna un filete, dibuja el rectángulo y debajo su respectivo texto g2.setStroke(stroke);

g2.draw(new Rectangle2D.Double(x+5, y+5, rectWidth-5, rectHeight-5)); g2.drawString("Rectangle 2D", x, stringY);

x += gridWidth;

/* Asigna el filete como punteado, dibuja un rectángulo con * bordes redondeados y su texto inferior

* Los parámetros de creación del objeto correspoden al valor en x e y inicial * x e y final y el radio de la circunferencia que define las puntas redondeadas.

(30)

*/

g2.setStroke(dashed);

g2.draw(new RoundRectangle2D.Double(x+20, y+20, rectWidth-40, rectHeight-40, 40, 40));

g2.drawString("RoundRectangle2D", x, stringY); x += gridWidth;

/* Asigna el filete, dibuja un arco con 7 parámetros de construcción.

* Ellos son: el rectángulo que enmarca el arco tiene esquina superior izquierda en * en x,y,el valor máximo en x, el valor máximo en y. Los siguientes dos parámetros * corresponde a los ángulos de inicio y fin del arco, en este caso 60 y 135 grados. * Finalmente el último parámetro corresponde al tipo de arco, los posibles valores son * OPEN, CHORD, PIE.

* Debajo escribe el texto correspondiente */

g2.setStroke(wideStroke);

g2.draw(new Arc2D.Double(x, y, rectWidth, rectHeight, 60, 135, Arc2D.OPEN)); g2.drawString("Arc2D", x, stringY);

x += gridWidth;

// Asigna el filete, dibuja una elipse y el texto inferior correspondiente g2.setStroke(stroke); g2.draw(new Ellipse2D.Double(x, y, rectWidth, rectHeight));

g2.drawString("Ellipse2D", x, stringY); x += gridWidth;

/* Asigna el filete, crea unos vectores con las coordenadas de los puntos del polígono, * crea el objeto GeneralPath (polygon) trazando lineas entre las coordenadas

* y finalmente cierra el poligono. Dibuja el polígono y el texto inferior * correspondiente.

*/

g2.setStroke(stroke);

int x1Points[] = {x, x+rectWidth, x, x+rectWidth}; int y1Points[] = {y, y+rectHeight, y+rectHeight, y};

GeneralPath polygon = new GeneralPath(GeneralPath.WIND_EVEN_ODD, x1Points.length); polygon.moveTo(x1Points[0], y1Points[0]);

for ( int index = 1; index < x1Points.length; index++ ) { polygon.lineTo(x1Points[index], y1Points[index]);

(31)

};

polygon.closePath(); g2.draw(polygon);

g2.drawString("GeneralPath", x, stringY);

// Modifica los valores de coordenadas x e y para dibujar la segunda fila x = 5;

y += gridHeight; stringY += gridHeight;

/* Realiza los mismo pasos que para el polígono anterior, lo que varía es * que en este caso no se cierra el polígono

*/

int x2Points[] = {x, x+rectWidth, x, x+rectWidth}; int y2Points[] = {y, y+rectHeight, y+rectHeight, y};

GeneralPath polyline = new GeneralPath(GeneralPath.WIND_EVEN_ODD, x2Points.length); polyline.moveTo (x2Points[0], y2Points[0]);

for ( int index = 1; index < x2Points.length; index++ ) { polyline.lineTo(x2Points[index], y2Points[index]); };

g2.draw(polyline);

g2.drawString("GeneralPath (Abierto)", x, stringY); x += gridWidth;

/* Asigna el color de relleno a azul. Rellena un objeto rectángulo y lo dibuja. Nuevamente * asigna el color, en este caso al negro y dibuja el texto inferior correspondiente

*/

g2.setPaint(Color.blue);

g2.fill(new Rectangle2D.Double(x, y, rectWidth, rectHeight)); g2.setPaint(fg);

g2.drawString("Rectangle2D con relleno", x, stringY); x += gridWidth;

/* Define el objeto gradiente para rellenar el rectángulo redondeado, partiendo * del color rojo hasta el color amarillo. Ese objeto GradientPaint se envía como * color de relleno mediante el setPaint. Se dibuja el rectángulo redondeado. * Se restaura el color de dibujo al negro y se dibuja el texto inferior.

(32)

*/

GradientPaint relleno = new GradientPaint(x,y,red,x+rectWidth, y,Color.yellow); g2.setPaint(relleno);

g2.fill(new RoundRectangle2D.Double(x, y, rectWidth, rectHeight, 10, 10));

g2.setPaint(fg);

g2.drawString("RoundRectangle2D con gradiente", x, stringY); x += gridWidth;

// Define el color de relleno en rojo y dibuja el arco. g2.setPaint(red);

g2.fill(new Arc2D.Double(x, y, rectWidth, rectHeight, 90, 135, Arc2D.PIE)); g2.setPaint(fg);

g2.drawString("Arc2D con relleno", x, stringY); x += gridWidth;

// Define un relleno de gradiente de rojo a blanco y dibuja la elipse rellena. relleno = new GradientPaint(x,y,red,x+rectWidth, y,white);

g2.setPaint(relleno);

g2.fill (new Ellipse2D.Double(x, y, rectWidth, rectHeight)); g2.setPaint(fg);

g2.drawString("Ellipse2D con gradiente", x, stringY); x += gridWidth;

/* Define los arreglos de las coordenadas para el polígono. Crea el polígono uniendo * los puntos con líneas. Asigna el color rojo y dibuja el polígono relleno.

* Retorna el valor de la pintura a negro y dibuja el polígono mediante draw, lo * que origina que se sibuje el filete del polígono.

*/

int x3Points[] = {x, x+rectWidth, x, x+rectWidth}; int y3Points[] = {y, y+rectHeight, y+rectHeight, y};

GeneralPath filledPolygon = new GeneralPath(GeneralPath.WIND_EVEN_ODD, x3Points.length);

filledPolygon.moveTo(x3Points[0], y3Points[0]); for ( int index = 1; index < x3Points.length; index++ ) { filledPolygon.lineTo(x3Points[index], y3Points[index]); };

(33)

filledPolygon.closePath(); g2.setPaint(Color.yellow); g2.fill(filledPolygon); g2.setPaint(fg); g2.setStroke(wideStroke); g2.draw(filledPolygon);

g2.drawString("GeneralPath con relleno y contorno", x, stringY); }

//Clase que se ejecuta public static void main(String s[]) {

//Crea el Frame

JFrame f = new JFrame("Demo de Figuras Primitivas"); f.addWindowListener(new WindowAdapter() {

public void windowClosing(WindowEvent e) {System.exit(0);} });

//Crea el applet de acuerdo a la clase ShapesDemo2D JApplet applet = new ShapesDemo2D();

//Agrega el objeto applet al Frame f.getContentPane().add("Center", applet); applet.init(); f.pack(); f.setSize(new Dimension(550,300)); f.show(); } } 36.3 QuadCurve2D y CubicCurve2D

La clase QuadCurve2D permite crear segmentos de curvas cuadráticos. Una curva cuadrática está definida por dos puntos finales y un punto de control.

La clase CubicCurve2D permite crear segmentos de curvas cúbicos. Una curva cúbica está definida por dos puntos finales y dos puntos de control, los segmentos de curvas cúbicos también se conocen como curvas de Bézier. Las siguientes figuras muestran ejemplos de curvas cuadráticas y cúbicas.

(34)

Figura 62 Formas Curvas

El siguiente código crea una curva cuadrática con dos puntos finales y un punto de control. Las posiciones de los puntos se seleccionan con respecto al tamaño de la ventana.

import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import javax.swing.*;

public class EjemploQuad extends JApplet {

public void init() {

setBackground(Color.white); setForeground(Color.black); }

public void paint(Graphics g) { Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Dimension d = getSize(); int w = d.width; int h = d.height;

//Crea el objeto de tipo QuadCurve2D QuadCurve2D.Double quad = new QuadCurve2D.Double();

(35)

//Crea los objetos que definen los puntos de inicio, final y control Point2D.Double inicio, fin, control;

inicio = new Point2D.Double(); fin = new Point2D.Double(); control = new Point2D.Double();

//Le asigna coordenadas a los puntos en x e y a partir del tamaño de la ventana inicio.setLocation(w/2-100, h/2+50);

fin.setLocation(w/2+100, h/2+50);

control.setLocation((int)(inicio.x)+100, (int)(inicio.y)-100);

quad.setCurve(inicio, control, fin); //Construye la curva

//Define color y filete

g2.setPaint(Color.magenta);

g2.setStroke(new BasicStroke(2.0f));

//Dibuja la curva g2.draw(quad);

//Modifica el color para dibujar los puntos como rectángulos g2.setPaint(Color.black);

g2.fill(new Rectangle2D.Double(inicio.x, inicio.y,3,3)); g2.drawString("Inicio", (int) inicio.x+5,(int) inicio.y+5); g2.fill(new Rectangle2D.Double(fin.x, fin.y,3,3)); g2.drawString("Fin",(int) fin.x+5, (int)fin.y+5);

g2.fill(new Rectangle2D.Double(control.x, control.y,3,3)); g2.drawString("Control",(int)control.x+5, (int)control.y+5); }

//Clase que se ejecuta public static void main(String s[]) {

//Crea el Frame

JFrame f = new JFrame("Dibujando un QuadCurve2D"); f.addWindowListener(new WindowAdapter() {

public void windowClosing(WindowEvent e) {System.exit(0);} });

(36)

//Crea el applet de acuerdo a la clase ShapesDemo2D JApplet applet = new EjemploQuad();

//Agrega el objeto applet al Frame f.getContentPane().add("Center", applet); applet.init(); f.pack(); f.setSize(new Dimension(400,300)); f.show(); } }

La ejecución de este código proporciona la siguiente ventana gráfica:

Figura 63 Ejecución del dibujo de una curva cuadrática

Intente implementar una aplicación que dibuje una curva cúbica, dibuje también los puntos de referencia para la curva, mediante rectángulos rellenos. Aplique diferentes tipos de filete, a partir de lo ya visto.

Lección 37 Áreas

Con la clase Area se pueden realizar operaciones boolenas, como uniones, intersecciones y substracciones, sobre dos objetos Shape cualquiera. Esta técnica, permite crear rápidamente objetos Shape complejos sin tener que describir cada línea de segmento o cada curva, este proceso se denomina construir un área geométrica (CAG). Una Area soporta las siguientes operaciones booleanas.

(37)

unión Sustracción

Intersección Or-Exclusivo

(XOR)

En el siguiente ejemplo, tomado del tutorial de gráficos del sitio web de Programación en castellano, se crea un Area que dibuja una pera a partir de objetos elipses y operaciones de unión, sustracción e intersección. La ejecución proporciona la siguiente ventana gráfica.

Figura 64 Formación de una pera a partir de operaciones de área

El código que se requiere para el renderizado de este gráfico se presenta a continuación. import java.awt.*; import java.awt.event.*; import java.awt.font.*; import java.awt.geom.*; import java.applet.*; import javax.swing.*; /*

(38)

* para adición, sustracción e intersección. */

public class Pera extends Applet {

Ellipse2D.Double circle, oval, leaf, stem; Area circ, ov, leaf1, leaf2, st1, st2;

public void init() {

circle = new Ellipse2D.Double(); oval = new Ellipse2D.Double(); leaf = new Ellipse2D.Double(); stem = new Ellipse2D.Double(); circ = new Area(circle);

ov = new Area(oval); leaf1 = new Area(leaf); leaf2 = new Area(leaf); st1 = new Area(stem); st2 = new Area(stem);

setBackground(Color.white); }

public void paint (Graphics g) { Graphics2D g2 = (Graphics2D) g;

//Toma las dimensiones del contexto gráfico, en este caso el Frame Dimension d = getSize(); int w = d.width; int h = d.height; double ew = w/2; double eh = h/2; g2.setColor(Color.green);

/* Crea la primera hoja a partir de la intersección de dos objetos Area, creados * a partir de una elipse

(39)

*/

leaf.setFrame(ew-16, eh-29, 15.0, 15.0); leaf1 = new Area(leaf);

leaf.setFrame(ew-14, eh-47, 30.0, 30.0); leaf2 = new Area(leaf);

leaf1.intersect(leaf2); g2.fill(leaf1);

// Crea la segund hoja.

leaf.setFrame(ew+1, eh-29, 15.0, 15.0); leaf1 = new Area(leaf);

leaf2.intersect(leaf1); g2.fill(leaf2);

g2.setColor(Color.black);

/* Crea el pedazo de tronco a partir del llenado del Area resultante de la sustracción de * dos objetos Area creados a partir de una elipse.

*/ stem.setFrame(ew, eh-42, 40.0, 40.0); st1 = new Area(stem); stem.setFrame(ew+3, eh-47, 50.0, 50.0); st2 = new Area(stem); st1.subtract(st2); g2.fill(st1); g2.setColor(Color.yellow);

/* Crea el cuerpo de la pera llenado el Area resultante de la unión de dos objetos * Area, creados de una elipse un circulo.

*/

circle.setFrame(ew-25, eh, 50.0, 50.0); oval.setFrame(ew-19, eh-20, 40.0, 70.0); circ = new Area(circle);

ov = new Area(oval); circ.add(ov);

(40)

}

public static void main(String s[]) { JFrame f = new JFrame("Pera");

f.addWindowListener(new WindowAdapter() {

public void windowClosing(WindowEvent e) {System.exit(0);} });

Applet applet = new Pera();

f.getContentPane().add("Center", applet); applet.init(); f.pack(); f.setSize(new Dimension(150,200)); f.show(); } }

Lección 38 Texto y Fuentes

Es posible mostrar una cadena de texto con cualquier tipo de letra disponible en el sistema, en cualquier tamaño y en el estilo que se seleccione. Para determinar las fuentes que están disponibles en el sistema es necesario llamar el método GrpahicsEnvironment.getAvailableFontFamilyNames. Este método retorna un arreglo de cadenas que contiene los nombres de las familias de las fuentes disponibles, cualquiera de estas cadenas, además del tamaño y el estilo, pueden ser utilizados como argumentos para crear un nuevo objeto Font.

Figura 65 Ejemplo de dibujo de fuentes

El siguiente ejemplo, muestra una aplicación donde es posible seleccionar el tipo de fuente, tamaño y estilo a partir de 3 objetos combo y observar las modificaciones en la cadena dibujada. El ejemplo es tomado del Tutorial de Java 2D de Sun Microsystems. La ejecución del programa genera una pantalla como la que se muestra al lado.

(41)

Para construir el ejemplo, es necesario utilizar el siguiente código que permite obtener los datos de las fuentes instaladas en el sistema y guardarlos en un objeto Vector, que posteriormente puede pasarse a un combo que despliegue los nombres de las fuentes:

GraphicsEnvironment gEnv = GraphicsEnvironment.getLocalGraphicsEnvironment(); String envcmbFuente[] = gEnv.getAvailableFontFamilyNames();

Vector vector = new Vector();

for ( int i = 1; i < envcmbFuente.length; i++ ) { vector.addElement(envcmbFuente[i]); }

cmbFuente = new JComboBox( vector ); cmbFuente.setMaximumRowCount( 9 ); cmbFuente.addItemListener(this); nuevafuente = envcmbFuente[0]; pnlLetra.add(cmbFuente);

A continuación se crea un objeto Font con un estilo Font.PLAIN y un tamaño de 10. Los otros estilos disponibles son ITALIC, BOLD y BOLD+ITALIC.

Font thisFont; ...

thisFont = new Font("Arial", Font.PLAIN, 10);

Posteriormente es posible crear un nuevo objeto Font a partir de un nombre de fuente, estilo y tamaño, que pueden seleccionarse de distintos combo. Al seleccionarse el tamaño el valor de item es de tipo cadena, por lo que se debe convertir a entero para poder crear la nueva fuente.

public void cambiarFuente(String nf, int nest, String ntam){ Integer nuevoTam = new Integer(ntam);

int tam = nuevoTam.intValue(); thisFont = new Font(nf, nest, tam); repaint();

} `

(42)

Para controlar la fuente que se utiliza para dibujar el texto, es necesario enviar los atributos de la fuente al contexto Graphics2D antes de renderizar. Los atributos de la fuente se envían pasando un objeto Font al método setFont. En este ejemplo, los atributos son envidos al construir el nuevo objeto Font y la cadena se dibuja en el centro del componente usando esta fuente. Cada vez que se modifiquen los atributos, se construye un nuevo objeto Font y se envía al contexto Graphics 2D en el metodo Paint() para que sean redibujados. El método getFontMetrics permite medir la longitud en píxeles de la cadena considerando los nuevos atributos, de manera que siempre se dibuje en el centro del componente.

g2.setFont(thisFont);

String cadena = "Seleccione una fuente, tamaño y estilo para modificarme"; FontMetrics medida = g2.getFontMetrics();

int ancho = medida.stringWidth( cadena ); int alto = medida.getHeight();

//Dibuja la cadena en el centro del panel correspondiente g2.drawString( cadena, w/2-ancho/2, h/2-alto/2 ); }

El código completo del ejemplo, comentado se muestra a continuación: /*

* Ejemplo de selección de fuentes. Construido para el Tutorial * de Java2D de Sun Microsystems.

*/ import java.lang.Integer; import java.awt.*; import java.awt.font.*; import java.awt.geom.*; import java.awt.event.*; import javax.swing.*; import java.util.Vector;

public class FontSelection extends JApplet implements ItemListener { JLabel lblFuente, lblTamano, lblEstilo;

pnlLetra fontC;

JComboBox cmbFuente, cmbTamano, cmbEstilo; int i = 0;

(43)

String nuevafuente = "Fuente sin seleccionar"; int nuevoestilo = 0;

String nuevotam = "10";

public void init() {

getContentPane().setLayout( new BorderLayout() );

JPanel pnlSuperior = new JPanel(); JPanel pnlLetra = new JPanel(); JPanel pnlTamano = new JPanel(); JPanel pnlEstilo = new JPanel();

JPanel pnlEstiloTamano = new JPanel();

//Se asignan las distribuciones para cada panel pnlSuperior.setLayout( new BorderLayout() ); pnlLetra.setLayout( new GridLayout( 2, 1 ) ); pnlTamano.setLayout( new GridLayout( 2, 1 ) ); pnlEstilo.setLayout( new GridLayout( 2, 1 ) ); pnlEstiloTamano.setLayout( new BorderLayout() );

//Incluye cada panel dentro de los correspondientes para visualizar mejor //la distribución de la aplicación

pnlSuperior.add( BorderLayout.WEST, pnlLetra );

pnlEstiloTamano.add( BorderLayout.WEST, pnlTamano ); pnlEstiloTamano.add( BorderLayout.CENTER, pnlEstilo ); pnlSuperior.add( BorderLayout.CENTER, pnlEstiloTamano );

getContentPane().add( BorderLayout.NORTH, pnlSuperior );

//Asigna características a la etiqueta de texto Fuentes lblFuente = new JLabel();

lblFuente.setText("Fuentes");

Font newFont = getFont().deriveFont(1); lblFuente.setFont(newFont);

lblFuente.setHorizontalAlignment(JLabel.CENTER); pnlLetra.add(lblFuente);

(44)

//Asigna características a la etiqueta de texto Tamaño lblTamano = new JLabel();

lblTamano.setText("Tamaño"); lblTamano.setFont(newFont);

lblTamano.setHorizontalAlignment(JLabel.CENTER); pnlTamano.add(lblTamano);

//Asigna características a la etiqueta de texto Estilo lblEstilo = new JLabel();

lblEstilo.setText("Estilo"); lblEstilo.setFont(newFont);

lblEstilo.setHorizontalAlignment(JLabel.CENTER); pnlEstilo.add(lblEstilo);

/*Se obtienen las fuentes disponibles en el contexto gráfico

*se asignan al objeto vector que posteriomente es enviado al combo

*de Fuentes. Se asigna un máximo de items para mostrar en el combo de 9 fila *y se predetermina como fuente inicial, la primera fuente ubicada (indice 0). *Finalmente se agrega el combo al panel correspondiente.

*/

GraphicsEnvironment gEnv = GraphicsEnvironment.getLocalGraphicsEnvironment(); String envcmbFuente[] = gEnv.getAvailableFontFamilyNames();

Vector vector = new Vector();

for ( int i = 1; i < envcmbFuente.length; i++ ) { vector.addElement(envcmbFuente[i]); }

cmbFuente = new JComboBox( vector ); cmbFuente.setMaximumRowCount( 9 ); cmbFuente.addItemListener(this); nuevafuente = envcmbFuente[0]; pnlLetra.add(cmbFuente);

/*Se asignan los valores para el combo de tamaño, los posibles tamaños serán 10, *12,14,16,18. Se define como máximo número de filas a mostrar 9. Se agrega el combo

(45)

*/

cmbTamano = new JComboBox( new Object[]{ "10", "12", "14", "16", "18"} ); cmbTamano.setMaximumRowCount( 9 );

cmbTamano.addItemListener(this); pnlTamano.add(cmbTamano);

/*Se arma el combo de estilo a partir de los estilos predeterminados.se siguen los mismo *pasos que para los combos anteriores y finalmente se incluye el combo en el panel *correspondiente.

*/

cmbEstilo = new JComboBox( new Object[]{ "PLAIN",

"BOLD", "ITALIC",

"BOLD & ITALIC"} );

cmbEstilo.setMaximumRowCount( 9 ); cmbEstilo.addItemListener(this);

cmbTamano.setMaximumRowCount( 9 ); pnlEstilo.add(cmbEstilo);

/*Se especifican las caracteristicas del Panel que va a contener el texto. */

fontC = new pnlLetra();

fontC.setBackground(Color.white);

getContentPane().add( BorderLayout.CENTER, fontC); }

/* El siguiente método detecta cuando se ha realizado la modificación de item * en alguno de los combos.

*/

public void itemStateChanged(ItemEvent e) { if ( e.getStateChange() != ItemEvent.SELECTED ) { return;

}

Referencias

Documento similar