JMU
Described Static Visual Content
An Introduction with Examples in Java


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


Simple Geometric Shapes
"Quick Start"
javaexamples/visual/statik/described/RandomRectangleCanvas.java
        package visual.statik.described;


import java.awt.*;
import java.awt.geom.*;
import java.util.Random;
import javax.swing.*;

/**
 * A JComponent that renders a Rectangle with randomly
 * generated attributes each time it's paint method is called
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class RandomRectangleCanvas extends JComponent
{
    private Random      generator;
    

    
    /**
     * Default Constructor
     */
    public RandomRectangleCanvas()
    {
       super();
       generator = new Random(System.currentTimeMillis());       
    }
    

    /**
     * Paint this Component
     *
     * @param g   The rendering engine to use
     */
    public void paint(Graphics g)
    {
       Graphics2D      g2;       
       int             height, maxHeight, maxWidth, width, x, y;
       Rectangle       rectangle;
        

       g2 = (Graphics2D)g;

       maxHeight = getHeight();
       maxWidth  = getWidth();

       x      = generator.nextInt(maxWidth  - 1);
       y      = generator.nextInt(maxHeight - 1);
       width  = generator.nextInt(maxWidth  - x - 1);
       height = generator.nextInt(maxHeight - y - 1);

       rectangle = new Rectangle(x, y, width, height);
       
       g2.draw(rectangle);       
    }

}
        
Encapsulating Points
javaexamples/visual/statik/described/SimpleShapeCanvas.java (Fragment: point)
               // Construct a point
       point = new Point2D.Double(20.0, 30.0);
        
Encapsulating Line Segments
Encapsulating Line Segments (cont.)
Encapsulating Line Segments (cont.)
javaexamples/visual/statik/described/SimpleShapeCanvas.java (Fragment: line)
               // Construct a line
       line = new Line2D.Double(0.0, 0.0, 50.0, 75.0);
        
Encapsulating Line Segments (cont.)
An Observation before Continuing

\(x(u) = p_x + u q_x - u p_x = (1 - u) p_x + u q_x\)

\(y(u) = p_y + u q_y - u p_y = (1 - u) p_y + u q_y\)

Encapsulating Quadratic Bezier Curves
javaexamples/visual/statik/described/SimpleShapeCanvas.java (Fragment: quadcurve)
               // Construct a quadratic curve
       quadraticCurve = new QuadCurve2D.Double(
                                     120.0, 120.0,  // End 1
                                     300.0, 180.0,  // Control
                                     130.0, 190.0); // End 2
        
Encapsulating Cubic Curves
javaexamples/visual/statik/described/SimpleShapeCanvas.java (Fragment: cubiccurve)
               // Construct a cubic curve
       cubicCurve = new CubicCurve2D.Double(
                                   320.0, 320.0,  // End 1
                                   300.0, 180.0,  // Control 1
                                   330.0, 370.0,  // End 2
                                   360.0, 390.0); // Control 2
        
Encapsulating Rectangles
javaexamples/visual/statik/described/SimpleShapeCanvas.java (Fragment: rectangle)
               // Construct a rectangle
       rectangle = new Rectangle2D.Double(
                                     10.0, 20.0, // Upper Left
                                     30.0,       // Width
                                     60.0);      // Height
        
Encapsulating Ellipses and Arcs
javaexamples/visual/statik/described/SimpleShapeCanvas.java (Fragment: arc)
               // Construct an arc
       arc = new Arc2D.Double(10.0, 200.0, // Upper Left
                              150.0,       // Width
                              100.0,       // Height
                              0.0,         // Starting Angle
                              135.0,       // Angular Extent        
                              Arc2D.PIE);  // Type        
        
Convexity of Closed 2-Dimensional Shapes
images/convex.gif images/nonconvex.gif
Glyphs
Glyphs (cont.)

f and i as two glyphs and as a ligature

images/ligature.gif
Glyphs (cont.)
Glyphs (cont.)

The RSB is positive for A and negative for f

images/glyph-advance.gif
Glyphs (cont.)

Kerning - The Use of Different Bearings for Differnt Glyphs

images/kerning.gif
Glyphs (cont.)
Glyphs (cont.)

Line Metrics

images/fontmetrics.gif
Glyphs (cont.)
Glyphs (cont.)

Glyphs as Shapes

