Animation einer Polygonrotation mit Excel-VBA

 

Abstract

 

In this animation the user can watch a polygon rotating about an arbitrary pivotal point. The polygon, the pivot, and a positive rotation angle can be defined by the user.

 

In the following text the mathematical basis of polygon rotation is shortly described. Then the program and its usage are depicted in detail.

 

The Excel workbook containing the whole program may be downloaded. There is no password protection.

 

 

Zusammenfassung

 

In dieser Animation kann der Benutzer sehen, wie ein Polygon um einen beliebigen Drehpunkt rotiert. Das Polygon,  der Drehpunkt und ein positiver Drehwinkel können vom Benutzer bestimmt werden.

 

Zunächst werden die mathematischen Grundlagen der Polygonrotation kurz behandelt. Anschließend werden das Programm und seine Benutzung eingehend beschrieben.

 

Die Arbeitsmappe kann heruntergeladen werden.  Der Code ist nicht passwortgeschützt.

 

 

 

Grundlagen

 

Im Folgenden werden kurz die mathematischen Grundlagen besprochen, welche dem Programm zugrunde liegen. Es wird jedoch nicht darauf eingegangen, wie die präsentierten Formeln abgeleitet werden können. Der interessierte Leser sollte mit Hilfe der mathematischen Fachliteratur infor­mieren.

 

 

Wie beschreiben wir ein Polygon?

 

Ein Polygon (Vieleck, n-Eck) ist gegeben durch seine n Eckpunkte und die Reihenfolge, in denen diese Eckpunkte zu verbinden sind, damit sich der Umriss (Polygonzug) ergibt. Zur Definition eines bestimmten Polygons genügt es daher, seine Punkte in der richtigen Reihenfolge anzugeben.

 

Ein Dreieck ist z.B. durch die Aufzählung seiner drei Eckpunkte (P1, P2, P3) bestimmt. Aus der Auf­zählung erkennen wir, dass der Polygonzug von P1 über P2 zu P3 führt und von dort aus wieder zu P1 zurück.

 

 

Die Rotation eines Punkts um einen anderen Punkt

 

Weil das Polygon durch seine Eckpunkte und deren Abfolge bestimmt ist, kann auch die Rotation eines Polygons auf die Rotation seiner Eckpunkte zurückgeführt werden. Wir wollen deshalb zunächst die Rotation eines Punkts um einen anderen Punkt betrachten. Diesen anderen Punkt bezeichnen wir als Drehpunkt oder als Rotationszentrum.

 

Das folgende Bild veranschaulicht die Rotation eines Punkts (rot) um den schwarz eingezeichneten Drehpunkt. Das Ausmaß der Rotation wird durch den Rotationswinkel α ausgedrückt. Mit einem positiven Winkel, wie im Bild, ist eine Drehung gegen den Uhrzeigersinn verbunden. Wie man sieht, bewegt sich der rotierende Punkt auf einem Kreis um den Drehpunkt, dessen Radius dem Abstand des Punkts zum Drehpunkt entspricht.

 

 

 

Die Koordinaten (xr, yr) des rotierten Punkts können wir leicht aus denen des ursprünglichen Punkts und dem Rotationswinkel α errechnen, wenn der Drehpunkt mit dem Ursprung des Koordinaten­systems zusammenfällt. Wir müssen in diesem Fall nur die einspaltige Matrix des Punkts von links mit der Transformationsmatrix multiplizieren, welche der Drehung um den Winkel α entspricht.

 

 

Beachten Sie, dass in der Formel der Rotationswinkel α durch sein Bogenmaß ausgedrückt wird.

 

Diese Formel können wir auch dann benutzen, wenn der Drehpunkt  nicht dem Ursprung entspricht. Wir erreichen dies durch einen kleinen Trick. Den zu rotierenden Punkt „normieren“ wir, indem wir von seinen Koordinaten die Koordinaten (xc, yc) des Drehpunkts C abziehen.

 

 

Dann wenden wir die obige Rotationstransformation auf den normierten Punkt an:

 

 

