/* Das Plotfenster, in dem der Graph dargestellt wird */

import java.awt.*;
import java.awt.event.*;

/** PlotFrame - Klasse stellt Ausdruck graphisch dar */
public class PlotFrame extends Frame {

        // Variablendeklaration
        double m_dInitialStartX, m_dInitialEndX, m_dInitialStartY, m_dInitialEndY; // Startwerte der Grenzen
        double rangeWidth, rangeHeight; // gewählter Range
        PlotCanvas pCanvas; // die Zeichenfläche
        PlotFrame dummyThis; // unangenehme Konstrukte...
        Plotter parent; // Schön verzahnt...
        MParse parser; // Eine MParse-Referenz

        // Exportiere Variablen

        /** minimal darzustellender x-Wert */
        public double m_dStartX;

        /** maximal darzustellender x-Wert */
        public double m_dEndX;

        /** minimal darzustellender y-Wert */
        public double m_dStartY;

        /** maximal darzustellender y-Wert */
        public double m_dEndY;

        /** Referenz auf die nächste Instanz (für Fenster-Liste) */
        public PlotFrame next;

        /** Der geparste Funktionsausdruck */
        public String AusdruckString;

        /**
         * Der Constructor
         * @param Plotter Referenz auf Mutterinstanz vom Typ Plotter
         * @param String Zu parsender Funktionsausdruck
         * @param dStartX minimal darzustellender x-Wert
         * @param dEndX maximal darzustellender x-Wert
         * @param dStartY minimal darzustellender y-Wert
         * @param dEndY maximal darzustellender y-Wert
         */
        PlotFrame (Plotter parent, String AusdruckString, double dStartX, double dEndX, double dStartY, double dEndY) {

                super ("Funktion: " + AusdruckString); // Fenstertitel = Funktionsausdruck

                // Das Parsen des Strings, Anlegen eines neuen MParse-Objekts
                parser = new MParse();
                try {
                        parser.parse( AusdruckString ); // Parsen
                        parser.evaluate( dStartX ); // Einen Wert testen
                } catch ( MParseException MPEX ) {
                        this.AusdruckString = MPEX.msg; // Das darf nicht sein, daher wird gekillt
                        return;
                }

                // Übernehmen der Variablen
                this.AusdruckString = AusdruckString;
                this.parent = parent; // Für Call-Back
                m_dEndX = dEndX; // Werte abspeichern
                m_dStartX = dStartX;
                m_dStartY = dStartY;
                m_dEndY = dEndY;

                // Initialisierung
                InitPlotFrame();
        }

        /** Die eigentliche Plot-Klasse -> Erweiterung der Canvas-Klasse */
        class PlotCanvas extends Canvas {

                // Globale Variabeln der PlotCanvas-Klasse
                int MouseX, MouseY;
                boolean ShiftPressed;
                Dimension pSize;