javaexamples/visual/statik/described/GlyphCanvas.java
        package visual.statik.described;


import java.awt.*;
import java.awt.font.*;
import javax.swing.*;

import app.*;


/**
 * A JComponent that displays a GlyphVector
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class GlyphCanvas extends JComponent
{
    protected Font                font;
    protected String              text;


    /**
     * Default Constructor
     */
    public GlyphCanvas()
    {
        super();

        // Construct a (logical) font
        font  = new Font("Serif", Font.ITALIC, 100);
    }


    /**
     * Paint this component
     *
     * @param g   The Graphics context to use
     */
    public void paint(Graphics g)
    {
        Graphics2D              g2;

        g2 = (Graphics2D)g;

        // Set the font
        g2.setFont(font);

        // Use antialiasing for text and shapes
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);

        // Use high-quality rendering
        g2.setRenderingHint(RenderingHints.KEY_RENDERING,
                    RenderingHints.VALUE_RENDER_QUALITY);

        // Use floating point for font metrics
        g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
                    RenderingHints.VALUE_FRACTIONALMETRICS_ON);


        // Render the glyphs
        if (text != null) paintGlyphs(g2, text);
    }



    /**
     * Render a String
     */
    protected void paintGlyphs(Graphics2D g2, String text)
    {
        FontRenderContext       frc;
        GlyphVector             glyphs;
        Shape                   shape;

        frc = g2.getFontRenderContext();

        glyphs = font.createGlyphVector(frc, text);
        shape = glyphs.getOutline(0.0f, 100.0f);

        g2.setColor(Color.BLACK);
        g2.draw(shape);
    }



    /**
     * Set the text associated with this component
     */
    public void setText(String text)
    {
        this.text = text;
    }
}
        
Glyphs (cont.)

Font Metrics

javaexamples/visual/statik/described/GlyphMetricsCanvas.java
        package visual.statik.described;


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


/**
 * A VisualComponent that displays a GlyphVector and LineMetrics
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class GlyphMetricsCanvas extends GlyphCanvas
{

    /**
     * Default Constructor
     */
    public GlyphMetricsCanvas()
    {
       super();

       // Construct a (physical) font
       font  = new Font("Default", Font.PLAIN, 100);
    }


    /**
     * Render the glyphs and the metrics
     *
     * Note: There are better approaches.  This method
     * is used for illustrative purposes only.
     */
    protected void paintGlyphs(Graphics2D g2, String text)
    {
       FontRenderContext       frc;
       GlyphVector             glyphs;

       frc     = g2.getFontRenderContext();
       glyphs  = font.createGlyphVector(frc, text);

       LineMetrics             lm;

       lm      = font.getLineMetrics(text, frc);

       float                   ascent, descent, height, leading;

       // Get the various metrics
       ascent  = lm.getAscent();
       descent = lm.getDescent();
       height  = lm.getHeight();
       leading = lm.getLeading();

       Dimension               d;
       float                   baseline, pWidth, y;
       Rectangle2D             bounds;
       Shape                   shape;
       d = getSize();
       g2.setColor(Color.BLACK);

       baseline = (float)(d.height)/2.0f;
       pWidth   = (float)(d.width);

       // Draw the metrics
       y = baseline - ascent;
       g2.setColor(Color.BLUE);
       g2.draw(new Line2D.Float(0.0f, y, pWidth, y));

       y = baseline;
       g2.setColor(Color.WHITE);
       g2.draw(new Line2D.Float(0.0f, y, pWidth, y));

       g2.setColor(Color.RED);
       y = baseline + descent;
       g2.draw(new Line2D.Float(0.0f, y, pWidth, y));

       if (leading > 0) 
       {
          y = baseline + descent + leading;
          g2.setColor(Color.GREEN);
          g2.draw(new Line2D.Float(0.0f, y, pWidth, y));
       }

       // Get the outline at (0,baseline)
       shape = glyphs.getOutline(0, baseline);

       g2.setColor(Color.BLACK);
       g2.draw(shape);
       g2.fill(shape);
    }

}
        
Glyphs (cont.)

Bounding Rectangle

javaexamples/visual/statik/described/CenteredGlyphCanvas.java
        package visual.statik.described;

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


