JMU
Visual Content
An Introduction


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


Visible Light
Visible Light (cont.)

An EM Wave

em-wave
Visible Light (cont.)
Light Sources and Reflected Light
Human Vision

The Eye

eye
Human Vision (cont.)
Human Vision (cont.)
Visual Perception
Visual Perception (cont.)
Visual Output Devices
Coordinate Systems
Coordinate Systems (cont.)
Coordinate Systems (cont.)
Modeling Color

The (Linear) Color Cube

rgb-cube
Modeling Color (cont.)
Rendering
Rendering (cont.)
Coordinate Transforms

A Translation of (25,25)

coordinates_translation
Coordinate Transforms (cont.)

A Rotation of \(- \pi / 6\) Radians

coordinates_rotation
Coordinate Transforms (cont.)

A Scaling of (1.5, 1.5)

coordinates_scaling
Coordinate Transforms (cont.)

A Reflection About the Horizontal Axis

coordinates_reflection
Clipping
Composition
Composition (cont.)
Obtaining a Rendering Engine in Java
Obtaining a Rendering Engine in Java (cont.)

An Example

javaexamples/visual/BoringComponent.java
import java.awt.*;
import javax.swing.*;


/**
 * A concrete extension of a JComponent that illustrates
 * the process of obtaining and using a rendering engine
 *
 * @version 1.0
 * @author  Prof. David Bernstein, James Madison University
 */
public class BoringComponent extends JComponent
{

    /**
     * Render this BoringComponent
     *
     * @param g   The rendering engine to use
     */
    public void paint(Graphics g)
    {
       Graphics2D         g2;
       
       // Cast the rendering engine appropriately
       g2 = (Graphics2D)g;
       

       // Put the rendering code here
    }

}

        
Obtaining a Rendering Engine in Java (cont.)
Designing a Visual Content System
Designing a Visual Content System (cont.)

A Common Design

visualization_design1
Designing a Visual Content System (cont.)

A Design with a Problem

visualization_design2a
Designing a Visual Content System (cont.)

A Better Design

visualization_design2
Designing a Visual Content System (cont.)

A Design that Satisfies Almost All of the Requirements

visualization_design3a
Designing a Visual Content System (cont.)

"Enhancing" the View with Specialization

visualization_design3b
Designing a Visual Content System (cont.)

A Good Design

visualization_design3
Implementation

The SimpleContent Interface

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

import java.awt.*;

/**
 * The requirements of simple static visual content
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public interface SimpleContent
{
    /**
     * Render this static visual content
     *
     * @param g2   The rendering engine to use
     */
    public abstract void render(Graphics g);    
}
        
Implementation (cont.)

The Visualization Class

Skeleton:

javaexamples/visual/Visualization.java (Fragment: 1)
package visual;

import java.awt.event.*;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

import visual.statik.SimpleContent;


/**
 * A collection of Content objects to be rendered
 * (and a collection of GUI components that will
 * render them)
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class Visualization
{
    private   CopyOnWriteArrayList<SimpleContent> content;
    private   LinkedList<VisualizationView>       views;
    

    /**
     * Default Constructor
     */
    public Visualization()
    {
       content = new CopyOnWriteArrayList<SimpleContent>();
    
       views   = new LinkedList<VisualizationView>();

       views.addFirst(createDefaultView());       
    }

}
        

Content Management:

javaexamples/visual/Visualization.java (Fragment: content)
    /**
     * Add a SimpleContent to the "front" of this Visualization
     *
     * Note: This method only adds the SimpleContent if it is not
     * already in the Visualization.  However, the "underlying"
     * content (e.g., BufferedImage, Shape) in two different
     * SimpleContent objects can be the same.
     *
     * To change the order of a SimpleContent that is already on the canvas
     * use toBack() or toFront().
     * 
     * @param r   The SimpleContent to add
     */
    public void add(SimpleContent r)
    {
       if (!content.contains(r))
       {
          content.add(r);
          repaint();
       }
    }

    /**
     * Clear this Visualization of all SimpleContent objects
     */
    public void clear()
    {
       content.clear();
    }

    /**
     * Get an Iterator that contains all of the SimpleContent
     * objects
     *
     * @return   The SimpleContent objects
     */
    public Iterator<SimpleContent> iterator()
    {
       return content.iterator();       
    }


    /**
     * Remove the given SimpleContent from this Canvas
     *
     * @param r    The SimpleContent to remove
     */
    public void remove(SimpleContent r)
    {
       if (content.remove(r)) repaint();
    }
        