Zum Schluss nehmen wir die Normierung wieder zurück und erhalten damit die Koordinaten (xr, yr) des um den Drehpunkt C rotierten Punkts

 

 

 

 

Die Rotation eines Polygons um einen beliebigen Punkt

 

Um ein n-Eck zu rotieren, rotieren wir alle seine n Eckpunkte. Wir wollen zunächst wieder an­nehmen, dass die Rotation um den Ursprung geschieht. In diesem Fall erhalten wir eine Matrix mit den Ko­ordinaten der gedrehten Eckpunkte, wenn wir die Matrix der ursprünglichen Eckpunkte von links mit der Transformationsmatrix multiplizieren:

 

 

Wollen wir das Polygon um einen beliebigen Drehpunkt C rotieren lassen, so müssen wir zuerst, wie oben gezeigt, alle seine Eckpunkte in „normierte“ Punkte verwandeln, indem wir von jedem dieser Punkte den Drehpunkt abziehen.

 

 

Wenn wir dann die Matrix der normierten Punkte von links mit der Transformationsmatrix multiplizieren, erhalten wir die Matrix der rotierten normierten Punkte:

 

 

Addieren wir danach zu jedem rotierten normierten Eckpunkt den Drehpunkt, so ergibt sich die Matrix der rotierten Eckpunkte:

 


 

 

Eine VBA-Funktion für die Polygonrotation


Die folgende Funktion polyRot beruht im Prinzip auf den obigen Überlegungen. Lediglich bei den Benennungen und einigen anderen Details gibt es Abweichungen.


Eine dieser Abweichungen betrifft die Anordnung der Punkte in den Parametern p und c. Während im vorhergehenden Abschnitt  in den Polygon-Matrizen jede Spalte einem Eckpunkt entspricht, sind in den Parameter-Arrays p und c die Punkte zeilenweise angeordnet. Die Koordinaten des ersten Eckpunkts sind also p(1, 1) und p(1, 2), die des zweiten p(2, 1) und p(2, 2), usw.  Der Grund für diese Abweichung ist, dass die zeilenweise Anordnung der Punkte intuitiver erscheint. Dasselbe gilt für das Array c, das in seiner einzigen Zeile die Koordinaten des Drehpunkts enthält.


Damit das Punkte-Array p im Rahmen der obigen Formeln genutzt werden kann, muss es erst in ein Array mit der passenden Ausrichtung verwandelt, also transponiert werden. Hierfür wird die Funktion TranspDbl im Modul matrDbl verwendet. Vor dem Transponieren wird bereits die oben beschriebene Normierung der Punkte durchgeführt, d.h. von ihren Koordinaten werden die Koordinaten des Drehpunkts abgezogen. Dies geschieht der Einfachheit halber nicht mit einer Matrixoperation, sondern Punkt für Punkt in einer Schleife.


Das Modul matrDbl enthält auch eine Funktion MProd für die Matrixmultiplikation, welche benutzt wird, um die transponierte Punktematrix mit der Rotationsmatrix zu multiplizieren. Zwar stehen Funktionen zum Transponieren und Multi­plizieren von Matrizen auch im Rahmen der Excel-Funktionen (worksheet functions) zur Verfügung, jedoch verlangen diese Funktionen Arrays vom Typ Variant als Input und sind für Double-Arrays nicht brauchbar.


Da polyRot ein Array liefern soll, in dem die Polygon-Eckpunkte zeilenweise angeordnet sind, wird am Ende der Funktion nochmals die Funktion TransDbl bemüht, um das Ergebnis in diese Anordnung zu bringen.


Nachfolgend der Code von polyRot:


'rotates polygon p about point c through angle alpha