/**
 * A panel that displays a GlyphVector centered
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class CenteredGlyphCanvas extends GlyphCanvas
{

    /**
     * Default Constructor
     */
    public CenteredGlyphCanvas()
    {
        super();
    }


    /**
     * Render the glyphs centered
     *
     * Note: There are better approaches.  This method
     * is used for illustrative purposes only.
     */
    protected void paintGlyphs(Graphics2D g2, String text)
    {
        FontRenderContext       frc;
        GlyphVector             glyphs;
        Rectangle2D             bounds;
        Shape                   shape;

        frc = g2.getFontRenderContext();
        glyphs = font.createGlyphVector(frc, text);

        g2.setColor(Color.black);

        // Get the outline at (0,0)
        shape = glyphs.getOutline();

        // Get the bounding box
        bounds = glyphs.getVisualBounds();

        Dimension               d;
        float                   x, y;

        d = getSize();

        // Center the text
        x = (float)(d.width/2-bounds.getWidth()/2);
        y = (float)(d.height/2+bounds.getHeight()/2);

        // Get the outline when centered
        shape = glyphs.getOutline(x,y);

        g2.draw(shape);
    }



}
        
Glyphs (cont.)
Glyphs (cont.)

Using Convenience Methods

javaexamples/visual/statik/described/AttributedStringCanvas.java
        package visual.statik.described;


import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.text.*;
import javax.swing.*;

/**
 * A panel that displays an AttributedString
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class AttributedStringCanvas extends GlyphCanvas
{

    /**
     * Default Constructor
     */
    public AttributedStringCanvas()
    {
       super();
    }


    /**
     * Paint this component
     *
     * @param g   The Graphics context to use
     */
    public void paint(Graphics g)
    {
       Graphics2D              g2;

       super.paint(g);
       g2 = (Graphics2D)g;

       // Use antialiasing for text and shapes
       g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                           RenderingHints.VALUE_ANTIALIAS_ON);

       // Use high-quality rendering
       g2.setRenderingHint(RenderingHints.KEY_RENDERING,
                           RenderingHints.VALUE_RENDER_QUALITY);

       // Use floating point for font metrics
       g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
                           RenderingHints.VALUE_FRACTIONALMETRICS_ON);


       // Render the glyphs
       if (text != null) paintGlyphs(g2, text);
    }


    /**
     * Render the text using attributes
     */
    protected void paintGlyphs(Graphics2D g2, String text)
    {
       AttributedString        as;

       as = new AttributedString(text);
       for (int i=0; i < text.length(); i++) 
       {
          as.addAttribute(TextAttribute.FONT,
                          new Font("Serif", Font.PLAIN, (i+10)), 
                          i, i+1);
       }

       g2.setColor(Color.BLACK);
       g2.drawString(as.getIterator(), 0, 100);
    }

}
        
Complicated Geometric Shapes
Complicated Geometric Shapes (cont.)

An Example

javaexamples/visual/statik/described/BoringBuzzy.java (Fragment: body1)
        
       bodyShape = new Path2D.Float();
       bodyShape.moveTo( 20, 50);
       bodyShape.lineTo( 20, 70);
       bodyShape.lineTo( 20, 90);
       bodyShape.lineTo( 10, 90);
       bodyShape.lineTo( 10,100);
       bodyShape.lineTo( 80,100);
       bodyShape.lineTo( 80, 90);
       bodyShape.lineTo( 40, 90);
       bodyShape.lineTo( 40, 70);
       bodyShape.lineTo( 40, 50);
       bodyShape.closePath();
        
Constructive Area Geometry
Constructive Area Geometry (cont.)

An Example

javaexamples/visual/statik/described/ConstructiveAreaGeometryCanvas.java
        package visual.statik.described;

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

