Die Flugbahn einer Biene (Oder: Animationen mit Neigung auf Basis von differenzierbaren Funktionen)

  • Hi!
    Heute melde ich mich mal mit einem etwas ungewöhnlichen Titel. Es geht um die (idealisierte) Flugbahn einer Biene. Ob Bienen sich wirklich so verhalten, sei mal dahingestellt, ich habe jedenfalls eine sinusförmige Flugbahn angenommen. :D
    Das klingt für sich genommen ja noch recht einfach, im beliegenden Skript Nr. 1 ist diese Aufgabe auch relativ leicht bewältigt worden. Man sieht allerdings, dass die Biene immer nur starr bleibt, das Bild also nie geneigt wird. Das lässt das ganze etwas starr wirken, finde ich. Ich habe mir also Gedanken gemacht, wie man eine realistische Neigung erzeugen kann.


    1. Die Berechnung
    Nun, wer in der Schule schon die Differentialrechnung kennengelernt hat, erfährt hier (hoffentlich) nichts neues. Die Flugbahn folgt der Funktion f(x)=sin(x), wobei hier alle trigonometrischen Funktionen mit Gradzahlen arbeiten, nicht mit dem Bogenmaß. Um die Neigung der Bienen-Grafik berechnen zu können, müssen wir in der Lage sein, an jeder Stelle von f(x) die Steigung zu bestimmen. Das Wort "Steigung" ist in diesem Zusammenhang synonym zu "Tangenten-Steigung" und "Änderungsrate". Als Mathematik- oder Physik-Leistungskurs-Schüler weiß man, dass (sin(x))'=cos(x) gilt, d.h. dass die Ableitung des Sinus' der Cosinus ist. Wer mit dem Begriff "Ableitung" überfordert ist, sollte sich einmal das hier anschauen. Wer sich fragt, warum genau der Cosinus die Ableitung vom Sinus ist, der schaut hier. Jedenfalls können wir mit der Funktion f'(x)=cos(x) bequem die momentane Steigung bestimmen. Für die weitere Verarbeitung hätten wir diese Steigung aber lieber als Winkel zur X-Achse angegeben, daher lassen wir unsere Steigung vorher nochmal durch die ATan-Funktion laufen (Arcus Tangens). Und schon haben wir den Neigungswinkel der Biene.
    Diese Taktik funktioniert wohlgemerkt nur, wenn wir als Flugbahn-Funktion eine differenzierbare Funktion wählen. So etwas wie f(x)=floor(x) (Treppen-Funktion) fällt damit also raus. Sollte aber auch eher der Sonderfall sein, entweder wird sowas Richtung Sinus oder Richtung quadratische Gleichung gehen.


    2. Das Zeichnen
    Die Berechnungen sind (scheinbar) erledigt, jetzt muss nur noch gezeichnet werden. Das einzige, was noch aussteht, ist die Neigung, der restliche Zeichenvorgang ist ja bereits programmiert. Doch wie neigen wir das Bild nun? Der ein oder andere wird vielleicht - wie ich anfangs auch - an die GDI+-eigenen Matrix-Funktionen denken. Gerade MatrixRotate ist in meinen Augen jedoch einfach unberechenbar. Man neigt damit die ganze Zeichenoberfläche. Wenn man an einem statischen Punkt ein Bild drehen möchte, kriegt man das mit viel Gefuddel locker hin, aber bei einer sinusförmigen Bewegung geht das nicht mehr so leicht. Es musste also eine Alternative her. Ein wenig Googlelei brachte mich dann auf die FreeImage-Bibliothek. Diese lässt sich jedoch nur mit viel Aufwand mit GDI+ verbinden; den Weg über Dateien wollte ich natürlich auch nicht gehen. Ich habe also einfach ein wenig in der GDI+-Dokumentation rumgestöbert und bin dann auf _GDIPlus_DrawImagePoints gestoßen. Ein paar schnelle Tests mit absoluten Werten haben ergeben: Ja, damit kann man ein Bild gedreht zeichnen, ohne die komplette Zeichenfläche drehen zu müssen. Jetzt galt es nur noch, das ganze in Abhängigkeit von einem Winkel zu berechnen. Mit Hilfe der trigonometrischen Funktionen und mit ein wenig Gehirnschmalz ist es mir dann gelungen, eine Funktion mit dem Namen _GDIPlus_DrawImageRotate zu schreiben:

    Spoiler anzeigen
    [autoit]


    Func _GDIPlus_DrawImageRotate($hGraphics, $hImage, $iX, $iY, $nAngle)
    ;chesstiger
    Local $nPi, $nDeg2Rad, $nAngleRad, $iW, $iH, $aP[6]

    [/autoit] [autoit][/autoit] [autoit]

    $nPi = ACos(-1)
    $nDeg2Rad = ($nPi / 180)
    $nAngleRad = $nAngle * $nDeg2Rad
    $iW = _GDIPlus_ImageGetWidth($hImage)
    $iH = _GDIPlus_ImageGetHeight($hImage)

    [/autoit] [autoit][/autoit] [autoit]

    $aP[0] = $iX
    $aP[1] = $iY
    $aP[2] = $iX + Cos($nAngleRad) * $iW
    $aP[3] = $iY + Sin($nAngleRad) * $iW
    $aP[4] = $iX + Cos($nAngleRad + $nPi / 2) * $iH
    $aP[5] = $iY + Sin($nAngleRad + $nPI / 2) * $iH

    [/autoit] [autoit][/autoit] [autoit]

    _GDIPlus_DrawImagePoints($hGraphics, $hImage, $aP[0], $aP[1], $aP[2], $aP[3], $aP[4], $aP[5])
    EndFunc

    [/autoit]


    Das muss dann nur noch mit dem Neigungswinkel verbunden werden und voilá, die Biene fliegt. :D

    Das eigentlich interessante an der ganzen Sache ist für mich die Erkenntnis, dass man mit AutoIt und der GDI+-UDF auch ohne die Matrix-Funktionen ein Bild drehen kann. Die Verbindung mit der Differentialrechnung ist natürlich auch spaßig, da man so ein wenig näher an die Realität rankommt. Das gilt natürlich auch für parabelförmige Bewegungen. Vielleicht kann ja jemand was mit meiner Spielerei anfangen. :D

    Lieben Gruß

  • Nach der Einleitung hab ich 500 Zeilen hochkomplizierten Code erwartet den man mindestens eine Stunde studieren muss bevor man in etwa weiß wie er funktioniert :D

    Die Drehfunktion zum Zeichnen ist super, ich habe eigentlich immer mit der Matrixvariante gearbeitet, deine Methode ist aber um ein vielfaches einfacher :)

    lg
    M