Two further nights I have spent the re-engineering of the coordinate transformation of rotated graphical objects. Oracle Forms allows it to rotate rectangle or lines with a defined angle. The angle can be integers between 0 and 359 degree.
Actually I haven’t any interest in graphical transformations, simple moves of objects can program everybody, but to calculate new coordinates for a rotated object is more complex.
The good news are, the FMX file contains the angle value. So I can calculate the new coordinates with a little bit mathematics.
I have started with rectangles, four corners, should be possible. As I entered a rotation angle, there were a lot of changes within the FMX file. I found the rotation angle, but why there are other changes? Some of the changes I had already located as x, y, width and height of the rectangle, but the meaning has been changed with a rotating angle != 0. To find out, what is going on in the file, I have to go deeper into mathematics.
Oracle Forms rotates the rectangle around its center. Every corner will get new coordinates. So the first thing I need is the center of the rectangle. It is simple to calculate for a non-rotated rectangle:
cx = x + (width/2)
cy = y + (height/2)
Next, I calculate every corner coordinate:
x0 = x, y0 = y
x1 = x + width, y1 = y
x2 = x + width, y + height
x3 = x, y + height
These four coordinates I have to transform by the following formula, where (x,y) is the corner coordinate, (cx,cy) is the center coordinate, q is the rotation angle and (x’,y’) is the new coordinate:
x' = x1 + cos(q) * (x - cx) - sin(q) * (y - cy)
y' = y1 + sin(q) * (x - cx) + cos(q) * (y - cy)
With a little bit Java, I can describe it as:
package info.phosco.forms.rotate; public class OraclePoint { private final double x; private final double y; public OraclePoint(double x, double y) { this.x = Math.round(x * 1000.0) / 1000.0; this.y = Math.round(y * 1000.0) / 1000.0; } public OraclePoint(int x, int y) { this((double) x, (double) y); } public double getX() { return this.x; } public double getY() { return this.y; } @Override public String toString() { return getClass().getName() + "[" + this.x + ", " + this.y + "]"; } }
package info.phosco.forms.rotate; public class RotateRect { // Rotation point: x1,y1 // Original x,y // Rotated x',y' // Rotation angle q private static OraclePoint rotate(OraclePoint p, OraclePoint c, int angle) { // x' = x1 + cos(q) * (x - x1) - sin(q) * (y - y1) double x = c.getX() + Math.cos(Math.toRadians(angle)) * (p.getX() - c.getX()) - Math.sin(Math.toRadians(angle)) * (p.getY() - c.getY()); // y' = y1 + sin(q) * (x - x1) + cos(q) * (y - y1) double y = c.getY() + Math.sin(Math.toRadians(angle)) * (p.getX() - c.getX()) - Math.cos(Math.toRadians(angle)) * (p.getY() - c.getY()); return new OraclePoint(x, y); } public static void main(String args[]) { int x = 37; int y = 47; int w = 120; int h = 73; int angle = 45; // calc every corner OraclePoint p0 = new OraclePoint(x, y); OraclePoint p1 = new OraclePoint(x + w, y); OraclePoint p2 = new OraclePoint(x + w, y + h); OraclePoint p3 = new OraclePoint(x, y + h); System.out.println("rotate angle: 0°"); System.out.println(p0); System.out.println(p1); System.out.println(p2); System.out.println(p3); // calc center point OraclePoint center = new OraclePoint((double) x + ((double) w / 2.0), (double) y + ((double) h / 2.0)); // Rotate rectangle OraclePoint r0 = rotate(p0, center, angle); OraclePoint r1 = rotate(p1, center, angle); OraclePoint r2 = rotate(p2, center, angle); OraclePoint r3 = rotate(p3, center, angle); System.out.println("----------"); System.out.println("rotate angle: " + angle + "°"); System.out.println(r0); System.out.println(r1); System.out.println(r2); System.out.println(r3); } }
I can round the new coordinates a bit and it was possible to find the values (after a further transformation with the Oracle Form coordinate system factor) within the changes of the FMX file. Some changes I cannot explain, some cordinates seems to be double stored into the file.