JMU
A Flexible Static Visual Content System


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


The Current System
An Additional Requirement
The system must support visual content that has multiple components parts
Design Alternatives Ignoring Content Types

One Obvious Design

images/TransformableContent_aggregate.gif
Design Alternatives Ignoring Content Types (cont.)

Using the Composite Pattern

images/TransformableContent_composite.gif
Design Alternatives Incorporating Content Types

Three "Copies" of the Composite

images/CompositeContent_duplication.gif
Design Alternatives Incorporating Content Types (cont.)

One Composite

images/CompositeContent_no-duplication.gif
Design Alternatives Incorporating Content Types (cont.)

Using an Abstract Parent

images/CompositeContent_no-generics.gif
Design Alternatives Incorporating Content Types (cont.)

Using a Parameterized Parent

images/CompositeContent.gif
visual.statik Package

AbstractAggregateContent

javaexamples/visual/statik/AbstractAggregateContent.java (Fragment: skeleton)
        package visual.statik;

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

/**
 * A generic piece of static visual content that is a composition
 * of many component parts
 *
 * Note: The bounds are not stored in attributes to make this
 * class easier to understand.  To do so would require:
 * (1) Use of the isTransformationRequired() method in the parent;
 * and (2) Changes to the add() and remove() methods in 
 * this class.
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public abstract class 
                AbstractAggregateContent<C extends TransformableContent>
       extends  AbstractTransformableContent
{
    // A LinkedList is used to order the components.
    // Since we don't expect many components to be removed
    // this doesn't raise efficiency concerns.
    //
    // Alternatively, we could have a z-order.
    protected LinkedList<C>        components;
    


    /**
     * Default Constructor
     */
    public AbstractAggregateContent()
    {
       super();       
       components = new LinkedList<C>();
    }

}
        
javaexamples/visual/statik/AbstractAggregateContent.java (Fragment: content)
        
    /**
     * Add a TransformableContent
     *
     * @param component   The TransformableContent to add
     */
    public void add(C component)
    {
       components.add(component);
    }

    /**
     * Return an Iterator containing all of the TransformableContent
     * objects
     *
     * @return   The Iterator
     */
    public Iterator<C> iterator()
    {
       return components.iterator();       
    }
    

    /**
     * Remove a TransformableContent
     *
     * @param component   The TransformableContent to remove
     */
    public void remove(C component)
    {
       components.remove(component);
    }
        
javaexamples/visual/statik/AbstractAggregateContent.java (Fragment: render)
        
    /**
     * Render this AbstractAggregateContent
     * (required by TransformableContent)
     *
     * @param g   The rendering engine to use
     */
    public void render(Graphics g)
    {
       Iterator<C>   i;
       C             component;
       

       i = components.iterator();
       while (i.hasNext())
       {
          component = i.next();

          component.setLocation(x, y);
          component.setRotation(angle, xRotation, yRotation);
          component.setScale(xScale, yScale);

          component.render(g);          
       }
    }
        
javaexamples/visual/statik/AbstractAggregateContent.java (Fragment: bounds)
        
    /**
     * Returns a high precision bounding box of the Content
     * either before or after it is transformed
     *
     * @param    ofTransformed    true to get the BB of the transformed content
     * @return   The bounding box
     */
    public Rectangle2D getBounds2D(boolean ofTransformed)
    {
       double                maxX, maxY, minX, minY, rx, ry;
       Iterator<C>           i;
       Rectangle2D           bounds;
       TransformableContent  component;

       maxX = Double.NEGATIVE_INFINITY;
       maxY = Double.NEGATIVE_INFINITY;
       minX = Double.POSITIVE_INFINITY;
       minY = Double.POSITIVE_INFINITY;
       
       
       i = components.iterator();
       while (i.hasNext())
       {
          component = i.next();

          bounds    = component.getBounds2D(ofTransformed);

          if (bounds != null)
          {
             rx = bounds.getX();
             ry = bounds.getY();
             if (rx < minX) minX = rx;
             if (ry < minY) minY = ry;
             
             rx = bounds.getX() + bounds.getWidth();
             ry = bounds.getY() + bounds.getHeight();
             if (rx > maxX) maxX = rx;
             if (ry > maxY) maxY = ry;
          }          
       }
       // We could use an object pool
       return new Rectangle2D.Double(minX, minY, maxX-minX, maxY-minY);
    }


    /**
     * Return an Iterator containing all of the TransformableContent
     * objects
     *
     * @return   The Iterator
     */
    public Iterator<C> iterator()
    {
       return components.iterator();       
    }
    

    /**
     * Remove a TransformableContent
     *
     * @param component   The TransformableContent to remove
     */
    public void remove(C component)
    {
       components.remove(component);
    }

    /**
     * Render this AbstractAggregateContent
     * (required by TransformableContent)
     *
     * @param g   The rendering engine to use
     */
    public void render(Graphics g)
    {
       Iterator<C>   i;
       C             component;
       

       i = components.iterator();
       while (i.hasNext())
       {
          component = i.next();

          component.setLocation(x, y);
          component.setRotation(angle, xRotation, yRotation);
          component.setScale(xScale, yScale);

          component.render(g);          
       }
    }

}
        