Public Function polyRot(ByRef p() As Double, _

                                   ByRef c() As Double, _

                                   ByVal alpha As Single) As Double()

 

    Dim a() As Double                          'shifted (normalized) points

    Dim n() As Double                          'normalized points, transposed

    Dim v() As Double                           'shifted points, rotated

    Dim r() As Double                           'rotated points

    Dim t(1 To 2, 1 To 2) As Double        'transformation matrix

    Dim i As Integer, j As Integer

   

    'express angle in radian measure

    alpha = WorksheetFunction.Pi * alpha / 180

       

    'shift points & transpose matrix

    ReDim a(1 To UBound(p, 1), 1 To 2)

    For i = 1 To UBound(p, 1)

        a(i, 1) = p(i, 1) - c(1, 1)

        a(i, 2) = p(i, 2) - c(1, 2)

    Next i

    n = matrDbl.TranspDbl(a)

   

   

    'Build rotation matrix

    t(1, 1) = Cos(alpha): t(1, 2) = -Sin(alpha)

    t(2, 1) = Sin(alpha): t(2, 2) = Cos(alpha)

   

    'apply rotation matrix to shifted points

    v = matrDbl.MProd(t, n)

   

    're-transpose matrix of points & undo shift

    r = matrDbl.TranspDbl(v)

    For i = 1 To UBound(p, 1)

        r(i, 1) = r(i, 1) + c(1, 1)

        r(i, 2) = r(i, 2) + c(1, 2)

    Next i

   

    polyRot = r

 

End Function


 

Von dem Modul matrDbl, welches diverse Matrix-Operationen auf der Basis von Double-Arrays enthält, werden nachfolgend nur die beiden Funktionen zum Transponieren und Multiplizieren aufgeführt, die von polyRot aus aufgerufen werden:


 

Public Function TranspDbl(ByRef m() As Double) As Double()

    Dim t() As Double

    ReDim t(1 To UBound(m, 2), 1 To UBound(m, 1))

    Dim i As Integer, j As Integer

    For i = 1 To UBound(m, 2)

        For j = 1 To UBound(m, 1)

            t(i, j) = m(j, i)

        Next j

    Next i

    TranspDbl = t

End Function

 

Public Function MProd(ByRef a() As Double, ByRef b() As Double) As Double()

    Dim p() As Double

    If UBound(a, 2) <> UBound(b, 1) Then

        ReDim p(-1 To -1)

        MProd = p

        Exit Function

    End If

    ReDim p(1 To UBound(a, 1), 1 To UBound(b, 2))

    Dim i As Integer, j As Integer, k As Integer

    For i = 1 To UBound(a, 1)

        For j = 1 To UBound(b, 2)

            p(i, j) = 0

            For k = 1 To UBound(b, 1)

                p(i, j) = p(i, j) + a(i, k) * b(k, j)

            Next k

        Next j

    Next i

    MProd = p

End Function

 

 




Die Animation


Die Arbeitsmappe PolygonRotation enthält zwei Arbeitsblätter. Das Arbeitsblatt StartRotation nimmt die Daten des Polygons auf. Es enthält außerdem eine Schaltfläche, mit der das Programm gestartet werden soll.  Das zweite Arbeitsblatt, PolygonRotation, ist anfangs leer. Nach dem Starten der Rotation enthält es die Graphik mit dem rotierenden Vieleck.


Das folgende Bild zeigt Arbeitsblatt StartRotation nach dem Öffnen der Arbeitsmappe. Auf der linken Seite sieht man die erwähnte Schaltfläche und darunter die Daten für das zu rotierende Vieleck.  Anfangs enthält die Arbeitsmappe Beispieldaten für ein Fünfeck, aber man kann diese Daten nach Belieben verändern.  Für die Anzahl der Punkte gibt es keine nennenswerte Begrenzung. Wenn man unbedingt will, kann man auch die Rotation eines Hundert- oder eines Tausendecks studieren.

 

Es ist auch möglich, die Tabelle mit den Polygonecken an einer anderen Stelle des Arbeitsblatts oder auf einem anderen Arbeitsblatt unterzubringen. 


Es ist wichtig, dass die Punkte in der Tabelle so angeordnet sind, dass sich die Außenlinie des Polygons ergibt, wenn man ihnen folgt. Der Startpunkt ist am Ende nochmals aufgeführt, damit sich der Polygonzug schließt.


