import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
* Hauptklasse für SimplePlot. Enthält eine main-Methode
* und erstellt das GUI.
*/
public class SimplePlotMain {
/**
* Main-Methode
*
* @param args Nicht benutzt
*/
public static void main (String args[]) {
// Erzeuge das GUI auf dem Event-Dispatch-Thread
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createAndShowGUI();
}
});
}
/**
* Erzeuge das GUI und zeige es an
*/
private static void createAndShowGUI() {
// Erzeuge einen JFrame
JFrame frame = new JFrame("SimplePlot");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new BorderLayout());
frame.setSize(800,600);
// Erstelle das SimplePlotPanel und füge es zum Frame hinzu
SimplePlotPanel plotPanel = new SimplePlotPanel();
frame.getContentPane().add(plotPanel, BorderLayout.CENTER);
// Erstelle eine Funktion, die geplottet werden soll
// und weise sie dem SimplePlotPanel zum plotten zu
Function function = new Function() {
@Override
public float compute(float argument) {
return (float)Math.sin(argument)*argument;
}
};
plotPanel.setFunction(function);
// Füge ein control-panel für die Einstellungen zum Frame hinzu
JComponent controlPanel = createControlPanel(plotPanel);
frame.getContentPane().add(controlPanel, BorderLayout.EAST);
// Ganz am Ende den Frame anzeigen
frame.setVisible(true);
}
/**
* Erzeugt ein control-panel mit Spinnern, mit denen man den Bereich
* einstellen kann, der von der Funktion angezeigt werden soll
*
* @param plotPanel Das SimplePlotPanel, auf das die Einstellungen
* der Spinner übertragen werden
* @return Das control-panel
*/
private static JComponent createControlPanel(final SimplePlotPanel plotPanel) {
JPanel controlPanel = new JPanel(new BorderLayout());
JPanel panel = new JPanel(new GridLayout(0,2));
controlPanel.add(panel, BorderLayout.NORTH);
// Erstelle Spinner für die minimalen und maximalen X- und Y-Werte
final JSpinner minXSpinner = new JSpinner(
new SpinnerNumberModel(-1.0, -1000.0, 1000.0, 0.1));
final JSpinner maxXSpinner = new JSpinner(
new SpinnerNumberModel( 1.0, -1000.0, 1000.0, 0.1));
final JSpinner minYSpinner = new JSpinner(
new SpinnerNumberModel(-1.0, -1000.0, 1000.0, 0.1));
final JSpinner maxYSpinner = new JSpinner(
new SpinnerNumberModel( 1.0, -1000.0, 1000.0, 0.1));
// Füge die Spinner und entsprechende Labels dem Panel hinzu
panel.add(new JLabel("minX"));
panel.add(minXSpinner);
panel.add(new JLabel("maxX"));
panel.add(maxXSpinner);
panel.add(new JLabel("minY"));
panel.add(minYSpinner);
panel.add(new JLabel("maxY"));
panel.add(maxYSpinner);
// Erstelle einen ChangeListener, der bei einer Änderung an
// den Spinnern die Werte ans SimplePlotPanel überträgt
ChangeListener changeListener = new ChangeListener() {
@Override
public void stateChanged(ChangeEvent event) {
float minX = ((Double)minXSpinner.getValue()).floatValue();
float maxX = ((Double)maxXSpinner.getValue()).floatValue();
float minY = ((Double)minYSpinner.getValue()).floatValue();
float maxY = ((Double)maxYSpinner.getValue()).floatValue();
plotPanel.setRangeX(minX, maxX);
plotPanel.setRangeY(minY, maxY);
}
};
minXSpinner.addChangeListener(changeListener);
maxXSpinner.addChangeListener(changeListener);
minYSpinner.addChangeListener(changeListener);
maxYSpinner.addChangeListener(changeListener);
// Setze einige Default-Werte, die für die Beispielfunktion
// hübsch aussehen...
minXSpinner.setValue(-10.0);
maxXSpinner.setValue( 10.0);
minYSpinner.setValue(-10.0);
maxYSpinner.setValue( 10.0);
return controlPanel;
}
}
/**
* Interface für eine allgemeine Funktion, die einen float-Wert
* in einen anderen float-Wert umrechnet. Solche Funktionen können
* mit dem SimplePlotPanel geplottet werden.
*/
interface Function {
/**
* Berechne den Funktionswert an der angegebenen Stelle
*
* @param argument Die Stelle, an der die Funktion berechnet wird
* @return Den Funktionswert an der angegebenen Stelle
*/
float compute(float argument);
}
/**
* Das JPanel in dem eine Funktion geplottet wird
*/
class SimplePlotPanel extends JPanel {
private static final long serialVersionUID = -6588061082489436970L;
/**
* Die Funktion, die geplottet wird
*/
private Function function;
/**
* Der minimale x-Wert für die Funktion
*/
private float minX = -1.0f;
/**
* Der maximale x-Wert für die Funktion
*/
private float maxX = 1.0f;
/**
* Der minimale Y-Wert, der angezeigt werden soll
*/
private float minY = -1.0f;
/**
* Der maximale Y-Wert, der angezeigt werden soll
*/
private float maxY = 1.0f;
/**
* Erstellt das SimplePlotPanel
*/
public SimplePlotPanel() {
}
/**
* Setze die Fuktion, die geplottet werden soll
*
* @param function Die Funktion, die geplottet werden soll
*/
public void setFunction(Function function) {
this.function = function;
repaint();
}
/**
* Setzt den Bereich der Funktion, der geplottet werden soll
*
* @param minX Der minimale X-Wert
* @param maxX Der maximale X-Wert
*/
public void setRangeX(float minX, float maxX) {
this.minX = minX;
this.maxX = maxX;
repaint();
}
/**
* Setzt den Bereich der Funktion, der geplottet werden soll
*
* @param minY Der minimale Y-Wert
* @param maxY Der maximale Y-Wert
*/
public void setRangeY(float minY, float maxY) {
this.minY = minY;
this.maxY = maxY;
repaint();
}
/**
* Überschriebene Methode aus JComponent: Zeichnet dieses Panel (also
* die Funktion) in das übergebene Graphics-Objekt.
*/
@Override
protected void paintComponent(Graphics gr) {
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setColor(Color.WHITE);
g.fillRect(0,0,getWidth(),getHeight());
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
paintAxes(g);
paintFunction(g);
}
/**
* Rechnet einen x-Wert für die Funktion um in eine x-Position
* auf diesem JPanel
*
* @param x Der x-Wert für die Funktion
* @return Der x-Wert auf diesem JPanel
*/
private int toScreenX(float x) {
float relativeX = (x-minX)/(maxX-minX);
int screenX = (int)(getWidth() * relativeX);
return screenX;
}
/**
* Rechnet einen y-Wert der Funktion um in eine y-Position
* auf diesem JPanel
*
* @param y Der y-Wert der Funktion
* @return Der y-Wert auf diesem JPanel
*/
private int toScreenY(float y) {
float relativeY = (y-minY)/(maxY-minY);
int screenY = getHeight() - 1 - (int)(getHeight() * relativeY);
return screenY;
}
/**
* Rechnet eine x-Position auf diesem Panel um in den X-Wert für
* die Funktion, der dort liegt
*
* @param x Der x-Wert auf diesem JPanel
* @return Der x-Wert für die Funktion
*/
private float toFunctionX(int x) {
float relativeX = (float)x/getWidth();
float functionX = minX + relativeX * (maxX - minX);
return functionX;
}
/**
* Malt einfache Koordinatenachsen in das übergebene Graphics-Objekt
*
* @param g Das Graphics-Objekt zum Zeichnen
*/
private void paintAxes(Graphics2D g) {
int x0 = toScreenX(0);
int y0 = toScreenY(0);
g.setColor(Color.BLACK);
g.drawLine(0,y0,getWidth(),y0);
g.drawLine(x0,0,x0,getHeight());
}
/**
* Zeichnet die Funktion in das übergebene Graphics-Objekt
*
* @param g Das Graphics-Objekt zum Zeichnen
*/
private void paintFunction(Graphics2D g) {
g.setColor(Color.BLUE);
int previousScreenX = 0;
float previouseFunctionX = toFunctionX(previousScreenX);
float previousFunctionY = function.compute(previouseFunctionX);
int previousScreenY = toScreenY(previousFunctionY);
for (int screenX=1; screenX<getWidth(); screenX++) {
float functionX = toFunctionX(screenX);
float functionY = function.compute(functionX);
int screenY = toScreenY(functionY);
g.drawLine(previousScreenX, previousScreenY, screenX, screenY);
previousScreenX = screenX;
previousScreenY = screenY;
}
}
}