/**
 * A JPanel that performs constructive area geometryt operations
 * on an Ellipse2D and a Rectangle2D
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class   ConstructiveAreaGeometryCanvas 
       extends JComponent
{
    private Ellipse2D        ellipse;
    private Rectangle2D      rectangle;
    private String           op;


    private static final Color GOLD  =new Color(194,161, 77);
    private static final Color PURPLE=new Color( 69,  0,132);

    
    /**
     * Default Constructor
     */
    public ConstructiveAreaGeometryCanvas()
    {
        ellipse   = new Ellipse2D.Double(20, 20, 100, 100);
        rectangle = new Rectangle2D.Double(60, 40, 200, 200); 


        op = "Outline";
    }



    /**
     * Paint this Component
     *
     * @param g   The rendering engine to use
     */
    public void paint(Graphics g)
    {
        Area               e, r;
        Graphics2D         g2;


        g2 = (Graphics2D)g;

        if (op.equalsIgnoreCase("Outline"))
        {
            g2.setPaint(Color.BLACK);
            g2.draw(ellipse);
            g2.draw(rectangle);

        }
        else
        {
            e = new Area(ellipse);
            r = new Area(rectangle);
            
            if      (op.equalsIgnoreCase("Union"))        
                           e.add(r);
            else if (op.equalsIgnoreCase("Intersection")) 
                           e.intersect(r);
            else if (op.equalsIgnoreCase("Difference"))   
                           e.subtract(r);
            else                                          
                           e.exclusiveOr(r);
            
            g2.setPaint(GOLD);
            g2.fill(e);
            g2.setPaint(PURPLE);
            g2.draw(e);
        }
    }


    /**
     * Set the CAG operation (Outline, Union, Intersection,
     * Difference, or Symmetric_Difference)
     *
     * @op  The operation
     */
    public void setOperation(String op)
    {
        this.op = op;
    }
}
        
Transforms
Transforms (cont.)

Translation

images/translation.gif
Transforms (cont.)

Two Scalings

images/scaling.gif
Transforms (cont.)

Reflection

images/reflection.gif
Transforms (cont.)

Rotation

images/rotation.gif
Transforms (cont.)

An Example

javaexamples/visual/statik/described/SpiralGlyphCanvas.java
        package visual.statik.described;


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

/**
 * A panel that displays a GlyphVector
 * in a spiral
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class SpiralGlyphCanvas extends GlyphCanvas
{

    /**
     * Constructor
     */
    public SpiralGlyphCanvas()
    {
       super();
    }


    /**
     * Render the glyphs
     */
    protected void paintGlyphs(Graphics2D g2, String text)
    {
       AffineTransform         at, trans;
       AlphaComposite          alpha;
       Dimension               d;
       float                   angle, x, y;
       FontRenderContext       frc;
       GlyphVector             glyphs;
       int                     i;
       Shape                   shape, transformedShape;

       d = getSize();

       frc = g2.getFontRenderContext();
       glyphs = font.createGlyphVector(frc, text);
       shape = glyphs.getOutline(0.0f, 100.0f);
       g2.setColor(Color.BLACK);

       for (i=0; i < 6; i++) 
       {
          angle = (float)(Math.PI/6.0 * i);
          x = (float)(d.width/2.0);
          y = (float)(d.height/2.0);
          at = AffineTransform.getRotateInstance(angle,x,y);
          trans = AffineTransform.getTranslateInstance(x,y);
          at.concatenate(trans);

          transformedShape = at.createTransformedShape(shape);
            
          g2.fill(transformedShape);
       }
    }
}
        
Rendering Described Content
Rendering Described Content (cont.)

Stroke Joins

images/stroke_joins.gif
Rendering Described Content (cont.)

Stroke Caps

images/stroke_caps.gif
Rendering Described Content (cont.)
Rendering Described Content (cont.)

An Example

javaexamples/visual/statik/described/RenderingExampleCanvas.java
        package visual.statik.described;


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