Ob die Punkte in der Tabelle in sinnvoller Reihenfolge aufgeführt sind, wird vom Programm nicht überprüft.


Klickt der Benutzer auf die mit „Animation starten“ beschriftete Schaltfläche, so wird ein kleines Formular geöffnet, das der Eingabe der Rotationsparameter dient. Diese sind


  • Der Bereich, in dem sich die Auflistung der Eckpunkte befindet. Die Eingabe kann durch Selektieren mit der Maus erfolgen.  Die Überschrift der Tabelle wird dabei nicht mit einbezogen.
  • Der Drehpunkt (Rotationszentrum). Er kann beliebig gewählt werden.
  • Der Drehwinkel. Er kann größer als 360 ° sein, aber nicht negativ.


Nach der Eingabe dieser Parameter genügt es, auf die Schaltfläche „Start Animation“ zu drücken, damit der Rotationslauf beginnt.



Die folgende Bilderserie zeigt Ausschnitte aus der Animation. Das Polygon selbst ist als roter Linienzug dargestellt. Im Beispiel handelt es sich offensichtlich um ein nichtkonvexes Fünfeck. Der Drehpunkt ist als grünes Dreieck sichtbar. Hier liegt er außerhalb des Polygons. Die blauen Punkte an den Ecken der Graphik haben mit der Rotation selbst nichts zu tun. Sie sorgen nur dafür, dass in der Graphik der relevante Ausschnitt des Koordinatensystems gezeigt wird (s. unten Animations­programm)



Sobald die Figur zum Stillstand gekommen ist, kann der Benutzer die Animation neu starten, gegebenenfalls mit neuen Parametern, oder den Programmlauf durch Klicken auf die rote Schaltfläche in der rechten oberen Ecke des Formulars beenden.


 

 


 

Das Animationsprogramm


Das Programm besitzt eine strenge vierschichtige Architektur.  Die oberste Schicht besteht aus dem Formularmodul PolygonRotationForm. Es koordiniert die Interaktion zwischen Programm und Benutzer und liest von diesem die Parameter der Animation ein.


Die zweite Schicht enthält nur das Standardmodul Rotanimation, welches die Animation gemäß den aus dem Formularmodul übergebenen Parametern durchführt.  Rotanimation bedient sich hierzu des Moduls Polygon, dessen bereits beschriebene Funktion polyRot die Eckpunkte eines um einen bestimmten Winkel rotierten Polygons liefert (s. oben). Das Standardmodul MatrDbl wird, wie bereits oben beschrieben,  von polyRot für diese Berechnungen benötigt.



 

 

Das Formularmodul PolygonRotationForm


Der Hauptteil dieses Moduls ist eine Ereignisprozedur ,  welche die Schritte enthält, die nach dem Anklicken der Schaltfläche mit der Aufschrift „Starte Animation“ ausgeführt werden sollen. Daneben enthält das Modul noch eine Funktion RangeTo DblArray, die ein Range-Objekt in ein Array vom Typ Double verwandelt.  Diese Umwandlung ist nötig, weil die Funktion polyRot des Moduls Polygon die Eckpunkte des zu drehenden Polygons in Form eines Double-Arrays verlangt.


Eine differenzierte Validierung der Eingabedaten wird nicht vorgenommen. Diese wäre für das RefEdit-Element auch sehr aufwändig. Deshalb werden eventuelle Fehler mit Hilfe eines On Error GoTo en bloc abgefangen.


Die Tätigkeit der Ereignisprozedur beschränkt sich darauf, die Eingaben in eine geeignete Form zu verwandeln und dann das Modul Rotanimation mit einem Aufruf der Prozedur rotateAngle mit der Abwicklung der Animation zu beauftragen. Mit diesem Aufruf werden vier Parameter übergeben (von links nach rechts):


  • Ein Array mit den Eckpunkten des Polygons
  • Ein Array mit den Koordinaten des Drehpunkts
  • Der Drehwinkel
  • Einen Wert  für die Verzögerungen, die zwischen die Einzelschritte der Animation eingefügt werden sollen.


