package image.create; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.geom.*; import java.text.DecimalFormat; // ESP imports import gui.elements.RGBControl; import model.compute.*; /** *

Title: Java Media for Experiencing Sensation and Perception

*

Description: This shows the effect of blurred contrours * on form perception.

*

Copyright: Copyright (c) 2002

*

Company: Hanover College

* @author John H. Krantz, Ph.D. * @version 0.1 */ public class ContourObject extends CreateImage { static final long serialVersionUID = 0; // constants that control the images private double slope = 1.0; private double intensity = 0.25; private boolean polarity = true; private double back = 150.0; // plotting the contours objects int lumPoint = 0; boolean plot = false; // shape values public final int SQUARE = 1; public final int CIRCLE = 0; private int shape = CIRCLE; // name of illusion public final static String NAME = "Craik or Minimal Contour"; // type of object values public final int CRAIK = 0; public final int MINCONTOUR = 1; private int type = CRAIK; private final static String [] TYPES = { "Craik-Cornsweet-O'Brien", "Minimal Contour"}; // image manipulation objects double cW = 0.5; DecimalFormat balFmt = new DecimalFormat("0.00"); // type private JPanel typePanel = new JPanel(); private JLabel typeLabel = new JLabel("Type: "); private JComboBox typeCBX = new JComboBox(TYPES); // Size of sine wave cycle private JPanel widthPanel = new JPanel(); private JLabel widthLabel = new JLabel("Circle Diam."); private JSlider widthSlider = new JSlider(1,90, (int)(cW*100.0)); private JLabel widthValLabel = new JLabel(" = "+ balFmt.format(cW)); // contrast private JPanel contrastPanel = new JPanel(); private JLabel contrastLabel = new JLabel("Contrast"); private JSlider contrastSlider = new JSlider(0,100, 25); private JLabel contrastValLabel = new JLabel(" = "+ balFmt.format(intensity)); // slope private JPanel slopePanel = new JPanel(); private JLabel slopeLabel = new JLabel("Edge Size"); private JSlider slopeSlider = new JSlider(0,100, 100); private JLabel slopeValLabel = new JLabel(" = "+ balFmt.format(slope)); // polarity private JPanel polarityPanel = new JPanel(); private JLabel polarityLabel = new JLabel("Direction"); private JCheckBox polarityCHK = new JCheckBox("Positive", polarity); private JCheckBox plotCHK = new JCheckBox("Plot Contour", plot); // foreground colors private RGBControl rgb = new RGBControl(); private JLabel rgbLabel = new JLabel("Color Balance"); // draw an image so that the image can be output to other modules //to use Image img; // the control panel to be returned private JPanel control = new JPanel(); // blur function for minimal contour image private Normal normal = new Normal(0,1.0/4.0); public ContourObject() { setupControl(); } private void setupControl(){ control.setLayout(new BorderLayout()); JPanel top = new JPanel(new GridLayout(6,1)); // type width control typePanel.add(typeLabel); typePanel.add(typeCBX); top.add(typePanel); // add the listener typeCBX.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e){ type = typeCBX.getSelectedIndex(); repaint(); } }); // object width control widthPanel.add(widthLabel); widthPanel.add(widthSlider); widthPanel.add(widthValLabel); top.add(widthPanel); // add the listener widthSlider.addChangeListener(new ChangeListener(){ public void stateChanged(ChangeEvent e){ if (type == CIRCLE){ cW = (double)widthSlider.getValue()/100.0; widthValLabel.setText(" = "+ balFmt.format(cW)); } repaint(); } }); // the Slope control slopePanel.add(slopeLabel); slopePanel.add(slopeSlider); slopePanel.add(slopeValLabel); top.add(slopePanel); // add the listener slopeSlider.addChangeListener(new ChangeListener(){ public void stateChanged(ChangeEvent e){ slope = (double)slopeSlider.getValue()/ (double)slopeSlider.getMaximum(); slopeValLabel.setText(" = "+ balFmt.format(slope)); repaint(); } }); // the polarity control polarityPanel.add(polarityLabel); polarityPanel.add(polarityCHK); top.add(polarityPanel); // add the listener polarityCHK.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e){ polarity = polarityCHK.isSelected(); if (polarity){ polarityCHK.setText("Positive"); } else { polarityCHK.setText("Negative"); } repaint(); } }); // the contrast control contrastPanel.add(contrastLabel); contrastPanel.add(contrastSlider); contrastPanel.add(contrastValLabel); top.add(contrastPanel); // add the listener contrastSlider.addChangeListener(new ChangeListener(){ public void stateChanged(ChangeEvent e){ double d = (double)contrastSlider.getValue()/ (double)contrastSlider.getMaximum(); intensity = d; contrastValLabel.setText(" = "+ balFmt.format(d)); repaint(); } }); // the polarity control top.add(plotCHK); // add the listener plotCHK.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e){ plot = plotCHK.isSelected(); repaint(); } }); rgb.setShowLabels(true); JPanel rgbPanel = new JPanel(); rgbPanel.add(rgbLabel); rgbPanel.add(rgb); control.add(rgbPanel,BorderLayout.CENTER); // add the listeners rgb.red.addChangeListener(new ChangeListener(){ public void stateChanged(ChangeEvent e){ repaint(); } }); rgb.green.addChangeListener(new ChangeListener(){ public void stateChanged(ChangeEvent e){ repaint(); } }); rgb.blue.addChangeListener(new ChangeListener(){ public void stateChanged(ChangeEvent e){ repaint(); } }); control.add(top,BorderLayout.NORTH); } // this method draws the stimulus public void paint(Graphics g){ Graphics2D g2d = (Graphics2D) g; double width = getSize().width; double height = getSize().height; double centerX = width/2.0; double centerY = height/2.0; // create the output image img = createImage((int)width,(int)height); Graphics2D gImg = (Graphics2D)img.getGraphics(); // set up to plot of the contour double lums[][] = new double [(int)width][2]; lumPoint = 0; lums[lumPoint][0] = 0; lums[lumPoint][1] = back; lumPoint ++; // clearn the screen with the background color gImg.setPaint(new Color((int)(back*rgb.getRedDbl()), (int)(back*rgb.getGreenDbl()), (int)(back*rgb.getBlueDbl()))); gImg.fill(new Rectangle(0,0,(int)width,(int)height)); double dir = 1.0; if (! polarity) dir = -1.0; // draw the circles //set the dimensions assuming width < height and // drawing the minimal contours double lumChange = (255.0-back)*intensity; int iMin = (int)(-lumChange*slope*8.0); int iMax = (int)(+lumChange*slope*8.0); // adjust size if drawing Craik if (type == CRAIK){ iMin = (int)(-slope*cW*width/2.0); iMax = (int)(+slope*cW*width/2.0); } double offset = (int)(cW*width/2.0); // now see if width < height if (height < width){ offset = (cW*height/2.0); // again adjust for Craik if (type == CRAIK){ iMin = (int)(-slope*cW*height/2.0); iMax = (int)(+slope*cW*height/2.0); } } // actual drawing now double lumMax = (255.0-back)*intensity*dir+back; double curLum = 0; double lastLum = 0; double lumOff = 0;// proportional change in luminance if (type == MINCONTOUR){ normal.setMean(0); normal.setStdDev(slope*offset/3.5); for (int i = -(int)(normal.getStdDev()*3.5); i <= (int)(normal.getStdDev()*3.5); i ++){ if (iMax != iMin){ lumOff = normal.convertToZ(i); lumOff = normal.getCumNormalHeight(lumOff); // how far out on the luminance range am I curLum = (lumOff*(lumMax-back)+back); } else curLum = lumMax; // add the new luminance point. lums[lumPoint][0]=i+centerX-offset; lums[lumPoint][1]=curLum; lumPoint ++; if (lastLum != curLum){ gImg.setPaint(new Color((int)(curLum*rgb.getRedDbl()), (int)(curLum*rgb.getGreenDbl()), (int)(curLum*rgb.getBlueDbl()))); if (shape == CIRCLE){ gImg.fill(new Ellipse2D.Double(i+centerX-offset, i+centerY-offset, 2*(Math.abs(i-offset)), 2*(Math.abs(i-offset)))); } else gImg.fill(new Rectangle2D.Double(i+centerX-offset, i+centerY-offset, 2*(Math.abs(i-offset)), 2*(Math.abs(i-offset)))); lastLum = curLum; } } } else { for (int i = iMin; i <= iMax; i ++){ if (iMax != iMin){ lumOff = ((double)iMax-Math.abs(i))/(double)iMax; lumOff = Math.pow(lumOff,3.0); if (i < 0) lumOff = lumOff*-1;// change side for outside curLum = (lumOff*(lumMax-back)+back); } else curLum = back; // add the new luminance point. lums[lumPoint][0]=i+centerX-offset; lums[lumPoint][1]=curLum; lumPoint ++; if (lastLum != curLum){ gImg.setPaint(new Color((int)((double)curLum*rgb.getRedDbl()), (int)((double)curLum*rgb.getGreenDbl()), (int)((double)curLum*rgb.getBlueDbl()))); if (shape == CIRCLE) gImg.fill(new Ellipse2D.Double(i+centerX-offset, i+centerY-offset, 2*(Math.abs(i-offset)), 2*(Math.abs(i-offset)))); else gImg.fill(new Rectangle2D.Double(i+centerX-offset, i+centerY-offset, 2*(Math.abs(i-offset)), 2*(Math.abs(i-offset)))); lastLum = curLum; } } } // put the image on the screen g2d.drawImage(img,0,0,this); // plotting the contour routine. if (plot){ GeneralPath contour = new GeneralPath(); float vertScale = (float)height/255.0f; contour.moveTo((float)lums[0][0],(float)( height-lums[0][1]*vertScale)); // first half of the contour for (int i = 1; i < lumPoint; i ++){ contour.lineTo((float)lums[i][0], (float)(height-lums[i][1]*vertScale)); } // second half of contour for (int i = lumPoint-1; i >=0; i --){ contour.lineTo((float)width-(float)lums[i][0], (float)(height-lums[i][1]*vertScale)); } //draw the contour g2d.setPaint(Color.cyan); g2d.draw(contour); } } // set methods // get methods public Image getImage() { return img; } public String getName(){ return TYPES[type]; } public JPanel getControlPanel(){ return control; } public int getHeight(){ return height; } public int getWidht() { return width; } public double getIntensity() { return intensity; } public double getSlope() { return slope; } public boolean getPolarity() { return polarity; } public int getShape() { return shape; } public int getDrawObject() { return type; } public boolean getPlot() { return plot; } }