Project SAIL: Student Authored Interactive Lessons
Java
code
Application.java
-------------------------------------------------------------------
/*
* Created on
*/
/**
* @author Andrew Owens
*/
import javax.swing.JFrame;
import java.awt.BorderLayout;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.util.*;
/**
* Application Creates and initializes each component of the system.
*/
public class Application extends JFrame {
public static void main( String[] args ) {
new Application( null );
}
public Application( SailApplet sailApplet ) {
applet = sailApplet;
//initApp();
initGUI();
initHQ();
isInitialized = true;
updateAll();
repaint();
}
private boolean isInitialized;
private JApplet applet;
private Container cp;
/////////////////////////////////////////////
// Mode Management
/////////////////////////////////////////////
public void initInstructionMode( ) {
exitCurrentMode();
mode = new InstructionMode( this, cp );
refreshMode();
}
public void initTestMode() {
exitCurrentMode();
mode = new TestMode( this );
refreshMode();
}
public void initExplorationMode() {
exitCurrentMode();
mode = new ExplorationMode( this, cp );
refreshMode();
}
public void initHQ() {
exitCurrentMode();
mode = new SelectionMode( this, cp );
refreshMode();
}
private void exitCurrentMode() {
cp.removeAll();
//cp.add( new JLabel("asdf"));
}
// fixes that repainting bug somehow...
private void refreshMode() {
if ( applet == null ) {
setVisible( true );
}
else {
}
}
/**
* Initialize the application's GUI/frame
*/
private Container initGUI() {
if ( applet != null ) {
cp = applet.getContentPane();
}
else {
setDefaultLookAndFeelDecorated( true );
setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
setTitle( APP_TITLE );
setSize( APP_WIDTH, APP_HEIGHT );
setResizable( IS_RESIZABLE );
cp = getContentPane();
}
if ( applet == null ) {
setVisible( true );
}
return cp;
}
/*
* private void onTimer() { updateAll(); }
*/
public void updateAll() {
if ( isInitialized ) {
mode.updateAll();
}
}
public class TestMode implements Mode {
public TestMode( Application app ) {
testPanel = new TestPanel( app );
cp.add( testPanel );
}
public void updateAll() {
}
TestPanel testPanel;
}
public class ExplorationMode implements Mode {
public ExplorationMode( Application app, Container cp ) {
add( new ExplorationScreen( app, cp ) );
}
public void updateAll() {
}
}
public class InstructionMode implements Mode, GraphCoordinator {
public InstructionMode( Application app, Container cp ) {
Point bottomLeft = new Point( -100, -100 );
Point upperRight = new Point( getWidth() + bottomLeft.getX() - 205, getHeight()
);
configManager = new ConfigManager( this );
pointTable = new PointTable( this );
statManager = new StatManager( this, pointTable );
grapher = new Grapher( configManager, statManager, pointTable, bottomLeft,
upperRight );
panelSeparator = new PanelSeparator( app, configManager, statManager,
pointTable );
cp.add( panelSeparator, BorderLayout.EAST );
cp.add( grapher.getGraph() );
cp.addComponentListener( new ResizeListener() );
isInitialized = true;
}
public void updateAll() {
updateGraph();
}
public void updateGraph() {
if ( isInitialized ) {
grapher.updateGraph( true );
panelSeparator.updatePanels();
pointTable.updateState();
}
}
private boolean isInitialized;
private ConfigManager configManager;
private StatManager statManager;
private Grapher grapher;
private PanelSeparator panelSeparator;
private PointTable pointTable;
public class ResizeListener implements ComponentListener {
public void componentShown( ComponentEvent e ) {
updateAll();
}
public void componentMoved( ComponentEvent e ) {
}
public void componentResized( ComponentEvent e ) {
grapher.resizeGraph( new Point( getWidth() - PANEL_WIDTH, getHeight() ) );
}
public void componentHidden( ComponentEvent e ) {
}
}
}
public class SelectionMode implements Mode {
public SelectionMode( Application app, Container cp ) {
cp.add( new SelectionScreen( app, cp ) );
}
public void updateAll() {
}
}
// mode enumerations
private static final int MODE_INVALID_FIRST = 0;
private static final int MODE_EXPLORATION = 1;
private static final int MODE_TEST = 2;
private static final int MODE_HOMEWORK = 3;
private static final int MODE_INSTRUCTION = 4;
private static final int MODE_LAST = 4;
private Mode mode;
// are we selecting a mode?
private static final String APP_TITLE = "Linear Regression";
private static final int APP_WIDTH = 780;
private static final int APP_HEIGHT = 500;
private static final boolean IS_RESIZABLE = true;
private static final int TIMER_DELAY = 100;
private static final int PANEL_WIDTH = 250;
}
ColorSchemeManager.java
-------------------------------------------------------------------
/*
* Created on Apr 5, 2005
*/
/**
* @author Andrew Owens
*/
/**
* ColorSchemeManager
* Provides centralized, static access for
* color scheme
*/
import java.awt.Color;
public class ColorSchemeManager {
public static final Color REGRESSION_LINE = Color.BLUE;
public static final Color CUSTOM_REGRESSION = Color.RED;
public static final Color GHOST_REGRESSION_LINE = new Color( 0, 0, 200, 100 );
public static final Color AXIS = new Color( 50, 50, 50, 100 );
public static final Color GRID = new Color( 200, 200, 200, 100 );
public static final Color DEVATION = new Color( 0, 128, 0 );
public static final Color RESIDUAL = new Color( 0, 128, 0 );
public static final Color GRAPH_TEXT = new Color( 0, 0, 0 );
//public static final Color DEVATION_SQUARE =
//public static final Color RESIDUAL_SQUARE =
}
ConfigEditor.java
-------------------------------------------------------------------
/*
* Created on Mar 23, 2005
*/
/**
* @author Andrew Owens
*/
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.JCheckBox;
import javax.swing.BoxLayout;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.Iterator;
public class ConfigEditor extends Panel implements ItemListener {
public ConfigEditor( ConfigManager newConfigManager ) {
settingConversions = new HashMap();
configManager = newConfigManager;
checkBoxes = new ArrayList();
initGUI();
loadDefaultSettings();
}
public String getName() {
return PANEL_NAME;
}
private void initGUI() {
setLayout( new BoxLayout( this, BoxLayout.Y_AXIS ) );
// the user will probably never change these
/*
for ( int i = 0; i < STATPANEL_SETTINGS.length; i++ ) {
// get the text of the checkbox
String checkBoxText = STATPANEL_SETTINGS[ i ][ 0 ];
// get the text of the corresponding setting-key
String settingKey = STATPANEL_SETTINGS[ i ][ 1 ];
// make the CheckBox
JCheckBox checkBox = new JCheckBox( checkBoxText );
checkBox.addItemListener( this );
// add the checkBoxTest (as the key) and the settingKey (as the
// value)
// in a hash map so we can convert them later
settingConversions.put( checkBoxText, settingKey );
configManager.setSetting( settingKey, new Boolean( false ) );
checkBoxes.add( checkBox );
// add the checkbox to the panel
add( checkBox );
}
add( new JSeparator() );
*/
for ( int i = 0; i < GRAPHER_SETTINGS.length; i++ ) {
// get the text of the checkbox
String checkBoxText = GRAPHER_SETTINGS[ i ][ 0 ];
// get the text of the corresponding setting-key
String settingKey = GRAPHER_SETTINGS[ i ][ 1 ];
// make the CheckBox
JCheckBox checkBox = new JCheckBox( checkBoxText );
checkBox.addItemListener( this );
// add the checkBoxTest (as the key) and the settingKey (as the
// value)
// in a hash map so we can convert them later
settingConversions.put( checkBoxText, settingKey );
configManager.setSetting( settingKey, new Boolean( false ) );
checkBoxes.add( checkBox );
// add the checkbox to the panel
add( checkBox );
}
}
public void updateContents() {
// there haven't been any modifications in the ConfigManager, so we'll update
its
// state to reflect its un-updated status
configManager.updateState();
}
public void itemStateChanged( ItemEvent e ) {
// get the item that was changed
JCheckBox checkBoxSrc = (JCheckBox) e.getItemSelectable();
// find the check box's text
String selectedText = checkBoxSrc.getText();
// using this text, find the setting that the box corresponds to
String keyConversion = (String) settingConversions.get( selectedText );
// checkboxes can only change boolean values so we'll
// change this setting in the ConfigManager to the value in
// the box
configManager.setSetting( keyConversion, new Boolean( checkBoxSrc.isSelected()
) );
}
private void loadDefaultSettings() {
for ( int i = 0; i < DEFAULT_CHECKED_SETTINGS.length; i++ ) {
String settingKey = DEFAULT_CHECKED_SETTINGS[ i ];
configManager.setSetting( settingKey, new Boolean( true ) );
// go through each check box, if it matches this
// one, then check it
Iterator checkBoxIt = checkBoxes.iterator();
while ( checkBoxIt.hasNext() ) {
JCheckBox checkBox = (JCheckBox) checkBoxIt.next();
// get the text of the box
// so that we can convert it to
// its corresponding setting
String checkBoxText = checkBox.getText();
// convert the check box text to
// its corresponding setting
String settingKeyCheckBox = (String)settingConversions.get( checkBoxText );
if ( settingKey.equals( settingKeyCheckBox ) ) {
checkBox.setSelected( true );
}
}
}
}
private ConfigManager configManager;
private HashMap settingConversions;
/*
private static final String[][] STATPANEL_SETTINGS = {
// StatPanel options
{ "Show Equation",
"ShowEquation" },
{ "Show SSres",
"ShowSSres" },
{ "Show SSdev",
"ShowSSdev" },
{ "Show r", "ShowR"
},
{ "Show r2",
"ShowRSquared" },
{ "Show x and y",
"ShowXYBars" }};
*/
// Graph options
private static final String[][] GRAPHER_SETTINGS = {
{ "Plot ŷ",
"PlotYHat" },
{ "Plot Residuals",
"PlotResiduals" },
{ "Plot x and y",
"PlotXYBars" },
{ "Plot Deviation Lines",
"PlotDevLines" },
{ "Plot Squares of Residuals",
"PlotResSquares" },
{ "Plot Squares of Deviations",
"PlotDevSquares" }};
// TODO:
// Load the defaults from a file or something instead of relying on this
// hackish way of setting them
private static final String[] DEFAULT_CHECKED_SETTINGS = {
"ShowEquation",
"ShowSSres",
"ShowSSdev",
"ShowR",
"ShowRSquared",
"ShowXYBars",
"PlotYHat",
"PlotXYBars"};
private ArrayList checkBoxes;
private boolean isUpdated;
private static final String PANEL_NAME = "Settings";
}
ConfigManager.java
-------------------------------------------------------------------
/*
* Created on Mar 24, 2005
*/
/**
* @author Andrew Owens
*/
import java.util.HashMap;
public class ConfigManager {
ConfigManager(GraphCoordinator newCoordinator ) {
settings = new HashMap();
coordinator = newCoordinator;
}
public Setting querySetting( String key ) {
if ( settings.containsKey( key ) ) {
return new Setting( settings.get( key ) );
}
else {
//return new Setting();
// @hack
return new Setting( new Boolean( false ) );
}
}
public void setSetting( String key, Object value ) {
settings.put( key, value );
makeUpdated();
}
public boolean isUpdated() {
return isUpdated;
}
public void updateState() {
isUpdated = false;
}
private void makeUpdated() {
isUpdated = true;
coordinator.updateGraph();
}
private HashMap settings;
private boolean isUpdated;
private GraphCoordinator coordinator;
}
EmbeddedGraph.java
-------------------------------------------------------------------
/*
* Created on Apr 14, 2005
*/
/*
* @author Andrew Owens
*/
import javax.swing.*;
public class EmbeddedGraph extends JPanel implements GraphCoordinator {
public EmbeddedGraph( Point newBottomLeft, Point newUpperRight, Point[] points,
String[] settings ) {
pointTable = new PointTable( this );
statManager = new StatManager( this, pointTable );
configManager = new ConfigManager( this );
grapher = new Grapher( configManager, statManager, pointTable, newBottomLeft,
newUpperRight );
for ( int i = 0; i < points.length; i++ ) {
pointTable.addPoint( points[ i ] );
}
Boolean boolTrue = new Boolean( true );
for ( int i = 0; i < settings.length; i++ ) {
configManager.setSetting( settings[ i ], boolTrue );
}
add( grapher.getGraph() );
isInitialized = true;
}
public void updateGraph() {
if ( isInitialized ) {
grapher.updateGraph( false );
}
}
//
public Graph getGraph() {
return grapher.getGraph();
}
private boolean isInitialized;
private Grapher grapher;
private Graph graph;
private PointTable pointTable;
private StatManager statManager;
private ConfigManager configManager;
}
ExplorationScreen.java
-------------------------------------------------------------------
/*
* Created on Apr 11, 2005
*/
/**
* @author Andrew Owens
*/
import java.awt.*;
import javax.swing.*;
import java.util.*;
public class ExplorationScreen extends JPanel {
public ExplorationScreen( Application app, Container cp ) {
contentPane = cp;
application = app;
initGUI();
}
private void initGUI() {
String[] settings = { "PlotSSres" };
Point[] points = { new Point(50, 50 ), new Point(20, 40 ) };
EmbeddedGraph embeddedGraph = new EmbeddedGraph( new Point(-10, -10), new
Point(500, 500), points, settings );
add( embeddedGraph.getGraph() );
}
private void loadStep( ExplorationStep step ) {
}
private ArrayList steps;
private Container contentPane;
private Application application;
}
ExplorationStep.java
-------------------------------------------------------------------
/*
* Created on Apr 11, 2005
*/
/*
* @author Andrew Owens
*/
public class ExplorationStep {
public ExplorationStep( String newDescription, int newEditableRow, int
newEditableCol, int newLastsShowCol ) {
}
private String description;
private int editableRow;
private int editableCol;
private int lastShowCol;
}
Graph.java
-------------------------------------------------------------------
/*
* Created on Mar 23, 2005
*/
/**
* @author Andrew Owens
*/
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.Dimension;
import java.awt.Color;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
public class Graph extends JPanel {
public Graph( Grapher newGrapher, Point newLowerLeft, Point newUpperRight ) {
lowerLeft = newLowerLeft;
upperRight = newUpperRight;
grapher = newGrapher;
initGUI();
loadImages();
firstPaint = true;
}
public void resizeGraph( Point newBottomLeft, Point newUpperRight ) {
lowerLeft = newBottomLeft;
upperRight = newUpperRight;
firstPaint = true;
}
public void paintComponent( Graphics g ) {
// TODO: I can only seem to create this backbuffer
// during the paint method and this seems
// to result in an occasional NullPointerException
// when it gets updated too fast
// if this is the first paint, then
// we need to create the back buffer
if ( !canDraw() ) {
setSize( (int) upperRight.getX(), (int) upperRight.getY() );
Dimension dim = getSize();
backBufferImage = (BufferedImage) createImage( (int) dim.getWidth(), (int)
dim.getHeight() );
backBuffer = backBufferImage.getGraphics();
firstPaint = false;
// @hack
clear();
grapher.updateGraph();
}
g.drawImage( backBufferImage, 0, 0, null, null );
}
/*
* public void paint( Graphics g ) { /* // TODO: I can only seem to create
* this backbuffer // during the paint method and this seems // to result in
* an occasional NullPointerException // when it gets updated too fast
* // if this is the first paint, then // we need to create the back buffer
* if ( !canDraw() ) { setSize( (int)upperRight.getX(),
* (int)upperRight.getY() ); Dimension dim = getSize(); backBufferImage =
* (BufferedImage) createImage( (int) dim.getWidth(), (int) dim.getHeight() );
* backBuffer = backBufferImage.getGraphics(); clear(); firstPaint = false; } //
* if it isn't the first painting, then copy the // back buffer to the
* screen //else {
* // g.drawImage( backBufferImage, 0, 0, this ); //} g.drawImage(
* backBufferImage, 0, 0, 100, 100, 100, 100, 100, 100, null );
*
* paintComponent( g ); statDisplay.paintComponents( g ); }
*/
public void initGUI() {
addMouseListener( grapher.getMouseHandler() );
addMouseMotionListener( grapher.getMouseHandler() );
}
/**
* Clear the graph
*/
public void clear() {
if ( canDraw() ) {
setClearColor();
backBuffer.setColor( Color.WHITE );
// TODO: fix this to be
backBuffer.fillRect( 0, 0, getWidth(), getHeight() ); //(int)upperRight.getX(),
// (int)upperRight.getY()
// );
}
}
public void drawMessage( Point point, String message ) {
Point realCoords = toGraphCoords( point );
if ( canDraw() ) {
setColor( ColorSchemeManager.GRAPH_TEXT );
backBuffer.drawString( message, (int)realCoords.getX(), (int)realCoords.getY()
);
}
}
/**
* Draws a line segment on the graph, converting its coordinates
*
* @param segment
* The line segment to render
*/
public void drawLineSegment( LineSegment segment, int lineWidth, Color color )
{
if ( canDraw() ) {
setColor( color );
Point start = new Point( segment.getP1() );
Point end = new Point( segment.getP2() );
start = toGraphCoords( start );
end = toGraphCoords( end );
if ( lineWidth == 1 ) {
backBuffer.drawLine( (int) start.getX(), (int) start.getY(), (int) end.getX(),
(int) end.getY() );
}
else {
for ( int lineOffset = (int) ( -lineWidth * 0.5 ); lineOffset <= (int) (
lineWidth * 0.5 ); lineOffset++ ) {
backBuffer.drawLine( (int) start.getX() + lineOffset, (int) start.getY(), (int)
end.getX()
+ lineOffset, (int) end.getY() );
}
}
}
}
public void drawLineSegment( LineSegment segment ) {
drawLineSegment( segment, 1, DEFAULT_LINESEGMENT_COLOR );
}
public void drawRect( Rectangle rect, Color color ) {
if ( canDraw() ) {
setColor( color );
Point coords = toGraphCoords( new Point( rect.getX(), rect.getY() ) );
Point dims = new Point( rect.getWidth(), rect.getHeight() );
backBuffer.fillRect( (int) coords.getX(), (int) coords.getY(), (int)
dims.getX(), (int) dims.getY() );
}
}
/**
* Draws a point on the graph; converts the point's dimensions
*
* @param point
* The point to be rendered TODO: render an image of a point
* rather than a single pixel
*/
public void drawPoint( Point point, double scale ) {
if ( canDraw() ) {
setPointColor();
Point transformedPoint = toGraphCoords( point );
backBuffer.drawImage( pointImg.getImage(), (int) ( transformedPoint.getX() -
POINT_RADIUS * scale ),
(int) ( transformedPoint.getY() - POINT_RADIUS * scale ),
(int) ( pointImg.getIconWidth() * scale ), (int) ( pointImg.getIconHeight() *
scale ), null, null );
}
}
public void drawPoint( Point point ) {
drawPoint( point, 1 );
}
/**
* Converts the given point from its position on the plane to the
* corresponding location on the graph
*
* @param point
* The point to be converted
* @return The converted point
*/
public Point toGraphCoords( Point point ) {
double x = point.getX();
double y = point.getY();
// since (0, 0) refers to the upper-left corner
// we subtract the point's y from the graph's height
y = upperRight.getY() - y;
y = y + lowerLeft.getY();
x = x - lowerLeft.getX();
// find the scale factors by getting the coordinates' ratios
final double scaleX = ( upperRight.getX() - lowerLeft.getX() ) /
upperRight.getX();
final double scaleY = ( upperRight.getY() - lowerLeft.getY() ) /
upperRight.getY();
// mulitply both coordinates by the scale factors
//x = x * scaleX;
//y = y * scaleY;
return new Point( x, y );
}
/**
* Converts a point from swing's coordinate system to the corresponding
* point on a 2D plane
*
* @param point
* The point to be converted
* @return The converted point
*/
public Point toPlaneCoords( Point point ) {
double x = point.getX();
double y = point.getY();
y = upperRight.getY() - y + lowerLeft.getY();
;
x = x + lowerLeft.getX();
//y = -y;
final double scaleX = ( upperRight.getX() - lowerLeft.getX() ) /
upperRight.getX();
final double scaleY = ( upperRight.getY() - lowerLeft.getY() ) /
upperRight.getY();
// x = x / scaleX;
// y = y / scaleY;
return new Point( x, y );
}
public Point getBottomLeft() {
return lowerLeft;
}
public Point getTopRight() {
return upperRight;
}
public void addStatDisplay( JPanel panel ) {
statDisplay = panel;
}
private void loadImages() {
//pointImg = new ImageIcon( POINT_IMAGE_PATH );
pointImg = JavaPic_pointpng.getImageIcon();
}
private boolean canDraw() {
return !firstPaint;
}
// TODO: replace this with an image drawing routine
private void setPointColor() {
backBuffer.setColor( Color.RED );
}
private void setClearColor() {
backBuffer.setColor( Color.WHITE );
}
private void setLineSegmentColor() {
backBuffer.setColor( Color.BLUE );
}
private void setColor( Color color ) {
backBuffer.setColor( color );
}
private JPanel statDisplay;
private ImageIcon pointImg;
private Grapher grapher;
private boolean firstPaint;
private Graphics backBuffer;
private BufferedImage backBufferImage;
private Graphics graphics;
private Point lowerLeft;
private Point upperRight;
private static final Color DEFAULT_LINESEGMENT_COLOR = Color.BLUE;
//private static final String POINT_IMAGE_PATH = "Point.jpg";
private static final int POINT_RADIUS = 4;
}
GraphBars.java
-------------------------------------------------------------------
/*
* Created on Apr 1, 2005
*/
/**
* @author Steve Bradley
*
* Added description code - AO
*/
import java.awt.Color;
public class GraphBars extends GraphShape {
public GraphBars( double newXBar, double newYBar, Point lowerLeft, Point
upperRight ) {
center = new Point( newXBar, newYBar );
xBar = new LineSegment( newXBar, lowerLeft.getY(), newXBar, upperRight.getX());
yBar = new LineSegment( lowerLeft.getX(), newYBar, upperRight.getX(), newYBar
);
}
protected void onDraw(Grapher.GraphWriter graph) {
graph.drawLineSegment(xBar, barColor);
graph.drawLineSegment(yBar, barColor);
graph.drawPoint( center, .5 );
}
public boolean isMouseOver( Point mousePoint ) {
// return ( xBar.ptLineDist( mousePoint) <= MOUSEOVER_DISTANCE
// || yBar.ptLineDist( mousePoint ) <= MOUSEOVER_DISTANCE );
return false;
}
public double getDistance( Point point ) {
return Math.min( xBar.ptLineDist( point ), yBar.ptLineDist( point ) );
}
public String getDescription( StatManager statManager ) {
return "x = " + StatManager.roundStat( statManager.calcXBar(),
3 )
+ "
y = " + StatManager.roundStat( statManager.calcYBar(), 3 ) +
"";
}
private Point center;
private static final Color barColor = new Color( 255, 128, 0, 128 );
private LineSegment xBar;
private LineSegment yBar;
private static final int MOUSEOVER_DISTANCE = 20;
}
GraphCoordinator.java
-------------------------------------------------------------------
/*
* Created on Apr 14, 2005
*/
/*
* @author Andrew Owens
*/
public interface GraphCoordinator {
public void updateGraph();
}
GraphDeviation.java
-------------------------------------------------------------------
/*
* Created on Mar 31, 2005
*/
/**
* @author Andrew Owens
*/
import java.awt.Color;
public class GraphDeviation extends GraphShape {
public GraphDeviation( Point point, double yBar ) {
initLine( point, yBar );
}
public String getDescription( StatManager statManager ) {
return "Length = "
+ StatManager.roundStat( deviationLine.getP1().distance( deviationLine.getP2()
), 3 )
+ "