## Punteado y Relleno de Gráficos Primitvos

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 appler, 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á construidas 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áicas usando CubicCurve2D y QuadCurve2D. Estos applets también demuestran como se dibujan las curvas con respecto al posicionamiento de los puntos de control permitiendonos interactivamente mover 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 appler, pulsa sobre ella. El applet aparecerá en una nueva ventana del navegador.
> > Quad.java contiene el código completo de este applet.

> > Primero se crra una nueva curva cuadrática con dos puntos finales y un punto de control y las posiciones de los puntos se seleccionan conrespecto 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 demuestra 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 contala el punto final rolo y el punto de control verde controla el punto final magenta.

> Esta es una imagen del GUI del applet. Para ajecutar el appler, 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 pulsos.

>     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 appler, 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 parh.

> >     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 tiene línea punteadas o están rellenas congradientes 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á definida 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 es usada cuendo un Shape es dibjujado 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 especifica 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 decoricació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 es definido por un array de punteada y una fase de punteado. El array de punteado degine 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 definida por un BufferedImage. Para crear un TexturePaint, se especifica una imagen que contiene el patrón y un rectángulo que es usado 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 appler, 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 objeto 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 siguinetes líneas transforman el TextLayout para que sea centrado en el origen y luego intorduce 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, Su 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 b>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;

Ozito