""" Beispielprogramm zur Demonstration von Objektorientiertem Programmieren

Es werden verschieden Stempelklassen definiert die mit Matplotlib
interaktiv genutzt werden koennen. Die Funktionsweise folgt einem simplen
Zeichenprogramm.

Im linken Fenster-Bereich Symbol zum plotten auswaehlen,
im rechten Bereich plotten.

Rechte Maustaste beendet das Programm.
"""

import numpy as np
from matplotlib import pyplot as plt


class Stempel:
    """Stempel-Basisklasse."""
    def __init__(self, farbe=(0, 0, 0), groesse=1.0):
        """Basiseigenschaften festlegen."""
        self.farbe = farbe
        self.groesse = groesse
        self.daten = None

    def plotten(self, x, y):
        """ Ausgabe mittels matplotlib """
        plt.plot(x + self.daten[0]*self.groesse,
                 y + self.daten[1]*self.groesse,
                 color=self.farbe, linewidth=2.5)


class LinienStempel(Stempel):
    """Nutze die Basisklasse um eine einfache Linie zu plotten."""
    def __init__(self, farbe=(0, 0, 0), groesse=1.0):
        """Basiseigenschaften festlegen."""
        # ueberschriebene Methode aufrufen
        super(LinienStempel, self).__init__(farbe, groesse)
        self.daten = [np.array([-0.5, 0.5]), np.array([-0.5, 0.5])]


class PolygonStempel(Stempel):
    """Nutze die Basisklasse um einen Linienzug zu plotten."""
    def __init__(self, polygon, farbe=(0, 0, 0), groesse=1.0):
        """Basiseigenschaften festlegen."""
        # ueberschriebene Methode aufrufen
        super(PolygonStempel, self).__init__(farbe, groesse)
        self.daten = polygon


class GaussStempel(Stempel):
    """Plotte einen Gaussfoermigen Abdruck.

    Ignoriert die Farbe, da Graustufendruck.
    """
    def __init__(self, farbe=(0, 0, 0), groesse=1.0, breite=2.5):
        """Basiseigenschaften festlegen."""
        super(GaussStempel, self).__init__(farbe=farbe, groesse=groesse)
        self.N = 11
        self.daten = (1 -
            np.exp(-np.linspace(-breite, breite, self.N)[:, np.newaxis]**2
                   -np.linspace(-breite, breite, self.N)[np.newaxis, :]**2))

    def plotten(self, x, y):        # Ersetzen der plotten-Methode von Stempel
        """ Ausgabe mittels matplotlib """
        plt.imshow(self.daten,
                   extent=(x-self.groesse/2, x+self.groesse/2,
                           y-self.groesse/2, y+self.groesse/2),
                   cmap=plt.cm.gray)


class GaussAlphaStempel(GaussStempel):
    """Plotte einen Gaussfoermigen Abdruck mit Alphamaske.

    Basiert auf dem GaussStempelund fuegt die Maske und
    rudimentaere Farbwahl hinzu.
    """
    def __init__(self, farbe=(1., 1., 1.), groesse=1.0, breite=1.5):
        """Basiseigenschaften festlegen."""
        super(GaussAlphaStempel, self).__init__(farbe=farbe, groesse=groesse,
                                                breite=breite)

        # neue Daten fuer alpha blending
        #  self.daten wurde schon in Basisklasse (GaussStempel) bestimmt
        z = np.ones((self.N, self.N, 4))
        z[:, :, 0:3] = self.farbe
        z[:, :, 3] = 1.0 - self.daten

        self.daten = z


class Plotter:
    """ Die Benutzeroberflaeche

        Verwendet Stempel um zu plotten.
    """
    def __init__(self):
        self.ypos_auswahl = []
        self.stempel = []

        # Auswahlmenue befuellen
        self.stempel_eintragen(0.1, LinienStempel(groesse=0.1))
        self.stempel_eintragen(0.3, LinienStempel(groesse=0.1,
                                                  farbe=(1.0, 0, 0)))
        poly = [np.array([-0.5, 0.0, 0.5, -0.5]),
                np.array([-0.5, 0.5, 0.5, -0.5])]
        self.stempel_eintragen(0.5, PolygonStempel(poly, groesse=0.1,
                                                   farbe=(0, 1.0, 0)))
        self.stempel_eintragen(0.7, GaussStempel(groesse=0.2))
        self.stempel_eintragen(0.9, GaussAlphaStempel(groesse=0.2,
                                                      farbe=(1.0, 0.0, 0.0)))

        self.gewaehlter_stempel = self.stempel[0]
        plt.plot([0.2, 0.2], [0.0, 1.0], color=(0, 0, 0), linewidth=2)
        plt.axis((0.0, 1.0, 0.0, 1.0))

    def stempel_eintragen(self, y, stempel):
        """Auswahlmenueeintrag erstellen."""
        self.ypos_auswahl.append(y)
        self.stempel.append(stempel)
        print("stempel.plotten(0.1, %f)" % y)
        stempel.plotten(0.1, y)

    def mauseingabe(self, event):
        """Auswertung der Mauseingabe.

        rechte Maustaste : schliesse alle Fenster
        linke Maustaste  : waehle neuen Stempel bzw.
                           drucke aktuellen Stempel
        """
        if event.button == 3:                   # Ende bei rechter Maustaste
            plt.close('all')
            import sys
            sys.exit(0)

        if event.xdata < 0.2:                   # Auswahl aendern
            i = abs(np.array(self.ypos_auswahl) - event.ydata).argmin()
            self.gewaehlter_stempel = self.stempel[i]

        else:                                   # Auswahl plotten
            print("gewaehlter_stempel.plotten(%f, %f)" % \
                  (event.xdata, event.ydata))
            self.gewaehlter_stempel.plotten(event.xdata, event.ydata)
            plt.axis((0, 1, 0, 1))
            plt.draw()


if __name__ == "__main__":
    print(__doc__)
    p = Plotter()
    plt.connect('button_press_event', p.mauseingabe)
    plt.show()