                /** PlotCanvas - Constructor mit MouseListener für Scrollen/Zoomen */
                PlotCanvas () {

                        // Tastatur-Handling
                        KeyListener unserKeyListener = new KeyAdapter() {
                                public void keyPressed(KeyEvent e) { // Taste gedrückt
                                        if (e.getKeyCode() == KeyEvent.VK_SHIFT) { ShiftPressed = true; }
                                }

                                public void keyReleased(KeyEvent e) { // Taste losgelassen
                                        if (e.getKeyCode() == KeyEvent.VK_SHIFT) { ShiftPressed = false; }
                                }
                        };

                        // Mouse-Handling
                        MouseListener unserMouseListener = new MouseAdapter() {
                                public void mousePressed(MouseEvent e) { // Mouse gedrückt
                                        MouseX = e.getX();
                                        MouseY = e.getY();
                                }
                                public void mouseClicked(MouseEvent e) { // Zurücksetzen
                                        if (e.getClickCount() == 2) { // Bei Doppelklick
                                                m_dStartX = m_dInitialStartX;
                                                m_dEndX = m_dInitialEndX;
                                                m_dStartY = m_dInitialStartY;
                                                m_dEndY = m_dInitialEndY;
                                                rangeWidth = Math.abs(m_dEndX - m_dStartX); // Breite und...
                                                rangeHeight = Math.abs(m_dEndY - m_dStartY); // ... Höhe der Ausgabe ausrechnen
                                                repaint();
                                        }
                                }
                        };

                        // Mousebewegungshandling
                        MouseMotionListener unserMotionListener = new MouseMotionAdapter() {
                                public void mouseDragged(MouseEvent e) {

                                        // Doppelklickhandling
                                        if (e.getClickCount() == 2) { // Bei Doppelklick
                                                m_dStartX = m_dInitialStartX;
                                                m_dEndX = m_dInitialEndX;
                                                m_dStartY = m_dInitialStartY;
                                                m_dEndY = m_dInitialEndY;
                                                rangeWidth = Math.abs(m_dEndX - m_dStartX); // Breite und...
                                                rangeHeight = Math.abs(m_dEndY - m_dStartY); // ... Höhe der Ausgabe ausrechnen
                                                repaint();
                                                return;
                                        }

                                        // Abmessungen unserer Zeichenfläche holen
                                        pSize = getSize (); // .height, .width sind die Maße...

                                        // Neue Grenzen berechnen
                                        if (!ShiftPressed) { // Wenn Shift nicht gedrückt, dann verschieben
                                                m_dStartX += (MouseX - e.getX()) * rangeWidth / pSize.width;
                                                m_dEndX += (MouseX - e.getX()) * rangeWidth / pSize.width;
                                                m_dStartY -= (MouseY - e.getY()) * rangeHeight / pSize.height;
                                                m_dEndY -= (MouseY - e.getY()) * rangeHeight / pSize.height;
                                                rangeWidth = Math.abs(m_dEndX - m_dStartX); // Breite und...
                                                rangeHeight = Math.abs(m_dEndY - m_dStartY); // ... Höhe der Ausgabe ausrechnen
                                        } else { // Wenn Shift gedrückt, dann spezial-zoom
                                                if (MouseX - e.getX() > 0) {
                                                        m_dStartX -= (MouseX - e.getX()) * rangeWidth / pSize.width;
                                                        m_dEndX += (MouseX - e.getX()) * rangeWidth / pSize.width;
                                                } else {
                                                        m_dStartX -= (MouseX - e.getX()) * rangeWidth / pSize.width;
                                                        m_dEndX += (MouseX - e.getX()) * rangeWidth / pSize.width;
                                                }
                                                if (MouseY - e.getY() > 0) {
                                                        m_dStartY -= (MouseY - e.getY()) * rangeHeight / pSize.height;
                                                        m_dEndY += (MouseY - e.getY()) * rangeHeight / pSize.height;
                                                } else {
                                                        m_dStartY -= (MouseY - e.getY()) * rangeHeight / pSize.height;
                                                        m_dEndY += (MouseY - e.getY()) * rangeHeight / pSize.height;
                                                }
                                                rangeWidth = Math.abs(m_dEndX - m_dStartX); // Breite und...
                                                rangeHeight = Math.abs(m_dEndY - m_dStartY); // ... Höhe der Ausgabe ausrechnen
                                        }

                                        MouseX = e.getX(); // Neue "alte" Werte Zwischenspeichern
                                        MouseY = e.getY();
                                        repaint();
                                }
                        };

                        addMouseListener(unserMouseListener); // Mouse-Event-Handling aktiv.
                        addMouseMotionListener(unserMotionListener);
                        addKeyListener(unserKeyListener);
                }