javaexamples/visual/Visualization.java (Fragment: move)
    /**
     * Move the given SimpleContent to the "back"
     *
     * Note: The SimpleContent must have already been added for this
     * method to have an effect.
     *
     * @param r   The SimpleContent to move to the back
     */
    public void toBack(SimpleContent r)
    {
       boolean         removed;
       
       removed = content.remove(r);
       if (removed)
       {
          content.add(r);
       }
    }

    /**
     * Move the given SimpleContent to the "front".
     *
     * Note: The SimpleContent must have already been added for this
     * method to have an effect.
     *
     * @param r   The SimpleContent to move to the front
     */
    public void toFront(SimpleContent r)
    {
       boolean         removed;
       
       removed = content.remove(r);
       if (removed)
       {
          content.add(0, r);
       }
    }
        

View Management:

javaexamples/visual/Visualization.java (Fragment: views)
    /**
     * Add a view to this Visualization
     *
     * @param view    The view to add
     */
    public void addView(VisualizationView view)
    {
       views.addLast(view);       
    }

    /**
     * Get the "main" view (i.e., the "main" VisualizationView
     * that is used to present the SimpleContent objects) associated
     * with this Visualization.
     *
     * Note: A visualization can actually have multiple
     *       views associated with it.  This is a convenience
     *       method that can be used when there is only one such
     *       view.
     *
     * @return   The view
     */
    public VisualizationView getView()
    {
       return views.getFirst();       
    }



    /**
     * Get all of the views associated with this Visualization
     *
     * @return   The views
     */
    public Iterator<VisualizationView> getViews()
    {
       return views.iterator();       
    }

    /**
     * Remove a view from this Visualization
     *
     * @param view    The view to remove
     */
    public void removeView(VisualizationView view)
    {
       views.remove(view);       
    }

    /**
     * Change the "main" view associated with this Visualization
     *
     * Note: A visualization can actually have multiple
     *       views associated with it.  This is a convenience
     *       method that can be used when there is only one such
     *       view.
     *
     * @param view   The new view to use as the "main" view
     */
    public void setView(VisualizationView view)
    {
       views.removeFirst();
       views.addFirst(view);       
    }
        

Forcing Rendering:

javaexamples/visual/Visualization.java (Fragment: repaint)
    /**
     * Repaint the view(s) assocaited with this Visualization
     */
    protected  void repaint()
    {
       Iterator<VisualizationView>   i;       
       VisualizationView             view;
       
       i = views.iterator();
       while (i.hasNext())
       {
          view = i.next();
          view.repaint();
       }
    }
        
Implementation (cont.)

The VisualizationView Class

Skeleton:

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


import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;

import visual.statik.*;

/**
 * A GUI component that presents a collection of SimpleContent
 * objects
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class      VisualizationView
       extends    JComponent
       implements MouseListener
{

    protected Visualization         model;
    protected VisualizationRenderer renderer;
    

    /**
     * Explicit Value Constructor
     *
     * @param model     The Visualization to use
     * @param renderer  The VisualizationRenderer to use
     */
    public VisualizationView(Visualization         model,
                             VisualizationRenderer renderer)
    {
       super();
       this.model    = model;       
       this.renderer = renderer;       

    }

    /**
     * Handle updates
     *
     * This is overriden so that the background is not
     * erased before painting (which is the default)
     */
    public void update(Graphics g)
    {
       paint(g);
    }

    
}
        

Rendering:

javaexamples/visual/VisualizationView.java (Fragment: render)
    /**
     * Set the VisualizationRenderer that this component
     * should use when rendering
     *
     * @param renderer  The VisualizationRenderer
     */
    public void setRenderer(VisualizationRenderer renderer)
    {
       this.renderer = renderer;       
    }
    

    
    /**
     * Operations to perform after rendering.
     * This method is called by paint().
     *
     * @param g   The rendering engine
     */
    protected void postRendering(Graphics g)
    {
       renderer.postRendering(g, model, this);
    }
    

    /**
     * Operations to perform before rendering.
     * This method is called by paint().
     *
     * @param g   The rendering engine
     */
    protected void preRendering(Graphics g)
    {
       renderer.preRendering(g, model, this);
    }
    

    /**
     * Render the content contained in the model.
     * This method is called by paint().
     *
     * @param g   The rendering engine
     */
    protected void render(Graphics g)
    {
       renderer.render(g, model, this);
    }
        
Implementation (cont.)

The PlainVisualizationRenderer Class