Nachfolgend der Code des Moduls:

 

'when StartBtn has been clicked

 

Private Sub StartBtn_Click()

    Dim p() As Double                          'polygon

    Dim c(1 To 1, 1 To 2) As Double      'rotation centre

    Dim angle As Single                       'rotation angle

    Dim inw As Worksheet                   'worksheet containing input data

   

    On Error GoTo errorhandler

 

    Set inw = Worksheets("StartRotation")

    c(1, 1) = CDbl(Me.xTBx.Text)

    c(1, 2) = CDbl(Me.yTBx.Text)

    p = RangeToDblArray(Range(Me.PolygonRefEd.Text))

    angle = CSng(Me.angleTBx.Text)

    Rotanimation.rotateAngle p, c, angle, 1 / 50

    Exit Sub

   

errorhandler:

    MsgBox "Fehler: Bitte prüfen Sie die Eingabe"

End Sub

 

 

'converts a Range object into an array of Double

 

Public Function RangeToDblArray(ByVal r As Range) As Double()

    Dim d() As Double

    ReDim d(1 To r.Cells.rows.Count, 1 To r.Cells.Columns.Count)

    Dim i As Integer, j As Integer

    For i = 1 To UBound(d, 1)

        For j = 1 To UBound(d, 2)

            d(i, j) = CDbl(r.Cells(i, j).Value)

        Next j

    Next i

    RangeToDblArray = d

End Function

 

 

 

 

Das Animationsmodul Rotanimation


Das Modul besteht aus der zentralen Prozedur rotateAngle und vier weiteren Routinen, an die rotateAngle Teilaufgaben delegiert.  Darüber hinaus benutzt rotateAngle die Funktion polyRot des Moduls Polygon und damit indirekt auch Routinen des Moduls MatrDbl.


Obwohl rotateAngle eine beträchtliche Länge besitzt, ist der Inhalt dieser Prozedur doch schnell erklärt:

Es wird eine Graphik (ein Chart) aufgebaut, welches das Polygon und den Drehpunkt als eigen­stän­dige Datenreihen enthält Dann werden mit Hilfe einer Schleife die Polygon-Datenreihe und ihre Darstellung in der Graphik sukzessive verändert.  Bei jeder dieser Veränderungen dreht sich das Polygon um ein Grad in die Richtung auf die angestrebte Endposition. Da zwischen die Verände­rungen nur geringe zeitliche Verzögerungen eingeschoben werden, erscheint ihre Abfolge dem Betrachter als eine einzige langsame Bewegung.


Eine detaillierte Betrachtung der Routine zeigt, dass tatsächlich nicht nur zwei, sondern drei Datenreihen in die Graphik aufgenommen werden.  Die dritte Reihe,  in den Kommentaren „frame“ genannt, stellt sicher, dass die Graphik den relevanten Ausschnitt des Koordinatensystems anzeigt und sich dieser Ausschnitt während der Animation nicht verändert.


Hier ist der Code des Moduls:

 

'Animates polygon rotation using parameters

'  -  p      corners of the polygon

'  -  rc     centre point of rotation

'  -  angle  rotation angle

'  -  dly    delay

 

