Punteado y Relleno de Gráficos Primitivos
Cambiando el punteado y los atributos de dibujo en el contexto de
Graphics2D, antes del dibujo, podemos fácilmente aplicar estilos divertidos de
líneas y patrones de relleno para gráficos primitivos. Por ejemplo, podemos dibujar una
línea punteada creando el objeto Stroke apropiado y llamando a
setStroke para añadirlo al contexto Graphics2D
antes de dibujar la línea. De forma similar, podemos aplicar un relleno de gradiente a un
Shape creando un objeto GradientPaint y
añadiendo al contexto Graphics2D llamando a
setPaint antes de dibujar la Shape.
El siguiente applet demuestra cómo podemos dibujar formas geométricas usando los métodos
Graphics2D draw y fill.
Esta es una imagen del GUI del applet. Para ajecutar el applet, pulsa
sobre ella. El applet aparecerá en una nueva ventana del navegador.
ShapesDemo2D.java contiene el código completo de este applet.
Cada una de las formas dibujadas por el applet está construida de geometrías y está
dibujada a través de Graphics2D. Las variables
rectHeight y rectWidth de este ejemplo definen las
dimensiones del espacio en que se dibuja cada forma, en pixels. La variables
x e y cambian para cada forma para que sean dibujadas en
formación de parrilla.
| |
// draw Line2D.Double
g2.draw(new Line2D.Double(x, y+rectHeight-1,
x + rectWidth, y));
|
| |
// draw Rectangle2D.Double
g2.setStroke(stroke);
g2.draw(new Rectangle2D.Double(x, y,
rectWidth,
rectHeight));
|
| |
// draw RoundRectangle2D.Double
g2.setStroke(dashed);
g2.draw(new RoundRectangle2D.Double(x, y,
rectWidth,
rectHeight,
10, 10));
|
| |
// draw Arc2D.Double
g2.setStroke(wideStroke);
g2.draw(new Arc2D.Double(x, y,
rectWidth,
rectHeight,
90, 135,
Arc2D.OPEN));
|
| |
// draw Ellipse2D.Double
g2.setStroke(stroke);
g2.draw(new Ellipse2D.Double(x, y,
rectWidth,
rectHeight));
|
| |
// draw GeneralPath (polygon)
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]);
};
polygon.closePath();
g2.draw(polygon);
|
| |
// draw GeneralPath (polyline)
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);
|
| |
// fill Rectangle2D.Double (red)
g2.setPaint(red);
g2.fill(new Rectangle2D.Double(x, y,
rectWidth, rectHeight));
|
| |
// fill RoundRectangle2D.Double
g2.setPaint(redtowhite);
g2.fill(new RoundRectangle2D.Double(x, y,
rectWidth,
rectHeight,
10, 10));
|
| |
// fill Arc2D
g2.setPaint(red);
g2.fill(new Arc2D.Double(x, y, rectWidth,
rectHeight, 90,
135, Arc2D.OPEN));
|
| |
// fill Ellipse2D.Double
g2.setPaint(redtowhite);
g2.fill (new Ellipse2D.Double(x, y,
rectWidth,
rectHeight));
|
| |
// fill and stroke GeneralPath
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]);
};
filledPolygon.closePath();
g2.setPaint(red);
g2.fill(filledPolygon);
|
Observa que este ejemplo usa implementaciones de doble precision de las clases
geométricas. Donde sea posible, las implementaciones de los float y doble precisión de
cada geométrico están proporcionados por clases internas.
Dibujar Curvas
Los applets Cubic y Quad demuestran como crear
curvas cúbicas y cuadráticas usando CubicCurve2D y
QuadCurve2D. Estos applets también demuestran como se dibujan las curvas con
respecto al posicionamiento de los puntos de control permitiendonos mover interactivamente
tanto los puntos de control como los puntos finales.
Ejemplo: Quad
El applet Quad demuestra una curva cuadrática, que es un segmento de
curva que tiene dos puntos finales y un único punto de control. El punto de control
determina la forma de la curva controlando tanto el punto de control como los vectores
tangenciales de los puntos finales.
Esta es una imagen del GUI del applet. Para ajecutar el applet, pulsa
sobre ella. El applet aparecerá en una nueva ventana del navegador.
Quad.java
contiene el código completo de este applet.
Primero se crea una nueva curva cuadrática con dos puntos finales y un punto de control y
las posiciones de los puntos se seleccionan con respecto al tamaño de la ventana.
QuadCurve2D.Double quad = new QuadCurve2D.Double();
Point2D.Double start, end, control;
start = new Point2D.Double();
one = new Point2D.Double();
control = new Point2D.Double();
quad.setCurve(start, one, control);
start.setLocation(w/2-50, h/2);
end.setLocation(w/2+50, h/2);
control.setLocation((int)(start.x)+50, (int)(start.y)-50);
Cada vez que el usuario mueva uno de los puntos, la curva se reseteará.
quad.setCurve(start, control, end);
Ejemplo: Cubic
El ejemplo Cubic muestra una curva cúbica, que es un segmento de
curva que tiene dos puntos finales y dos puntos de control. Cada punto de control
determina la forma de la curva mediante el control de uno de los vectores tangenciales de
un punto final. En el ejemplo Cubic, las cruces coloreadas se
dibujan donde se encuentran los puntos de control y los puntos finales. El punto de
control azul controla el punto final rojo y el punto de control verde controla el punto
final magenta.
Esta es una imagen del GUI del applet. Para ajecutar el applet, pulsa
sobre ella. El applet aparecerá en una nueva ventana del navegador.
Cubic.java
contiene el código completo de este applet.
Una curva cúbica se crea con dos puntos finales y dos puntos de control y las
localizaciones de los puntos se seleccionan con respecto al tamaño de la ventana.
CubicCurve2D.Double cubic = new CubicCurve2D.Double();
Point2D.Double start, end, one, two;
start = new Point2D.Double();
one = new Point2D.Double();
two = new Point2D.Double();
end = new Point2D.Double();
cubic.setCurve(start, one, two, end);
...
start.setLocation(w/2-50, h/2);
end.setLocation(w/2+50, h/2);
one.setLocation((int)(start.x)+25, (int)(start.y)-25);
two.setLocation((int)(end.x)-25, (int)(end.y)+25);
Como en el ejemplo Quad, la curva es reseteada cada vez que se
mueven los puntos.
cubic.setCurve(start, one, two, end);
Dibujar formas arbitrarias
El ejemplo ShapesDemo usa GeneralPath para
hacer polígonos en forma de cristales, pero tambien podemos usar
GeneralPath para hacer formas arbitrarias tanto con líneas rectas como curvas.
Ejemplo: Odd_Shape
El ejemplo Odd_Shape usa GeneralPath para crear
formas arbitrarias en la sección
Formas.
Esta es una imagen del GUI del applet. Para ajecutar el applet, pulsa
sobre ella. El applet aparecerá en una nueva ventana del navegador.
Odd_Shape.java
contiene el código completo de este applet.
El siguiente código crea un nuevo GeneralPath y añade el primer
punto al path.
GeneralPath oddShape = new GeneralPath();
...
x = w/2 + 50;
y = h/2 - 25;
x2 = x;
y2 = y;
oddShape.moveTo(x, y);
Después de añadir el primer punto al path, se añaden tres líneas rectas.
x -= 100;
oddShape.lineTo(x, y);
y += 50;
oddShape.lineTo(x, y);
x += 100;
oddShape.lineTo(x, y);
Finalmente, se añade una curva cúbica.
x += 10;
y -= 10;
x1 = x - 20;
y1 = y - 20;
oddShape.curveTo(x, y, x1, y1, x2, y2);
Definir Estilos de línea divertidos y Patrones de relleno.
Probablemente habrás observado en el ejemplo anterior algunas de las formas tienen líneas
punteadas o están rellenas con gradientes de dos colores. Usando las clases
Stroke y Paint de Java 2D, podemos fácilmente definir
estilos de línea divertidos y patrones de relleno.
Estilos de Línea
Los estilos de línea están definidos por el atributo stroke en el contexto
Graphics2D. Para seleccionar el atributo stroke podemos crear un objeto
BasicStroke y pasarlo dentro del método
Graphics2D setStroke.
Un objeto BasicStroke contiene información sobre la anchura de la
línea, estilo de uniones, estilos finales, y estilo de punteado. Esta información se
usa cuando se dibuja un Shape con el método
draw.
La anchura de línea es la longitud de la línea medida
perpendicularmente a su trayectoria. La anchura de la línea se especificada como un valor
float en las unidades de coordenadas de usuario, que es equivalente
a 1/72 pulgadas cuando se utiliza la transformación por defecto.
El estilo de unión es la decoración que se aplica cuando se encuentran dos segmentos de
línea. BasicStroke soporta tres estilos de unión:.
JOIN_BEVEL
JOIN_MITER
JOIN_ROUND
El estilo de finales es la decoración que se aplica cuando un segmento de línea termina.
BasicStroke soporta tres estilos de finalización:
CAP_BUTT
CAP_ROUND
CAP_SQUARE
El estilo de punteado define el patrón de las secciones opacas y transparentes aplicadas
a lo largo de la longitud de la línea. Este estilo está definido por un array de punteado y
una fase de punteado. El array de punteado define el patrón de punteado. Los elementos
alternativos en el array representan la longitud del punteado y el espacio entre
punteados en unidades de coordenadas de usuario.. El elemento 0 representa el primer
punteado, el elemento 1 el primer espacio, etc. La fase de punteado es un desplazamiento
en el patrón de punteado, también especificado en unidades de coordenadas de usuario. La
fase de punteado indica que parte del patrón de punteado se aplica al principio de la
línea.
Patrón de Relleno
Los patrones de rellenos están definidos por el atributo paint en el contexto
Graphics2D. Para seleccionar el atributo paint, se crea un ejemplar de un
objeto que implemente el interface Paint y se pasa dentro del método
Graphics2D setPaint.
Tres clases implementan el interface Paint:
Color, GradientPaint, y TexturePaint
. GradientPaint y TexturePaint son nuevas en el
JDK 1.2.
Para crear un GradientPaint, se especifica una posición inicial y un
color y una posición final y otro color. El gradiente cambia proporcionalmente desde un
color al otro a lo largo de la línea que conecta las dos posiciones.
El patrón para una TexturePaint esta definido por un
BufferedImage. Para crear un TexturePaint, se especifica
una imagen que contiene el patrón y un rectángulo que se usa para replicar y anclar el
patrón.
Ejemplo: StrokeAndFill
El applet StrokeAndFill permite al usuario seleccionar un gráfico
primitivo, un estilo de línea, un estilo de dibujo y o bien puntear el exterior del
objeto, rellenarlo con el dibujo seleccionado, o puntear el objeto en blanco y rellenar
el dibujo con el dibujo seleccionado.
Esta es una imagen del GUI del applet. Para ajecutar el applet, pulsa
sobre ella. El applet aparecerá en una nueva ventana del navegador.
StrokeAndFill.java contiene el código completo de este applet.
Los primitivos son inicializados e introducidos en un array de objetos
Shape. El siguiente código crea un Rectangle y un
Ellipse2D.Double y los introduce en el array
shapes.
shapes[0] = new Rectangle(0, 0, 100, 100);
shapes[1] = new Ellipse2D.Double(0.0, 0.0, 100.0, 100.0);
Para crear un objeto Shape desde una cadena de texto, primero
debemos crear un objeto TextLayout desde el texto de la cadena.
TextLayout textTl = new TextLayout("Text",
new Font("Helvetica", 1, 96),
new FontRenderContext(null, false, false));
Las siguientes líneas transforman el TextLayout para que sea
centrado en el origen y luego introduce el objeto Shape resultante
de la llamda a getOutline dentro del array
shapes.
AffineTransform textAt = new AffineTransform();
textAt.translate(0,
(float)textTl.getBounds().getHeight());
shapes[2] = textTl.getOutline(textAt);
Podemos elegir un primitivo accediendo al índice apropiado dentro del array
shapes.
Shape shape =
shapes[Transform.primitive.getSelectedIndex()];
Cómo se realiza el dibujo dependen de las opciones elegidas.
- Cuando el usuario elige stroke, se llama a
Graphics2D.draw para realizar el dibujo, Si se elige
text como primitivo, las líneas son recuperadas y el dibujo se hace con el
método draw.
- Cuando el usuario elige fill, se llama a
Graphics2D.fill o Graphics2D.drawString para realizar el
dibujado.
- Cuando el usuario elige stroke and fill, se llama a
fill o drawString para rellenar el
Shape, y luego se llama a draw para dibujar la línea
exterior.
|
Nota:
Para rellenar y puntear un gráfico primitivo, necesitamos hacer dos llamadas separadas a
métodos: fill o drawString para rellenar el
interior, y draw para dibujar el exterior.
|
Los tres estilos de línea usados en este ejemplo -- ancho, estrecho y punteado -- son
ejemplares de BasicStroke.
// Sets the Stroke.
...
case 0 : g2.setStroke(new BasicStroke(3.0f)); break;
case 1 : g2.setStroke(new BasicStroke(8.0f)); break;
case 2 : float dash[] = {10.0f};
g2.setStroke(new BasicStroke(3.0f,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER,
10.0f, dash, 0.0f));
break;
El estilo de punteado de este ejemplo tiene 10 unidades de punteado alternados con 10
unidades de espacio. El principio del patrón del punteado se aplica al principio de la
línea -- la fase de punteado es 0.0.
En este ejemplo se usan tres estilos de dibujo -- sólido, gradiente y polka. El dibujo de
color sólido es un ejemplar de Color, el gradiente un ejemplar de
GradientPaint, y el patrón un ejemplar de
TexturePaint.
// Sets the Paint.
...
case 0 : g2.setPaint(Color.blue); break;
case 1 : g2.setPaint(new GradientPaint(0, 0,
Color.lightGray,
w-250, h, Color.blue, false));
break;
case 2 : BufferedImage bi = new BufferedImage(5, 5,
BufferedImage.TYPE_INT_RGB);
Graphics2D big = bi.createGraphics();
big.setColor(Color.blue);
big.fillRect(0, 0, 5, 5);
big.setColor(Color.lightGray);
big.fillOval(0, 0, 5, 5);
Rectangle r = new Rectangle(0,0,5,5);
g2.setPaint(new TexturePaint(bi, r));
break;