javaexamples/visual/PlainVisualizationRenderer.java (Fragment: render)
    /**
     * Render the content contained in the model.
     * This method is called by paint().
     *
     * @param g   The rendering engine
     */
    public void render(Graphics          g,
                       Visualization     model,
                       VisualizationView view)
    {

       Iterator<SimpleContent>   iter;
       SimpleContent             c;

       iter = model.iterator();
       while (iter.hasNext())
       {
          c  = iter.next();
          if (c != null) c.render(g);
       }
    }
        
Implementation (cont.)

The ScaledVisualizationRenderer Class

Skeleton:

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

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


/**
 * A decorator of a VisualizationRenderer that adds
 * scaling capabilities.  That is, a ScaledVisualizationRenderer
 * will scale all of the content to fit its component.
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class      ScaledVisualizationRenderer
       implements VisualizationRenderer
{
    private double                  height, scaleX, scaleY, width;       
    private VisualizationRenderer   decorated;
    

    /**
     * Explicit Value Constructor
     *
     * @param decorated  The VisualizatioRenderer to decorate
     * @param width      The full-size width of the content
     * @param height     The full-size height of the content
     */
    public ScaledVisualizationRenderer(VisualizationRenderer decorated,
                                       double width, double height)
    {
       this.decorated = decorated;       
       this.width     = width;
       this.height    = height;       
    }
    

    /**
     * Render the content contained in the model.
     *
     * @param g      The rendering engine
     * @param model  The Visualization containing the content
     * @param view   The component presenting the content
     */
    public void render(Graphics          g,
                       Visualization     model,
                       VisualizationView view)
    {
       decorated.render(g, model, view);       
    }
}
        

Before Rendering:

javaexamples/visual/ScaledVisualizationRenderer.java (Fragment: preRendering)
    

    /**
     * Operations to perform before rendering.
     *
     * @param g      The rendering engine
     * @param model  The Visualization containing the content
     * @param view   The component presenting the content
     */
    public void preRendering(Graphics          g,
                             Visualization     model,
                             VisualizationView view)
    {
       Dimension         size;       
       Graphics2D        g2;
       
       

       g2   = (Graphics2D)g;       
       size = view.getSize();
       scaleX = size.getWidth()  / width;
       scaleY = size.getHeight() / height;

       g2.scale(scaleX, scaleY);

       decorated.preRendering(g, model, view);       
    }
        

After Rendering:

javaexamples/visual/ScaledVisualizationRenderer.java (Fragment: postRendering)
    
    /**
     * Operations to perform after rendering.
     *
     * @param g      The rendering engine
     * @param model  The Visualization containing the content
     * @param view   The component presenting the content
     */
    public void postRendering(Graphics          g,
                              Visualization     model,
                              VisualizationView view)
    {
       Graphics2D   g2;
       
       g2 = (Graphics2D)g;       
       g2.scale(1.0/scaleX, 1.0/scaleY);

       decorated.postRendering(g, model, view);       
    }
        
Implementation (cont.)

The PartialVisualizationRenderer Class

Skeleton:

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

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

/**
 * A decorator of a VisualizationRenderer that 
 * only shows part of the content.
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class      PartialVisualizationRenderer
       implements VisualizationRenderer
{
    private double                  x, y;       
    private VisualizationRenderer   decorated;
    

    /**
     * Explicit Value Constructor
     *
     * @param decorated  The VisualizatioRenderer to decorate
     * @param x          The left-most point to render
     * @param y          The upper-most point to render
     */
    public PartialVisualizationRenderer(VisualizationRenderer decorated,
                                        double x, double y)
    {
       this.decorated = decorated;       
       this.x         = x;
       this.y         = y;       
    }

    /**
     * Render the content contained in the model.
     *
     * @param g      The rendering engine
     * @param model  The Visualization containing the content
     * @param view   The component presenting the content
     */
    public void render(Graphics          g,
                       Visualization     model,
                       VisualizationView view)
    {
       decorated.render(g, model, view);       
    }
}
        

Before Rendering:

javaexamples/visual/PartialVisualizationRenderer.java (Fragment: preRendering)
    

    /**
     * Operations to perform before rendering.
     *
     * @param g      The rendering engine
     * @param model  The Visualization containing the content
     * @param view   The component presenting the content
     */
    public void preRendering(Graphics          g,
                             Visualization     model,
                             VisualizationView view)
    {
       Graphics2D   g2;
       
       g2 = (Graphics2D)g;       
       g2.translate(-x, -y);
       decorated.preRendering(g, model, view);
    }
        

After Rendering:

javaexamples/visual/PartialVisualizationRenderer.java (Fragment: postRendering)
    
    /**
     * Operations to perform after rendering.
     *
     * @param g      The rendering engine
     * @param model  The Visualization containing the content
     * @param view   The component presenting the content
     */
    public void postRendering(Graphics          g,
                              Visualization     model,
                              VisualizationView view)
    {
       Graphics2D   g2;
       
       g2 = (Graphics2D)g;       
       g2.translate(x, y);
       decorated.postRendering(g, model, view);       
    }
        
