|
A Flexible Static Visual Content System
|
|
Prof. David Bernstein |
| Computer Science Department |
| bernstdh@jmu.edu |
Content
object can only contain one ``piece'' of visual content.sampled.Content object that
used one BufferedImage
as a character's head and another
BufferedImage as its bodydescribed.Content object
that encapsulates, for example, a house with a door and
roof that are different colorsOne Obvious Design
Using the Composite Pattern
Three "Copies" of the Composite
One Composite
Using an Abstract Parent
Using a Parameterized Parent
visual.statik Package
AbstractAggregateContent
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>();
}
}
/**
* 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);
}
/**
* 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);
}
}
/**
* 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
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
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
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);
}
}
}
described.CompositeContent
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);
}
}
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);
}
}
Visualization
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);
}
}
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);
}
}