visual.statik Package (cont.)

CompositeContent

javaexamples/visual/statik/CompositeContent.java
        package visual.statik;



/**
 * Static visual content that is a composition
 * of many component parts, either sampled or described
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class      CompositeContent 
       extends    AbstractAggregateContent<TransformableContent>
       implements TransformableContent
{
    /**
     * Default Constructor
     */
    public CompositeContent()
    {
       super();       
    }
    
}
        
visual.statik.sampled Package

CompositeContent

javaexamples/visual/statik/sampled/CompositeContent.java
        package visual.statik.sampled;

import java.awt.Composite;
import java.awt.image.BufferedImageOp;
import java.util.Iterator;

import visual.statik.AbstractAggregateContent;


/**
 * Static sampled visual content that is a composition
 * of many component parts
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class      CompositeContent 
       extends    AbstractAggregateContent<TransformableContent>
       implements TransformableContent
{
    /**
     * Default Constructor
     */
    public CompositeContent()
    {
       super();       
    }
 
    /**
     * Set the BufferedImageOp to use when transforming
     * the Image
     *
     * @param op   The BufferedImageOp
     */
    public void setBufferedImageOp(BufferedImageOp op)
    {
       Iterator<TransformableContent>  i;
       
       i = iterator();
       while (i.hasNext())
       {
          i.next().setBufferedImageOp(op);          
       }
    }

    /**
     * Set the transparency/Composite
     *
     * @param c   The Composite
     */
    public void setComposite(Composite c)
    {
       Iterator<TransformableContent>  i;
       
       i = iterator();
       while (i.hasNext())
       {
          i.next().setComposite(c);          
       }
    }
    
}
        
visual.statik.described Package

CompositeContent

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

import java.awt.*;
import java.util.Iterator;

import visual.statik.AbstractAggregateContent;


/**
 * Static described visual content that is a composition
 * of many component parts
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class      CompositeContent 
       extends    AbstractAggregateContent<TransformableContent>
       implements TransformableContent
{
    /**
     * Default Constructor
     */
    public CompositeContent()
    {
       super();       
    }


    /**
     * Set the Color to use for stroking
     *
     * @param color The Color to use (or null to prevent stroking)
     */
    public void setColor(Color color)
    {
       Iterator<TransformableContent>  i;
       
       i = iterator();
       while (i.hasNext())
       {
          i.next().setColor(color);          
       }
    }

    /**
     * Set the Paint to use for filling
     *
     * @param paint  The Paint to use (or null to prevent filling)
     */
    public void setPaint(Paint paint)
    {
       Iterator<TransformableContent>  i;
       
       i = iterator();
       while (i.hasNext())
       {
          i.next().setPaint(paint);          
       }
    }

    /**
     * Set the Stroke to use
     *
     * @param stroke  The Stroke to use (or null to use the default)
     */
    public void setStroke(Stroke stroke)
    {
       Iterator<TransformableContent>  i;
       
       i = iterator();
       while (i.hasNext())
       {
          i.next().setStroke(stroke);          
       }
    }
   
}
        
Examples

described.CompositeContent

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


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

import visual.statik.described.CompositeContent;
import visual.statik.described.Content;


/**
 * An example of a  described.CompositeContent object that contains
 * only described content
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class BoringBuzzy extends CompositeContent
{
    /**
     * Default Constructor
     */
    public BoringBuzzy()
    {
       super();
       Arc2D.Float            helmetShape, visorShape;       
       BasicStroke            stroke;
       Color                  black, gold, gray, purple;
       CompositeContent       head;       
       Content                body, hair, helmet, visor;
       Path2D.Float           bodyShape;       
       QuadCurve2D.Float      hairShape;
       

       black  = Color.BLACK;
       gold   = new Color(0xc2,0xa1,0x4d);
       gray   = new Color(0xaa,0xaa,0xaa);
       purple = new Color(0x45,0x00,0x84);

       stroke = new BasicStroke();

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


       body = new Content(bodyShape, black, purple, stroke);
       add(body);

       head = new CompositeContent();
       add(head);

       hairShape = new QuadCurve2D.Float(10,2,40,10,30,25);       
       hair = new Content(hairShape, purple, null, stroke);
       head.add(hair);

       helmetShape = new Arc2D.Float(2,20,70,40,2,360,Arc2D.OPEN);
       helmet=new Content(helmetShape, black, gold, stroke);
       head.add(helmet);

       visorShape = new Arc2D.Float(40,25,35,30,315,90,Arc2D.PIE);
       visor=new Content(visorShape, black, gray, stroke);
       head.add(visor);
    }
}
        
Examples (cont.)

Mixed Content

javaexamples/visual/statik/FancyBuzzy.java
        package visual.statik;


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


