metron
metron

Reputation: 201

Rotation of Axis

I am using the rotation of axis formula as follows:

enter image description here

Assuming θ is 30 degrees so in order to go back to un-rotated state I am simply using (360-30) for α (all in radians). However, I am not getting back to 0 degrees. I have added the code in python for verification, so everything seems fine (adding 90 degrees or π/2 for α) gives me correct offset however, adding offset to go back to "un-rotated" state is an issue.

What could be the problem?

import numpy as np
from matplotlib import pyplot as plt
from matplotlib.patches import Ellipse
import numpy.random as rnd


angle=2 * np.pi * rnd.random(size=1)
print("angle",np.degrees(angle))
x_circle90 = np.cos(np.pi / 2)
y_circle90 = np.sin(np.pi / 2)
x_90 = (x_circle90 * (40 * 0.5))
y_90 = (y_circle90 * (50 * 0.5))
xx90 = x_90 * np.cos(angle) - y_90 * np.sin(angle) + 108
yy90 = x_90 * np.sin(angle) + y_90 * np.cos(angle) + 60


x_circle0 = np.cos(0)
y_circle0 = np.sin(0)
x_0 = (x_circle0 * (40 * 0.5))
y_0 = (y_circle0 * (50 * 0.5))
xx0 = x_0 * np.cos(angle) - y_0 * np.sin(angle) + 108
yy0 = x_0 * np.sin(angle) + y_0 * np.cos(angle) + 60

negation=(2*np.pi-angle)
x_circlen = np.cos(negation)
y_circlen = np.sin(negation)
x_n = (x_circlen * (40 * 0.5))
y_n = (y_circlen * (50 * 0.5))
xxn = x_n * np.cos(angle) - y_n * np.sin(angle) + 108
yyn = x_n * np.sin(angle) + y_n * np.cos(angle) + 60


fig = plt.figure()
ax = fig.add_subplot(111)
ax.add_patch(
    Ellipse(xy=[108,60],
            width=40,
            height=50,
            angle=np.degrees(angle),
            fill=False, color='k', linewidth=1))

ax.plot(xx0, yy0, 'rp', color='blue', markersize=2)
ax.plot(xx90, yy90, 'rp', color='green', markersize=2)
ax.plot(xxn, yyn, 'rp', color='black', markersize=2)
ax.plot(108,60,'rp', color='red', markersize=2)
ax.set_xlim(xmin=0, xmax=200)
ax.set_ylim(0, 200)
ax.set_aspect('equal', adjustable='box')
plt.show()
plt.pause()
plt.close

enter image description here

Upvotes: 2

Views: 572

Answers (1)

Spektre
Spektre

Reputation: 51903

So the problem is you have known ellipse (x0,y0,rx,ry) rotated by a0 and want to obtain the point on ellipse that will create exact angle ma between ellipse center and axis going through it and parallel to x.

  1. you got the minus sign wrong it should be before one of the sin(a) ...
  2. this is rotation of XY plane or around z axis if you want!!!
  3. parametric angle b is nonlinear so simple substraction will not work

The rotation formula itself is:

x' =  x*cos(a) - y*sin(a)
y' =  x*sin(a) + y*cos(a)

or:

x' =  x*cos(a) + y*sin(a)
y' = -x*sin(a) + y*cos(a)

for the reverse direction ... Now parametric ellipse is:

x = rx*cos(b)
y = ry*sin(b)

So when put together (second rotation formula):

x' =  rx*cos(b)*cos(a) + ry*sin(b)*sin(a)
y' = -rx*cos(b)*sin(a) + ry*sin(b)*cos(a)

Now that we have formula that can get us point from parametric angle we want to compute point from real angle. That can be done in more ways. I choosed to:

  1. compute direction vector with real angle ma
  2. un-rotate it by a0
  3. rescale it so ellipse would become circle simply by multiplying x or y (just one) by ratio ry/rx or rx/ry
  4. rotate by a0
  5. using atan2 to obtain parametric angle

Here C++/VCL example:

//---------------------------------------------------------------------------
const double pi2=6.283185307179586476925286766559;
const double deg=6.283185307179586476925286766559/360.0;
double x0=125,y0=75,rx=100,ry=50.0,a0=15.0*deg; // ellipse center,radiuses,rotation
//---------------------------------------------------------------------------
void ellipse_draw(TCanvas *can) // render ellipse
    {
    int e;
    double b=0.0,db=0.01*pi2,x,y;
    x =  rx*cos(b)*cos(a0) + ry*sin(b)*sin(a0) +x0;
    y = -rx*cos(b)*sin(a0) + ry*sin(b)*cos(a0) +y0;
    can->MoveTo(x,y);
    for (e=1;e;b+=db)
        {
        if (b>=pi2) { b=pi2; e=0; }
        x =  rx*cos(b)*cos(a0) + ry*sin(b)*sin(a0) +x0;
        y = -rx*cos(b)*sin(a0) + ry*sin(b)*cos(a0) +y0;
        can->LineTo(x,y);
        }
    }
//---------------------------------------------------------------------------
void ellipse_getpnt(double &mx,double &my,double ma) // (mx,my) = point at global angle ma from (x0,y0)
    {
    int e;
    double b,x,y;
    // direction vector with global angle ma in ellipse local coordinates
    b=ma+a0;
    x =cos(b);
    y =sin(b);
    // rescale to circle
    y*=rx/ry;
    // convert (x,y) to unrotated coordinates
    mx =  x*cos(a0) + y*sin(a0);
    my = -x*sin(a0) + y*cos(a0);

    // b = atan2(my,mx)
    e=1;
    if (fabs(mx)<1e-3){ e=0; b=+90.0*deg; if (my<0) b= -90.0*deg; }
    if (fabs(my)<1e-3){ e=0; b=+ 0.0*deg; if (mx<0) b=+180.0*deg; }
    if (fabs(mx)+fabs(my)<1e-3){ e=0;  ma=0.0*deg; }
    if (e) b=atan2(my,mx);

    b+=a0;

    // point on ellipse with new angle
    mx =  rx*cos(b)*cos(a0) + ry*sin(b)*sin(a0) +x0;
    my = -rx*cos(b)*sin(a0) + ry*sin(b)*cos(a0) +y0;
    }
//---------------------------------------------------------------------------

The first function just renders the ellipse by lines (just so you can match your rendering and coordinate system). The second one is the computation you need. It computes mx,my point coordinates corresponding to real angle ma.

Here preview:

preview

Blue is ellipse, gray is direction to real angle (I used mouse position) and Aqua is line between ellipse center and computed point for real angle. As you can see it overlaps exactly so it works. It works along the whole circumference.

For simplicity I used the atan2 function which is common but at least on mine environment I had to handle some edge cases ...

Upvotes: 1

Related Questions