                public void paint(Graphics g) {

                        /* Der aktuelle X-Wert (Abszisse) */
                        double dAbszisse;

                        /* Der aktuelle Funktionswert (Ordinate) */
                        double dFunktionsWert;

                        /* Das Gitternetz wird erzeugt, indem sowohl in
                        horizontaler als auch in vertikaler Richtung der
                        darzustellende Bereich in 10 gleichgroße Intervalle
                        unterteilt wird. Ist z.B. der darzustellende Bereich
                        in x-Richtung von 2 bis 4, und in y-Richtung von
                        -1 bis 7, so wird in x-Richtung (y-Richtung) alle
                        0,2 Einheiten (0,8 Einheiten) eine Linie dargestellt.
                        Diese Variable hält die Länge dieses Intervalls, das
                        bedeutet, den Abstant zwischen zwei dieser Linien. */
                        long lRasterAbstand;

                        long addFaktor; // Für Achsenbeschriftung

                        /* Größe des Applets holen */
                        pSize = getSize ();

                        /* Wenn das Applet-Fenster zu klein ist, versuchen
                        wir die Darstellung gar nicht erst, sondern zeichnen
                        lediglich ein rotes "X". */
                        if ((pSize.height < 80) || (pSize.width < 80)) {
                                g.setColor(Color.red);
                                g.drawLine (0, 0, pSize.width - 1, pSize.height - 1);
                                g.drawLine (0, pSize.height - 1, pSize.width - 1, 0);
                                return;
                        }

                        /* Eine bestimmte Schriftart für die
                        Achsenbeschriftungen verwenden */
                        g.setFont ( new Font("SansSerif", Font.PLAIN, 12) );

                        /* Die aktuelle X-Position in dem Zeichenbereich */
                        int iScreenX;

                        /* Die aktuelle Y-Position in dem Zeichenbereich */
                        int iScreenY;

                        /* Der "gemerkte" Wert für die Y-Pixelposition des
                        letzten Durchlaufs. Dieser Wert wird benötigt, um
                        nicht nur den Punkt an (iScreenX,iScreenY)
                        zeichnen zu können, sondern um eine Linie, ausgehend
                        von (iScreenX-1,iOldScreenY) nach (iScreenX,iScreenY)
                        zeichnen zu können; denn das sieht wesentlich
                        schöner aus. */
                        int iOldScreenY;

                        // Achsen zeichnen, wenn diese im Range liegen
                        if ((m_dStartX <= 0) && (m_dEndX >= 0)) { // Y-Achse
                                g.setColor (Color.black);
                                iScreenX = (int)( (0 - m_dStartX) * pSize.width / rangeWidth);
                                g.drawLine ( iScreenX, 0, iScreenX, pSize.height - 1 );
                                g.drawString ("y-Achse", iScreenX - 47, 13);

                                // Skala auf Y-Achse einzeichnen
                                lRasterAbstand = Math.round (rangeHeight / 10); // lRasterAbstand berechnen
                                if (lRasterAbstand != 0) {
                                        if (m_dStartY > 0) {
                                                addFaktor = Math.round(m_dStartY) - 1;
                                        } else if (m_dEndY < 0) {
                                                addFaktor = -Math.round(m_dEndY) - 1;
                                        } else { addFaktor = 0; } // Wenn 0 in der Mitte liegt

                                        for (int i = 1; i < 12; i++) {
                                                iScreenY = (int)( pSize.height - (i * lRasterAbstand + addFaktor - m_dStartY) * pSize.height / rangeHeight);
                                                if ((iScreenY > 0) && (iScreenY < pSize.height - 1)) {
                                                        g.drawLine ( iScreenX - 2, iScreenY, iScreenX + 2, iScreenY); // horizontales Strichlein rechts von 0
                                                        g.drawString (
                                                            new Long(i * lRasterAbstand + addFaktor).toString(), iScreenX + 5, iScreenY + 5);
                                                }
                                                iScreenY = (int)( pSize.height - (0 - m_dStartY - i * lRasterAbstand - addFaktor) * pSize.height / rangeHeight);
                                                if ((iScreenY > 0) && (iScreenY < pSize.height - 1)) {
                                                        g.drawLine ( iScreenX - 2, iScreenY, iScreenX + 2, iScreenY); // horizontales Strichlein links von 0
                                                        g.drawString ("-" + new Long(i * lRasterAbstand + addFaktor).toString(), iScreenX + 5, iScreenY + 5);
                                                }
                                        }
                                }
                        }
                        if ((m_dStartY <= 0) && (m_dEndY >= 0)) { // X-Achse
                                g.setColor (Color.black);
                                iScreenY = pSize.height - (int)( (0 - m_dStartY) * pSize.height / rangeHeight);
                                g.drawLine ( 0, iScreenY, pSize.width - 1, iScreenY);
                                g.drawString ("x-Achse", pSize.width - 47, iScreenY - 4);

                                // Skala auf X-Achse einzeichnen
                                lRasterAbstand = Math.round (rangeWidth / 10); // lRasterAbstand berechnen
                                if (lRasterAbstand != 0) {
                                        if (m_dStartX > 0) {
                                                addFaktor = Math.round(m_dStartX) - 1;
                                        } else if (m_dEndX < 0) {
                                                addFaktor = -Math.round(m_dEndX) - 1;
                                        } else { addFaktor = 0; } // Wenn 0 in der Mitte liegt

                                        for (int i = 1; i < 12; i++) {
                                                iScreenX = (int)( (i * lRasterAbstand + addFaktor - m_dStartX) * pSize.width / rangeWidth);
                                                if ((iScreenX > 0) && (iScreenX < pSize.width - 1)) {
                                                        g.drawLine ( iScreenX, iScreenY - 2, iScreenX, iScreenY + 2); // vertikales Strichlein rechts von 0
                                                        g.drawString (
                                                            new Long(i * lRasterAbstand + addFaktor).toString(), iScreenX - 2, iScreenY + 14);
                                                }
                                                iScreenX = (int)( (0 - m_dStartX - i * lRasterAbstand - addFaktor) * pSize.width / rangeWidth);
                                                if ((iScreenX > 0) && (iScreenX < pSize.width - 1)) {
                                                        g.drawLine ( iScreenX, iScreenY - 2, iScreenX, iScreenY + 2); // vertikales Strichlein links von 0
                                                        g.drawString ("-" + new Long(i * lRasterAbstand + addFaktor).toString(), iScreenX - 6, iScreenY + 14);
                                                }
                                        }
                                }
                        }

                        /* Zeichenfarbe setzen */
                        g.setColor (Color.blue);

/*
###############################################################################
Bitte UNTERHALB dieses Kommentars den Programmcode einfügen
###############################################################################
*/

int i=1;
int x;
int y;



while (i<=pSize.width){

	dAbszisse=(i)/(pSize.width/(m_dEndX-m_dStartX))+m_dStartX;
	
	dFunktionsWert=parser.evaluate(dAbszisse);

	y=pSize.height-(int)((dFunktionsWert-m_dStartY)*(pSize.height/(m_dEndY-m_dStartY)));
	
	
	// x=(int)(i*(pSize.width/(m_dEndX-m_dStartX)));

	g.drawLine(i,y,i,y);
	
	i++;	}


/*
###############################################################################
Bitte OBERHALB dieses Kommentars den Programmcode einfügen
###############################################################################
*/

                }
        }