/**
 * An example of CompositeContent
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class FancyBuzzy extends visual.statik.CompositeContent
{
    /**
     * Default Constructor
     */
    public FancyBuzzy()
    {
       super();
       Arc2D.Float                           helmetShape, visorShape;
       BasicStroke                           stroke;
       Color                                 black, gold, gray, purple;
       Path2D.Float                          bodyShape;       
       QuadCurve2D.Float                     hairShape;

       visual.statik.CompositeContent        head;       
       visual.statik.described.Content       body, hair, helmet, visor;
       visual.statik.sampled.Content         screw;       
       visual.statik.sampled.ContentFactory  factory;

       black  = Color.black;
       gold   = new Color(0xc2,0xa1,0x4d);
       gray   = new Color(0xaa,0xaa,0xaa);
       purple = new Color(0x45,0x00,0x84);

       stroke = new BasicStroke();


       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();
       body = new visual.statik.described.Content(bodyShape, 
                                                  black, purple, 
                                                  stroke);
       add(body);


       helmetShape = new Arc2D.Float(2,20,70,40,2,360,Arc2D.OPEN);
       helmet=new visual.statik.described.Content(helmetShape, 
                                                  black, gold, stroke);


       factory = new visual.statik.sampled.ContentFactory();
       screw = factory.createContent("/visual/statik/screw.gif", 4);


       visorShape = new Arc2D.Float(40,25,35,30,315,90,Arc2D.PIE);
       visor=new visual.statik.described.Content(visorShape, 
                                                       black, gray, stroke);



       hairShape = new QuadCurve2D.Float(10,2,40,10,30,25);       
       hair = new visual.statik.described.Content(hairShape, 
                                                  purple, null, stroke);
       add(hair);


       head = new visual.statik.CompositeContent();
       head.add(hair);       
       head.add(helmet);
       head.add(screw);
       head.add(visor);
       
       add(head);

    }

}
        
Examples (cont.)

A Visualization

javaexamples/visual/statik/VisualizationApp.java
        package visual.statik;


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

import app.*;
import visual.*;
import visual.statik.sampled.*;


/**
 * An example that illustrates the use of sampled and described
 * content in the same visualization
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 *
 */
public class   VisualizationApp
       extends AbstractMultimediaApp
{
    /**
     * The entry point of the application
     */
    public void init()
    {
       BufferedImageOpFactory        opFactory;       
       Content                       woods;
       ContentFactory                factory;
       FancyBuzzy                    buzzy;       
       JPanel                        contentPane;       
       Visualization                 visualization;
       VisualizationView             view;
       


       
       factory   = new ContentFactory();       
       opFactory = BufferedImageOpFactory.createFactory();

       woods  = factory.createContent("/visual/statik/woods.gif", 
                                      3);
       woods.setLocation(0,0);
       woods.setBufferedImageOp(opFactory.createBlurOp(3));

       buzzy  = new FancyBuzzy();
       buzzy.setLocation(200, 318);       

       visualization = new Visualization();
       view          = visualization.getView();

       view.setBounds(0,0,471,418);     
       view.setSize(471,418);       
       
       visualization.add(woods);
       visualization.add(buzzy);

       // The content pane
       contentPane = (JPanel)rootPaneContainer.getContentPane();
       contentPane.add(view);
    }
}
        
Examples (cont.)

Picture-in-a-Picture

javaexamples/visual/statik/PIPApp.java
        package visual.statik;


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

import app.*;
import visual.*;
import visual.statik.sampled.*;


/**
 * An example that illustrates the use of multiple views
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 *
 */
public class   PIPApp
       extends AbstractMultimediaApp
{
    /**
     * The entry point of the application
     */
    public void init()
    {
       BufferedImageOpFactory        opFactory;       
       Content                       woods, house;
       ContentFactory                factory;
       FancyBuzzy                    buzzy;       
       JPanel                        contentPane;       
       Visualization                 model1, model2;
       VisualizationRenderer         renderer1, renderer2;       
       VisualizationView             view1, view2;
       
       factory   = new ContentFactory();       
       opFactory = BufferedImageOpFactory.createFactory();

       woods  = factory.createContent("/visual/statik/woods.gif", 
                                      3);
       woods.setLocation(0,0);
       woods.setBufferedImageOp(opFactory.createBlurOp(3));

       buzzy  = new FancyBuzzy();
       buzzy.setLocation(200, 318);       

       model1 = new Visualization();
       model1.add(woods);
       model1.add(buzzy);

       view1     = model1.getView();
       renderer1 = view1.getRenderer();       
       view1.setRenderer(new ScaledVisualizationRenderer(renderer1, 
                                                         471.0, 
                                                         418.0));       
       view1.setBounds(0,0,471,418);     
       view1.setSize(471,418);       


       house = factory.createContent("/visual/statik/house.gif", 
                                      3);
       house.setLocation(0,0);

       model2 = new Visualization();
       model2.add(house);

       view2     = model2.getView();
       renderer2 = view2.getRenderer();       
       view2.setRenderer(new ScaledVisualizationRenderer(renderer2, 
                                                         525.0, 
                                                         375.0));       
       view2.setBounds(50,50,160,120);     
       view2.setSize(160,120);       
       
       // The content pane
       contentPane = (JPanel)rootPaneContainer.getContentPane();
       contentPane.add(view2);
       contentPane.add(view1);

    }
}