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