Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
Warum muss ich die operationen mit AffineTransform in umgekehrter Reihenfolge ausfuehren..?
AffineTransform ist column-major (d.h. die tx, ty fuer die translation sind im 3ten column anstatt in der dritten row, welches bei row-major der Fall ist).
Dadurch muss man alle Operationen in umgekehrter Reihenfolge ausfuehren man das rausbekommt was man will.
Da verdreht es einem ja das Hirn
Warum hat man sich bei Java's AffineTransform dafuer entschieden das so zu machen? Vielleicht macht es ja andere Sachen wieder leichter die ich jetzt nicht in Betracht ziehe? Oder ist die Performance leicht besser bei column-major?
Also das mit umgekehrter Reihenfolge hat vor allem mathematische Gründe.
Ich kenne das Gebiet nur aus der Computergrafik mit OpenGL etc, vermute aber mal, dass es hier ähnlich laufen wird.
Angenommen du hast 2 Transformationen T_rotation und T_translation und einen Punkt P.
Vermutlich weißt du, dass Transformationen über die Matrixmultiplikation verknüpft werden.
Wendest du jetzt erst die Rotation und dann eine Translation an rechnest du ja:
(T_rotation * T_translation) * P.
Äquivalenter Java Code dazu wäre:
Java:
AffineTransform trans = new AffineTransform();
trans.rotate(Math.PI/2);
trans.translate(1, 1);
Point p = new Point(1, 1);
trans.transform(p, p);
System.out.println(p);
Allerdings ist die Matrixmultiplikation assoziativ. Es gilt also:
(T_rotation * T_translation) * P = T_rotation * (T_translation * P).
Und damit weißt du im Prinzip auch schon, warum du die Transformationen in umgekehrter Reihenfolge anwenden musst.
Weil die Matrixmultiplikation assoziativ ist rechnest du zuerst die Klammer aus, wendest also die Translation auf den Punkt an. Auf den transformierten Punkt wendest du dann die Rotation an.
Bin mit der Java-Klasse nicht vertraut aber ich kann mir auch nicht vorstellen, dass eine row-major Darstellung der Translation daran irgendwas ändern würde, da das mathematisch begründed ist.
Wenn es dir zu kompliziert ist, kannst du die Transformationen auch der Reihe nach anwenden. Es macht keinen Unterschied ob man den obigen Code ausführt oder diesen hier:
Java:
AffineTransform trans2 = new AffineTransform();
trans2.translate(1, 1);
Point p2 = new Point(1, 1);
trans2.transform(p2, p2); //wende zuerst Translation auf Punkt an
trans2.setToIdentity();
trans2.rotate(Math.PI/2);
trans2.transform(p2, p2); //wende danach Rotation auf Punkt an
System.out.println(p2);
Der obere Code rechnet erst:
T_rotation * T_translation und wendet die resultierende Matrix auf den Punkt an.
Der untere Code wendet erst die Translation auf den Punkt an. Und danach wird die Rotation auf den resultierenden Punkt angewendet. Das Ergebnis ist in beiden Fällen das gleiche.
AffineTransform ist so definiert, dass gilt: P' = T1 * T2 * ... * P, wobei P ein Spaltenvektor ist.
Man hätte es natürlich auch genausogut andersherum definieren können. Also P' = P * T1 * T2 * ... mit P als Zeilenvektor und T1 - Tn in row-major bzw. P' = ... * T2 * T1 * P mit P als Spaltenvektor und T1 - Tn in column-major, was aufs gleiche rauskäme.
Die Philosophie dahinter ist, dass Koordinatensysteme transformiert werden und nachfolgende Transformationen relativ zum aktuellen transformierten Koordinatensystem sein sollen. Definierst du Transformationen dagegen so rum, wie du es bevorzugst, also row-major, dann sind alle Transformationen immer relativ zum absoluten Koordinatensystem. Oder einfacher ausgedrückt, in einem Fall sind Transformationen relativ und im anderen absolut. Wobei eine Sequenz von relativen Transformationen natürlich mathematisch equivalent ist zur umgekehrten Sequenz von absoluten Transformationen. Das ganze relativ zu definieren ist gerade bei Swing sehr nützlich, da man dann in der paint Methode nicht wissen muss, ob das GUI Element z.B. gedreht oder sonstwie transformiert wurde.
AffineTransform trans2 = new AffineTransform();
trans2.translate(1, 1);
Point p2 = new Point(1, 1);
trans2.transform(p2, p2); //wende zuerst Translation auf Punkt an
trans2.setToIdentity();
trans2.rotate(Math.PI/2);
trans2.transform(p2, p2); //wende danach Rotation auf Punkt an
System.out.println(p2);
Wenn AffineTransform row-major waere koenntest du trans2.setToIdentity(); weglassen. Du wuerdest die Transformationen multiplizieren (und zwar in der Reihenfolge wie man sie anwenden will) und erst zum Schluss auf den Punkt anwenden.
AffineTransform ist so definiert, dass gilt: P' = T1 * T2 * ... * P, wobei P ein Spaltenvektor ist.
Man hätte es natürlich auch genausogut andersherum definieren können. Also P' = P * T1 * T2 * ... mit P als Zeilenvektor und T1 - Tn in row-major bzw. P' = ... * T2 * T1 * P mit P als Spaltenvektor und T1 - Tn in column-major, was aufs gleiche rauskäme.
Die Philosophie dahinter ist, dass Koordinatensysteme transformiert werden und nachfolgende Transformationen relativ zum aktuellen transformierten Koordinatensystem sein sollen. Definierst du Transformationen dagegen so rum, wie du es bevorzugst, also row-major, dann sind alle Transformationen immer relativ zum absoluten Koordinatensystem. Oder einfacher ausgedrückt, in einem Fall sind Transformationen relativ und im anderen absolut. Wobei eine Sequenz von relativen Transformationen natürlich mathematisch equivalent ist zur umgekehrten Sequenz von absoluten Transformationen. Das ganze relativ zu definieren ist gerade bei Swing sehr nützlich, da man dann in der paint Methode nicht wissen muss, ob das GUI Element z.B. gedreht oder sonstwie transformiert wurde.