Public Sub rotateAngle(ByRef p() As Double, _

                                  ByRef rc() As Double, _

                                  ByVal angle As Single, _

                                  ByVal dly As Single)

   

   

    Dim serA As Series   'for the polygon

    Dim serB As Series   'for the center point

    Dim serC As Series   'frame

    Dim dur As Single    'delay

    Dim w As Worksheet   'output worksheet

    Dim dr As Range      'where the chart will be shown

    Dim sh As Shape

    Dim i As Integer

    Dim pts() As Double

    Dim md As Double

    Dim xmin As Double, xmax As Double

    Dim ymin As Double, ymax As Double

    Dim f(1 To 5, 1 To 2) As Double

   

    'calculate minimum and maximum coordinates

    md = maxDist(p, rc)

    xmin = Round(rc(1, 1) - md - 1)

    xmax = Round(rc(1, 1) + md + 1)

    ymin = Round(rc(1, 2) - md - 1)

    ymax = Round(rc(1, 2) + md + 1)

   

    'fill array for the frame

    f(1, 1) = xmin: f(1, 2) = ymin

    f(2, 1) = xmax: f(2, 2) = ymin

    f(3, 1) = xmax: f(3, 2) = ymax

    f(4, 1) = xmin: f(4, 2) = ymax

    f(5, 1) = xmin: f(5, 2) = ymin

   

    'further preliminaries

    dur = dly

    If Not wrkshExists("PolygonRotation") Then Worksheets.Add.Name = "PolygonRotation"

    Set w = Worksheets("PolygonRotation")

    Set dr = w.Range("B3:G24")

    w.Activate

   

    On Error Resume Next

    w.ChartObjects.Delete

    Set sh = w.Shapes.AddChart

   

    With dr

        sh.Top = .Top

        sh.Left = .Left

        sh.Height = .Height

        sh.Width = .Width

    End With

  

    'fill the chart

    With sh.Chart

       

        'frame

        Set serC = .SeriesCollection.NewSeries

        serC.Values = col(f, 2)

        serC.XValues = col(f, 1)

        .ChartType = xlXYScatter

        .HasLegend = False

       

        'polygon

        Set serA = .SeriesCollection.NewSeries

        serA.Values = col(p, 2)

        serA.XValues = col(p, 1)

        serA.ChartType = xlXYScatterLinesNoMarkers

 

        'center of rotation

        Set serB = .SeriesCollection.NewSeries

        serB.Values = col(rc, 2)

        serB.XValues = col(rc, 1)

        serB.ChartType = xlXYScatter

       

        'format the axes

        .HasAxis(xlCategory) = True

        .Axes(xlCategory).MinimumScale = xmin

        .Axes(xlCategory).MaximumScale = xmax

        .HasAxis(xlValue) = True

        .Axes(xlValue).MinimumScale = ymin

        .Axes(xlValue).MaximumScale = ymax

        .SetElement (msoElementPrimaryValueGridLinesNone)

       

        'for every angle from 1 to angle degrees: redraw chart

        For i = 1 To angle

            delay dur

            pts = Polygon.polyRot(p, rc, i)

            serA.Values = col(pts, 2)

            serA.XValues = col(pts, 1)

        Next i

 

    End With

 

End Sub

 

 

'gives the euclidian distance between rotation centre cp

'and the most remote point in p

Private Function maxDist(ByRef p() As Double, _

                                     ByRef cp() As Double) As Double

    Dim i As Integer

    Dim d As Double, md As Double

    md = 0

    For i = 1 To UBound(p, 1)

        d = ((cp(1, 1) - p(i, 1)) ^ 2 + (cp(1, 2) - p(i, 2)) ^ 2) ^ 0.5

        If d > md Then md = d

    Next i

    maxDist = md

End Function

 

 

 

'extracts column colNo from matrix m

Public Function col(ByRef m() As Double, ByVal colNo As Integer) As Double()

    Dim i As Integer

    Dim c() As Double

    ReDim c(1 To UBound(m, 1))

    For i = 1 To UBound(m, 1)

        c(i) = m(i, colNo)

    Next i

    col = c

End Function

 

 

'causes a delay

Private Sub delay(ByVal duration As Single)

    Dim t As Single

    t = Timer

    Do While Timer < t + duration

        DoEvents

    Loop

End Sub

 

 

'checks whether worksheet named wName exists

Private Function wrkshExists(ByVal wName As String) As Boolean

    wrkshExists = False

    Dim w As Worksheet

    For Each w In Worksheets

        If w.Name = wName Then

            wrkshExists = True

            Exit For

        End If

    Next w

End Function

 

Animation einer Polygonrotation
Lesen Sie vor der Anwendung die oben stehende Beschreibung
PolygonRotation2.zip
Komprimiertes Archiv im ZIP Format 40.1 KB