/**
 * A VisualComponent that illustrates the rendering of
 * described visual content
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class   RenderingExampleCanvas 
       extends JComponent
{
    
    /**
     * Default Constructor
     */
    public RenderingExampleCanvas()
    {
       super();
    }
    

    /**
     * Paint this Component
     *
     * @param g   The rendering engine to use
     */
    public void paint(Graphics g)
    {
       AlphaComposite         composite;       
       GradientPaint          gradient;       
       Graphics2D             g2;       
       Rectangle2D            rectangle;
       Stroke                 stroke;
       
        

       g2 = (Graphics2D)g;

       

       // Use antialiasing for text and shapes
       g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                           RenderingHints.VALUE_ANTIALIAS_ON);

       // Use high-quality rendering
       g2.setRenderingHint(RenderingHints.KEY_RENDERING,
                           RenderingHints.VALUE_RENDER_QUALITY);


        
       // The first rectangle
       rectangle = new Rectangle2D.Double( 10.0,  20.0,
                                           100.0,      
                                           150.0);     

       // Fill in JMU Gold
       g2.setPaint(new Color(0xC2, 0xA1, 0x4D));
       g2.fill(rectangle);
        

       // Stroke in JMU purple
       stroke = new BasicStroke(5.0f, 
                                BasicStroke.CAP_BUTT, 
                                BasicStroke.JOIN_MITER);
       g2.setStroke(stroke);       
       g2.setColor(new Color(0x45, 0x00, 0x84));
       g2.draw(rectangle);





       // The second rectangle
       rectangle = new Rectangle2D.Double( 200.0, 200.0,
                                           100.0,       
                                           150.0);      

       // Fill using a gradient
       gradient=new GradientPaint(200.0f, 275.0f, Color.CYAN,
                                  300.0f, 275.0f, Color.WHITE);       
       g2.setPaint(gradient);
       g2.fill(rectangle);

       // Stroke in black
       stroke = new BasicStroke(10.0f, 
                                BasicStroke.CAP_BUTT, 
                                BasicStroke.JOIN_ROUND);
       g2.setStroke(stroke);       
       g2.setColor(Color.BLACK);
       g2.draw(rectangle);



       // The third rectangle
       rectangle = new Rectangle2D.Double( 50.0, 50.0,
                                          200.0,      
                                          250.0);     


       // Use alpha blending to achieve a transparency effect
       composite = AlphaComposite.getInstance(
                                     AlphaComposite.SRC_OVER,
                                     0.5f);
       g2.setComposite(composite);
       
       // Fill in gray
       g2.setPaint(Color.YELLOW);
       g2.fill(rectangle);

       // Don't stroke 
    }
    
}

        
Design of a Described Static Visual Content System
Requirements
  1. Support the addition of described static visual content
  2. Support the removal of described static visual content
  3. Support \(z\)-ordering of described static visual content
  4. Support the transformation of described static visual content
  5. Support the rendering of described static visual content
Design of a Described Static Visual Content System
Design of a Described System (cont.)

Two Designs

images/attributedshape.gif images/decoratedshape.gif
Design of a Described System (cont.)

A Better Design

images/TransformableContent_described.gif
Design of a Described System (cont.)
TransformableContent
javaexamples/visual/statik/described/TransformableContent.java
        package visual.statik.described;

import java.awt.*;
import java.awt.geom.*;


/**
 * The requirements of transformable static 
 * described visual content
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public interface TransformableContent 
       extends   visual.statik.TransformableContent
{

    /**
     * Set the Color to use for stroking
     *
     * @param color The Color to use (or null to prevent stroking)
     */
    public abstract void setColor(Color color);


    /**
     * Set the Paint to use for filling
     *
     * @param paint  The Paint to use (or null to prevent filling)
     */
    public abstract void setPaint(Paint paint);


    /**
     * Set the Stroke to use
     *
     * @param stroke  The Stroke to use (or null to use the default)
     */
    public abstract void setStroke(Stroke stroke);

}
        
Design of a Described System (cont.)
Content
javaexamples/visual/statik/described/Content.java (Fragment: skeleton)
        package visual.statik.described;


import java.awt.*;
import java.awt.geom.*;


/**
 * Described visual content that knows how to render and transform
 * itself
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class Content 
             extends visual.statik.AbstractTransformableContent
             implements TransformableContent
{
    private Color                color;    
    private Paint                paint;    
    private Rectangle2D.Double   originalBounds;
    private Rectangle2D.Double   transformedBounds;
    private Shape                originalShape;
    private Shape                transformedShape;
    private Stroke               stroke;

    private final Stroke          DEFAULT_STROKE = 
                                   new BasicStroke();

    private final AffineTransform IDENTITY = 
                                   new AffineTransform();
    
    /**
     * Default Constructor
     */
    public Content()
    {
       this(null, null, null, null);
    }
    
    

    /**
     * Explicit Value Constructor
     *
     * @param shape     The Shape
     * @param color     The Color to stroke with (or null for no stroke)
     * @param paint     The Paint to fill with (or null for no fill)
     * @param stroke    The Stroke (or null for the default)
     */
    public Content(Shape  shape,  Color color, Paint paint,
                   Stroke stroke)
    {
       super();       

       setColor(color);
       setPaint(paint);
       setStroke(stroke);       

       setShape(shape);
    }

    /**
     * Fill the given Rectangle2D with the bounds
     * from the given Shape
     *
     * @param r    The Rectangle2D to fill (out)
     * @param s    The Shape to use (in)
     */
    private void getBoundsFor(Rectangle2D.Double r, Shape s)
    {
       Rectangle2D       temp;
       
       if (s != null)
       {
          temp = s.getBounds2D();

          if (temp != null)
          {
             r.x      = temp.getX();
             r.y      = temp.getY();
             r.width  = temp.getWidth();
             r.height = temp.getHeight();  
          }
       }
    }
}
        
