/*
* -------------------------------------------------------------------------
* $Id: CsPanel.java,v 1.3 2004/04/07 20:08:26 estewart Exp $
* -------------------------------------------------------------------------
* Copyright (c) 1999 Visual Numerics Inc. All Rights Reserved.
*
* This software is confidential information which is proprietary to
* and a trade secret of Visual Numerics, Inc. Use, duplication or
* disclosure is subject to the terms of an appropriate license
* agreement.
*
* VISUAL NUMERICS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
* SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. VISUAL
* NUMERICS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
* AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR
* ITS DERIVATIVES.
*--------------------------------------------------------------------------
*/
/*
* CsPanel.java
*
* Created on August 19, 2003, 2:15 PM
*/
package com.imsl.demo.fitting;
import com.imsl.chart.*;
import com.imsl.math.*;
import java.util.Vector;
import java.awt.event.*;
import javax.swing.JOptionPane;
public class CsPanel extends javax.swing.JPanel implements ActionListener, MouseListener {
private Chart chart;
private AxisXY axis;
private Data point;
private Vector dataX, dataY;
private double[] xRange, yRange;
private final int numMethod = 6;
private Data line[] = new Data[numMethod];
private javax.swing.JFrame parentFrame;
private javax.swing.JRadioButtonMenuItem[] jRadioButtons;
private final int numData = 5;
private final String dataName[] = {"Blank", "Coffee Sales", "Titanium Data", "Ocean Depth", "Sin(x) Function"};
/** Creates new form CsPanel */
public CsPanel(javax.swing.JFrame parent) {
this.parentFrame = parent;
dataX = new Vector();
dataY = new Vector();
initComponents();
setPreferredSize(new java.awt.Dimension(parent.getSize().width, (int)(0.85*parent.getSize().height)));
jButtonGenerate.addActionListener(this);
jButtonEnter.addActionListener(this);
jButtonReset.addActionListener(this);
checkAkima.addActionListener(this);
checkInterp.addActionListener(this);
checkPeriod.addActionListener(this);
checkShape.addActionListener(this);
checkSmooth.addActionListener(this);
checkSmooth2.addActionListener(this);
addDataButtons();
setChart();
jPanelChart.setPreferredSize(new java.awt.Dimension(parent.getSize().width, (int)(0.66*parent.getSize().height)));
getData();
update();
}
private void setChart() {
chart = jPanelChart.getChart();
chart.getLegend().setPaint(true);
chart.getLegend().setViewport(0.8,0.95,0.05,0.15);
axis = new AxisXY(chart);
axis.setViewport(0.12,0.78,0.05,0.88);
// Set axes ranges
axis.getAxisX().setAutoscaleInput(AxisXY.AUTOSCALE_OFF);
axis.getAxisY().setAutoscaleInput(AxisXY.AUTOSCALE_OFF);
axis.getAxisX().setWindow(0.0, 50.0);
axis.getAxisY().setWindow(0.0, 75.0);
axis.getAxisX().setTextFormat(new java.text.DecimalFormat("###.#"));
axis.getAxisY().setTextFormat(new java.text.DecimalFormat("###.#"));
// Add MouseListeren to the chart.
jPanelChart.addMouseListener(this);
}
// add the available data sets to the dataMenu
private void addDataButtons() {
jRadioButtons = new javax.swing.JRadioButtonMenuItem[numData];
javax.swing.ButtonGroup group = new javax.swing.ButtonGroup();
for (int i=0; i<numData; i++) {
jRadioButtons[i] = new javax.swing.JRadioButtonMenuItem();
jRadioButtons[i].setText(dataName[i]);
jRadioButtons[i].addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
getData();
update();
}
});
group.add(jRadioButtons[i]);
((FittingMain)parentFrame).getDataMenu().add(jRadioButtons[i]);
}
jRadioButtons[1].setSelected(true);
}
// clear and load data sets determined by the menu selection
public void getData() {
dataX.clear();
dataY.clear();
xRange = new double[2];
yRange = new double[2];
String xTitle = "X Value";
String yTitle = "Y Value";
String xFormat = "###.#";
String yFormat = "###.#";
int selected = -1;
for (int i=0; i<numData; i++) {
if (jRadioButtons[i].isSelected()) {
selected = i;
break;
}
}
switch (selected) {
case 0: // blank
xRange[0] = 0.0;
xRange[1] = 50.0;
yRange[0] = 0.0;
yRange[1] = 50.0;
break;
case 3: /* These data are the depth of the Atlantic Ocean from
* Cape Hatteras, North America to Cape Blanc, Africa
*/
double x3[] = {0,4,8,12,16,20,24,28,32,36,40,44,48,52,56,60};
double y3[] = {0,46,52,47,52,56,55,42,25,49,55,60,58,51,42,0};
for (int i=0; i<x3.length; i++) {
dataX.add(new Double(x3[i]*100));
dataY.add(new Double(y3[i]*-100));
}
xTitle = "Distance, km";
yTitle = "Depth, m";
xFormat = "#,###";
yFormat = "#,###";
xRange[0] = 0.0;
xRange[1] = 6000.0;
yRange[0] = -6500.0;
yRange[1] = 0.0;
break;
case 1: /* These data
* data discussed by Neter and Wasserman (1974,
* pp. 279–285). The data set contains the response variable y measuring coffee
* sales (in hundred gallons) and the number of self-service coffee dispensers.
*/
//double x2[] = {53,61,69,77,85,93,101,109,117,125,133,141,149,157,165,173,181,189,197,205,213,221,229,237};
//double y2[] = {8103,6044,4694,3827,3086,3540,3941,2033,1703,1242,1310,1332,1486,2066,2888,2059,2153,1874,1919,1575,1434,1153,942,756};
//xRange[1] = 250.0;
//yRange[1] = 8500.0;
double x1[] = {0,1,2,4,5,6,7};
double y1[] = {508.1,568.2,651.7,755.3,787.6,841.4,854.7,};
xRange[1] = 8;
yRange[1] = 1000.0;
for (int i=0; i<x1.length; i++) {
dataX.add(new Double(x1[i]));
dataY.add(new Double(y1[i]));
}
xTitle = "Number of Self-Service Dispensers";
yTitle = "Coffee Sales, Hundreds of Gallons";
xFormat = "###";
yFormat = "###";
xRange[0] = 0.0;
yRange[0] = 0.0;
break;
case 2: /* These data are from A Practical Guide to Splines by C. de Boor
* these data represent a property of titanium as a function of
* temperature. They have been used extensively as an example in spline
* approximation with variable knots.
* The peak near 885 degrees C is the point of phase change to the beta phase
*/
double y2[] = {.644,.622,.638,.649,.652,.639,.646,.657,.652,.655,.644,.663,.663,.668,.676,.676,.686,.679,.678,.683,.694,.699,.710,.730,.763,.812,.907,1.044,1.336,1.881,2.169,2.075,1.598,1.211,.916,.746,.672,.627,.615,.607,.606,.609,.603,.601,.603,.601,.611,.601,.608};
for (int i=0; i<y2.length; i++) {
dataX.add(new Double(585 + 10.0*(i+1)));
dataY.add(new Double(y2[i]));
}
xTitle = "Temperature, "+(char)186+"C";
yTitle = "Specific Heat of Titanium, J/gK";
xFormat = "#,###";
yFormat = "#.#";
xRange[0] = 550.0;
xRange[1] = 1100.0;
yRange[0] = 0.5;
yRange[1] = 2.5;
break;
case 4: /* This is a sin() function
*/
int num = 14;
for (int i=0; i<num; i++) {
double x = (double)i;
dataX.add(new Double(x));
dataY.add(new Double(Math.sin(x)));
}
xTitle = "X Value";
yTitle = "Sin(X)";
xFormat = "##.#";
yFormat = "##.#";
xRange[0] = 0.0;
xRange[1] = num;
yRange[0] = -2.0;
yRange[1] = 2.0;
break;
}
axis.getAxisX().getAxisTitle().setTitle(xTitle);
axis.getAxisY().getAxisTitle().setTitle(yTitle);
axis.getAxisX().setWindow(xRange);
axis.getAxisY().setWindow(yRange);
axis.getAxisX().setTextFormat(new java.text.DecimalFormat(xFormat));
axis.getAxisY().setTextFormat(new java.text.DecimalFormat(yFormat));
}
// Draw the graph.
private void drawGraph() {
if ((dataX.size() != 0)) {
double[] x = new double[dataX.size()];
double[] y = new double[dataY.size()];
for (int i = 0; i < x.length; i++) {
x[i] = ((Double) dataX.get(i)).doubleValue();
y[i] = ((Double) dataY.get(i)).doubleValue();
}
// Draw the points.
point = new Data(axis, x, y);
point.setDataType(Data.DATA_TYPE_MARKER);
point.setMarkerType(Data.MARKER_TYPE_OCTAGON_X);
point.setMarkerSize(0.8);
point.setMarkerColor(java.awt.Color.blue);
// Draw the spline using CsAkima
if (checkAkima.isSelected() && (dataX.size() > 3)) {
final CsAkima cs0 = new CsAkima(x, y);
ChartFunction fcn0 = new ChartFunction() {
public double f(double x) {
return cs0.value(x);
}
};
line[0] = new Data(axis, fcn0, xRange[0], xRange[1]);
line[0].setLineColor(java.awt.Color.magenta);
line[0].setTitle("CsAkima");
line[0].setLineWidth(1.5);
}
// Draw the spline using CsInterpolate
if (checkInterp.isSelected() && (dataX.size() > 2)) {
final CsInterpolate cs1 = new CsInterpolate(x, y);
ChartFunction fcn1 = new ChartFunction() {
public double f(double x) {
return cs1.value(x);
}
};
line[1] = new Data(axis, fcn1, xRange[0], xRange[1]);
line[1].setLineColor(java.awt.Color.green);
line[1].setTitle("CsInterpolate");
line[1].setLineWidth(1.5);
}
// Draw the spline using CsPeriodic
if (checkPeriod.isSelected() && (dataX.size() > 3)) {
final CsPeriodic cs2 = new CsPeriodic(x, y);
ChartFunction fcn2 = new ChartFunction() {
public double f(double x) {
return cs2.value(x);
}
};
line[2] = new Data(axis, fcn2, xRange[0], xRange[1]);
line[2].setLineColor(java.awt.Color.orange);
line[2].setTitle("CsPeriodic");
line[2].setLineWidth(1.5);
}
// Draw the spline using CsShape
if (checkShape.isSelected() && (dataX.size() > 2)) {
try {
final CsShape cs3 = new CsShape(x, y);
ChartFunction fcn3 = new ChartFunction() {
public double f(double x) {
return cs3.value(x);
}
};
line[3] = new Data(axis, fcn3, xRange[0], xRange[1]);
line[3].setLineColor(java.awt.Color.blue);
line[3].setTitle("CsShape");
line[3].setLineWidth(1.5);
} catch (CsShape.TooManyIterationsException e) {
System.out.println("Too many iterations");
} catch (SingularMatrixException e) {
System.out.println("Singular matrix");
}
}
// Draw the spline using CsSmooth
if (checkSmooth.isSelected() && (dataX.size() > 2)) {
final CsSmooth cs4 = new CsSmooth(x, y);
ChartFunction fcn4 = new ChartFunction() {
public double f(double x) {
return cs4.value(x);
}
};
line[4] = new Data(axis, fcn4, xRange[0], xRange[1]);
line[4].setLineColor(java.awt.Color.cyan);
line[4].setTitle("CsSmooth");
line[4].setLineWidth(1.5);
}
// Draw the spline using CsSmooth2
if (checkSmooth2.isSelected()) {
// Set the weights
final double sdev = 1./Math.sqrt(3.);
double weights[] = new double[dataX.size()];
for (int i = 0; i < dataX.size(); i++) {
weights[i] = sdev;
}
// Set the smoothing parameter
double smpar = (double)dataX.size();
final CsSmoothC2 cs5 = new CsSmoothC2(x, y, weights, smpar);
ChartFunction fcn5 = new ChartFunction() {
public double f(double x) {
return cs5.value(x);
}
};
line[5] = new Data(axis, fcn5, xRange[0], xRange[1]);
line[5].setLineColor(java.awt.Color.red);
line[5].setTitle("CsSmoothC2");
line[5].setLineWidth(1.5);
}
}
}
// Redraw the chart.
private void update() {
for (int i = 0; i<numMethod; i++) {
if (line[i] != null) line[i].remove();
}
if (point != null) point.remove();
drawGraph();
repaint();
}
public void setVisible(boolean show) {
super.setVisible(show);
if (show) {
addDataButtons();
}
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
private void initComponents() {//GEN-BEGIN:initComponents
jPanel = new javax.swing.JPanel();
jButtonEnter = new javax.swing.JButton();
jButtonGenerate = new javax.swing.JButton();
jLabelPoints = new javax.swing.JLabel();
numField = new javax.swing.JTextField("6", 4);
jButtonReset = new javax.swing.JButton();
jPanel1 = new javax.swing.JPanel();
jLabel2 = new javax.swing.JLabel();
jPanelChecks = new javax.swing.JPanel();
checkAkima = new javax.swing.JCheckBox();
checkInterp = new javax.swing.JCheckBox();
checkPeriod = new javax.swing.JCheckBox();
checkShape = new javax.swing.JCheckBox();
checkSmooth = new javax.swing.JCheckBox();
checkSmooth2 = new javax.swing.JCheckBox();
jPanelChart = new com.imsl.chart.JPanelChart();
setLayout(new javax.swing.BoxLayout(this, javax.swing.BoxLayout.Y_AXIS));
setPreferredSize(new java.awt.Dimension(500, 600));
jPanel.setMaximumSize(new java.awt.Dimension(32767, 36));
jButtonEnter.setText("Enter Points");
jPanel.add(jButtonEnter);
jButtonGenerate.setText("Generate");
jPanel.add(jButtonGenerate);
jLabelPoints.setText("# of Points: ");
jPanel.add(jLabelPoints);
numField.setColumns(4);
numField.setText("6");
jPanel.add(numField);
jButtonReset.setText("Reset");
jPanel.add(jButtonReset);
add(jPanel);
jPanel1.setMaximumSize(new java.awt.Dimension(32767, 20));
jPanel1.setMinimumSize(new java.awt.Dimension(93, 20));
jPanel1.setPreferredSize(new java.awt.Dimension(93, 20));
jLabel2.setText("Select Cubic Spline Models:");
jPanel1.add(jLabel2);
add(jPanel1);
jPanelChecks.setMaximumSize(new java.awt.Dimension(32767, 28));
jPanelChecks.setMinimumSize(new java.awt.Dimension(468, 28));
jPanelChecks.setPreferredSize(new java.awt.Dimension(468, 28));
checkAkima.setText("Akima");
jPanelChecks.add(checkAkima);
checkInterp.setSelected(true);
checkInterp.setText("Interpolate");
jPanelChecks.add(checkInterp);
checkPeriod.setText("Periodic");
jPanelChecks.add(checkPeriod);
checkShape.setText("Shape");
jPanelChecks.add(checkShape);
checkSmooth.setText("Smooth");
jPanelChecks.add(checkSmooth);
checkSmooth2.setText("SmoothC2");
jPanelChecks.add(checkSmooth2);
add(jPanelChecks);
jPanelChart.setMinimumSize(new java.awt.Dimension(500, 500));
jPanelChart.setPreferredSize(new java.awt.Dimension(500, 500));
add(jPanelChart);
}//GEN-END:initComponents
// Implement ActionListener
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("Enter Points")) {
// Enter points using a text area.
javax.swing.JTextArea textArea = new javax.swing.JTextArea(10, 5);
javax.swing.JScrollPane scroll = new javax.swing.JScrollPane(textArea);
int option = 0;
try {
option = JOptionPane.showConfirmDialog(this, scroll,
"Enter Points", JOptionPane.OK_CANCEL_OPTION);
if (option == JOptionPane.CANCEL_OPTION) return;
} catch (Exception ex) {
}
java.util.StringTokenizer lineToken = new java.util.StringTokenizer(textArea.getText(), "\n");
boolean dup = false;
while (lineToken.hasMoreElements()) {
java.util.StringTokenizer token = new java.util.StringTokenizer(lineToken.nextToken(), "(){}[] ,\t\n");
// If the line does not contain 2 numbers, then ignore.
if (token.countTokens() == 2) {
try {
double x = Double.parseDouble(token.nextToken());
double y = Double.parseDouble(token.nextToken());
// Ignore any points that are not in the range.
if ((x >= xRange[0]) && (x <= xRange[1]) &&
(y >= yRange[0]) && (y <= yRange[1])) {
// Only add if the x value is distinct or else
// we'll throw a nasty java.lang.IllegalArgumentException
for (int i = 0; i < dataX.size(); i++) {
if (((Double) dataX.get(i)).doubleValue() == x) {
dup = true;
}
}
if (!dup) {
dataX.add(new Double(x));
dataY.add(new Double(y));
}
}
} catch (Exception ex) {
}
}
}
if (dup) {
JOptionPane.showMessageDialog(this, "X values must be distinct.\n" +
"Duplicates have not been added.",
"Data Error", JOptionPane.ERROR_MESSAGE);
}
update();
} else if (e.getActionCommand().equals("Generate")) {
int i = 0;
try {
i = Integer.parseInt(numField.getText());
} catch (Exception exception) {
i = 6;
numField.setText("6");
} finally {
if (i <= 0) {
i = 6;
numField.setText("6");
}
for (int j = 0; j < i; j++) {
dataX.add(new Double(xRange[1] * Math.random() + xRange[0]));
dataY.add(new Double(yRange[1] * Math.random() + yRange[0]));
}
update();
}
} else if (e.getActionCommand().equals("Reset")) {
numField.setText("6");
dataX = new Vector();
dataY = new Vector();
update();
getData();
update();
} else { // this means a checkbox is changed
update();
}
}
// Implement MouseListener
public void mouseClicked(MouseEvent e) {
double user[] = {0,0};
axis.mapDeviceToUser(e.getX(), e.getY(), user);
// Do not add the point if it is not in the range.
if ((user[0] < xRange[0]) || (user[0] > xRange[1]) ||
(user[1] < yRange[0]) || (user[1] > yRange[1])) return;
// Button is to add points. Otherwise, remove points.
if (e.getModifiers() == MouseEvent.BUTTON1_MASK) {
// But only add if the x value is distinct or else
// Splines throw a java.lang.IllegalArgumentException
boolean dup = false;
for (int i = 0; i < dataX.size(); i++) {
if (((Double) dataX.get(i)).doubleValue() == (double) user[0]) {
dup = true;
}
}
if (dup) {
JOptionPane.showMessageDialog(this, "X values must be distinct.",
"Data Error", JOptionPane.ERROR_MESSAGE);
} else {
dataX.add(new Double(user[0]));
dataY.add(new Double(user[1]));
update();
}
} else {
int idx = -1;
double min = 0.01;
final double ux = (user[0]-xRange[0])/(double)(xRange[1]-xRange[0]);
final double uy = (user[1]-yRange[0])/(double)(yRange[1]-yRange[0]);
for (int i = 0; i < dataX.size(); i++) {
double x = (((Double)dataX.get(i)).doubleValue()-xRange[0])/
(double)(xRange[1]-xRange[0]);
double y = (((Double)dataY.get(i)).doubleValue()-yRange[0])/
(double)(yRange[1]-yRange[0]);
double dist = Math.sqrt((ux - x)*(ux - x) + (uy - y)*(uy - y));
if (dist < min) {
min = dist;
idx = i;
}
}
if (idx != -1) {
dataX.remove(idx);
dataY.remove(idx);
update();
}
}
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JCheckBox checkSmooth;
private javax.swing.JLabel jLabelPoints;
private javax.swing.JButton jButtonGenerate;
private javax.swing.JCheckBox checkShape;
private javax.swing.JButton jButtonReset;
private javax.swing.JTextField numField;
private javax.swing.JLabel jLabel2;
private javax.swing.JCheckBox checkInterp;
private javax.swing.JCheckBox checkPeriod;
private com.imsl.chart.JPanelChart jPanelChart;
private javax.swing.JCheckBox checkSmooth2;
private javax.swing.JCheckBox checkAkima;
private javax.swing.JPanel jPanel1;
private javax.swing.JButton jButtonEnter;
private javax.swing.JPanel jPanel;
private javax.swing.JPanel jPanelChecks;
// End of variables declaration//GEN-END:variables
}