Designing a Visual Content System (cont.)
Designing a Visual Content System (cont.)

Adding Transformations using a Helper

Transformable_design3
Designing a Visual Content System (cont.)

Adding Transformations using Stand-Alone Interfaces

Transformable_design1
Designing a Visual Content System (cont.)

Adding Transformations using Individual Interfaces

Transformable_design2
Designing a Visual Content System (cont.)

The Best Way to Add Transformations

TransformableContent
Implementation (cont.)

The AbstractTransformableContent Class

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

import java.awt.geom.*;


/**
 * A partial implementation of the TransformableContent
 * interface that keeps track of transformation information
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public abstract class AbstractTransformableContent 
                implements TransformableContent
{

    protected boolean          relocated, rerotated, rescaled;
    protected double           angle;
    protected double           xScale, yScale;
    protected double           x, y;    
    protected double           xRotation, yRotation;    


    /**
     * Default Constructor
     */
    public AbstractTransformableContent()
    {
       setTransformationRequired(false);
       
       angle     = 0.0;
       xScale    = 1.0;
       yScale    = 1.0;
       x         = 0.0;
       y         = 0.0;
       xRotation = 0.0;
       yRotation = 0.0;
    }

    /**
     * Get the (concatenated) AffineTransform
     *
     * @return  The rotation, scaling and translation concatenated
     */
    protected AffineTransform getAffineTransform()
    {
       // We could use an object pool rather than local 
       // variables.  Rough tests estimate that this method 
       // would require 1/3 as much time with an object pool.
       AffineTransform     at, rotation, scaling, translation;
       Rectangle2D         bounds;

       // Start with the identity transform
       at = AffineTransform.getTranslateInstance(0.0, 0.0);

       if (rerotated)
       {
          bounds = getBounds2D(false);
          
          
          rotation = AffineTransform.getRotateInstance(angle, 
                                                   xRotation,
                                                   yRotation);
          at.preConcatenate(rotation);
       }

       
       if (rescaled)
       {          
          scaling = AffineTransform.getScaleInstance(xScale, 
                                                     yScale);
          at.preConcatenate(scaling);
       }
       

       if (relocated)
       {
          translation = AffineTransform.getTranslateInstance(x, 
                                                             y);
          at.preConcatenate(translation);
       }

       return at;
    }
    
    /**
    * Returns a high precision bounding box of the appropriately
    * transformed content (required by TransformedContent) 
    *
    * @return   The bounding box
    */
    public Rectangle2D getBounds2D()
    {
       return getBounds2D(true);       
    }
    

   
    /**
     * 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 abstract Rectangle2D getBounds2D(boolean transformed);
       

    /**
     * Set the translation
     * (required by TransformedContent)
     *
     * @param x   The x posiiton
     * @param y   The y position
     */
    public void setLocation(double x, double y)
    {
       this.x = x;
       this.y = y;
       relocated = true;       
    }


    /**
     * Set the angle of rotation around the midpoint
     * (required by TransformedContent)
     *
     * @param angle   The rotation angle
     * @param x       The x-coordinate of the point to rotate around
     * @param y       The y-coordinate of the point to rotate around
     */
    public void setRotation(double angle, double x, double y)
    {
       this.angle = angle;
       xRotation  = x;
       yRotation  = y;       
       rerotated  = true;       
    }
    

    /**
     * Set the scaling
     * (required by TransformedContent)
     *
     * @param xScale   The scaling in the horizontal dimension
     * @param yScale   The scaling in the vertical  dimension
     */
    public void setScale(double xScale, double yScale)
    {
       this.xScale = xScale;
       this.yScale = yScale;
       rescaled    = true;       
    }
    

    /**
     * Set the scaling
     *
     * @param scale   The scaling (in both dimensions)
     */
    public void setScale(double scale)
    {
       setScale(scale, scale);
    }


    /**
     * Set whether this AbstractTransformableContent object
     * needs to be transformed before being used
     *
     * @param required   true to indicate that a transformation is requried
     */
    protected void setTransformationRequired(boolean required)
    {
       relocated = required;
       rerotated = required;
       rescaled  = required;
    }
    

    /**
     * Does this AbstractTransformableContent object need
     * to be transformed before being used?
     *
     * @return  true to indicate a transformation is required
     */
    protected boolean isTransformationRequired()
    {
       return (relocated || rerotated || rescaled);
    }
    
}