javaexamples/visual/statik/described/Content.java (Fragment: getters)
            /**
     * Get the stroke Color
     *
     * @return The stroke Color
     */
    public Color getColor()
    {
       return color;       
    }


    /**
     * Get the fill Paint
     *
     * @return The Paint
     */
    public Paint getPaint()
    {
       return paint;       
    }

    /**
     * Get the Stroke
     *
     * @return The Stroke
     */
    public Stroke getStroke()
    {
       return stroke;
    }
        
javaexamples/visual/statik/described/Content.java (Fragment: setters)
            /**
     * Set the Color to use for stroking
     *
     * @param color The Color to use (or null to prevent stroking)
     */
    public void setColor(Color color)
    {
       this.color = color;       
    }


    /**
     * Set the Paint to use for filling
     *
     * @param paint  The Paint to use (or null to prevent filling)
     */
    public void setPaint(Paint paint)
    {
       this.paint = paint;       
    }


    /**
     * Set the Shape
     *
     * @param shape  The Shape (or null to render nothing)
     */
    public void setShape(Shape shape)
    {
       originalShape     = shape;       

       transformedBounds = new Rectangle2D.Double();
       originalBounds    = new Rectangle2D.Double();

       if (originalShape != null)
       {
          if (isTransformationRequired()) 
          {
             createTransformedContent();
          }
          else
          {
             transformedShape  = shape;       
          }

          getBoundsFor(originalBounds, originalShape);       
          getBoundsFor(transformedBounds, transformedShape);
       }
    }
    

    /**
     * Set the Stroke to use
     *
     * @param stroke  The Stroke to use (or null to use the default)
     */
    public void setStroke(Stroke stroke)
    {
       if (stroke == null) this.stroke = DEFAULT_STROKE;       
       else                this.stroke = stroke;
    }
        
javaexamples/visual/statik/described/Content.java (Fragment: transform)
            /**
     * Create the transformed version of the shape
     */
    private void createTransformedContent()
    {
       createTransformedContent(getAffineTransform());
    }

    /**
     * Create a transformed version of the shape
     *
     * @param at   The AffineTransform to use
     */
    private void createTransformedContent(AffineTransform at)
    {
       transformedShape = at.createTransformedShape(
                                              originalShape);
       setTransformationRequired(false);       

       getBoundsFor(transformedBounds, transformedShape);
    }
        
javaexamples/visual/statik/described/Content.java (Fragment: bounds)
            /**
     * Returns a high precision bounding box of the Content
     * either before or after it is transformed
     *
     * @param    transformed    true to get the BB of the transformed content
     * @return   The bounding box
     */
    public Rectangle2D getBounds2D(boolean transformed)
    {
       if   (transformed) return transformedBounds;
       else               return originalBounds;
    }
        
javaexamples/visual/statik/described/Content.java (Fragment: render)
            /**
     * Render this described visual content
     *
     * @param g2   The rendering engine to use
     */
    public void render(Graphics g)
    {
       Color       oldColor;
       Graphics2D  g2;       
       Paint       oldPaint;
       Stroke      oldStroke;
       

       g2 = (Graphics2D)g;
       
       
       // Transform the Shape (if necessary)
       if (isTransformationRequired()) 
       {
          createTransformedContent();
       }
          
       if (transformedShape != null)
       {
          // Save the state
          oldColor     = g2.getColor();
          oldPaint     = g2.getPaint();
          oldStroke    = g2.getStroke();


          // Fill the Shape (if appropriate)
          if (paint != null)
          {
             g2.setPaint(paint);
             g2.fill(transformedShape);
          }

          // Stroke the Shape if appropriate
          if (color != null)
          {
             if (stroke != null) g2.setStroke(stroke);          
             g2.setColor(color);
             g2.draw(transformedShape);          
          }

          // Restore the state
          g2.setColor(oldColor);
          g2.setPaint(oldPaint);
          g2.setStroke(oldStroke);
       }       
    }