        private void InitPlotFrame() {

                // Ein paar allgemeinere Inits und Checks
                if (m_dEndX == Double.NaN) { m_dEndX = 5; }
                if (m_dStartX == Double.NaN) { m_dStartX = -5; }
                if (m_dEndY == Double.NaN) { m_dEndY = 5; }
                if (m_dStartY == Double.NaN) { m_dStartY = -5; }

                m_dInitialEndX = m_dEndX; // Startwerte der Grenzen speichern
                m_dInitialStartX = m_dStartX;
                m_dInitialStartY = m_dStartY;
                m_dInitialEndY = m_dEndY;

                rangeWidth = Math.abs(m_dEndX - m_dStartX); // Breite und...
                rangeHeight = Math.abs(m_dEndY - m_dStartY); // ... Höhe der Ausgabe ausrechnen

                dummyThis = this;

                // Ein paar AWT-Inits
                WindowListener ExitListener = new WindowAdapter() {
                        // Event-Methode die beim Schließen aufgerufen wird
                        public void windowClosing (WindowEvent ThisEvent) {
                                setVisible (false); // Fenster verschwinden lassen
                                parent.KickMe(dummyThis); // Aus Fensterliste kicken...
                                // dispose(); // und killen...
                        }
                };

                addWindowListener(ExitListener); // Event-Handling aktiv.

                pCanvas = new PlotCanvas();
                add (pCanvas);
                setSize(400, 400); // Größe wie in html-Datei
                show(); // und anzeigen (visible=true)...
        }
}

