轴的旋转
Rotation of Axis
我正在使用 rotation of axis formula 如下:
假设 θ 是 30 度,所以为了回到未旋转状态,我简单地使用 (360-30) 作为 α(全部以弧度表示)。但是,我不会回到 0 度。我已经在 python 中添加了代码进行验证,所以一切看起来都很好(为 α 添加 90 度或 π/2)给出但是我纠正了偏移量,添加偏移量以返回“未旋转”状态是一个问题。
可能是什么问题?
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
所以问题是您已经知道椭圆 (x0,y0,rx,ry
) 旋转 a0
并且想要获得椭圆上的点,该点将在椭圆中心和轴之间创建精确的角度 ma
穿过它并与 x 平行。
- 你把减号弄错了,它应该在
sin(a)
... 之一之前
- 如果需要,这是 XY 平面或围绕
z
轴的旋转!!!
- 参数角度
b
是非线性的,所以简单的减法不起作用
旋转公式本身是:
x' = x*cos(a) - y*sin(a)
y' = x*sin(a) + y*cos(a)
或:
x' = x*cos(a) + y*sin(a)
y' = -x*sin(a) + y*cos(a)
对于反向...现在参数化椭圆是:
x = rx*cos(b)
y = ry*sin(b)
所以放在一起时(二次旋转公式):
x' = rx*cos(b)*cos(a) + ry*sin(b)*sin(a)
y' = -rx*cos(b)*sin(a) + ry*sin(b)*cos(a)
现在我们有了可以从参数角度得到点的公式,我们想从实际角度计算点。这可以通过更多方式来完成。我选择了:
- 用实际角度计算方向向量
ma
- 取消旋转
a0
- 重新缩放,使椭圆变成圆形
只需将
x
或 y
(仅一个)乘以比率 ry/rx
或 rx/ry
- 旋转
a0
- 使用atan2获取参数角度
此处为 C++/VCL 示例:
//---------------------------------------------------------------------------
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;
}
//---------------------------------------------------------------------------
第一个函数只是按线渲染椭圆(这样您就可以匹配您的渲染和坐标系)。第二个是你需要的计算。它计算 mx,my
对应于真实角度 ma
.
的点坐标
此处预览:
蓝色是椭圆,灰色是实际角度的方向(我使用鼠标位置),Aqua 是椭圆中心和实际角度计算点之间的线。如您所见,它完全重叠,因此可以正常工作。它沿着整个圆周工作。
为简单起见,我使用了 atan2
函数,该函数很常见,但至少在我的环境中我必须处理一些边缘情况...
我正在使用 rotation of axis formula 如下:
假设 θ 是 30 度,所以为了回到未旋转状态,我简单地使用 (360-30) 作为 α(全部以弧度表示)。但是,我不会回到 0 度。我已经在 python 中添加了代码进行验证,所以一切看起来都很好(为 α 添加 90 度或 π/2)给出但是我纠正了偏移量,添加偏移量以返回“未旋转”状态是一个问题。
可能是什么问题?
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
所以问题是您已经知道椭圆 (x0,y0,rx,ry
) 旋转 a0
并且想要获得椭圆上的点,该点将在椭圆中心和轴之间创建精确的角度 ma
穿过它并与 x 平行。
- 你把减号弄错了,它应该在
sin(a)
... 之一之前
- 如果需要,这是 XY 平面或围绕
z
轴的旋转!!! - 参数角度
b
是非线性的,所以简单的减法不起作用
旋转公式本身是:
x' = x*cos(a) - y*sin(a)
y' = x*sin(a) + y*cos(a)
或:
x' = x*cos(a) + y*sin(a)
y' = -x*sin(a) + y*cos(a)
对于反向...现在参数化椭圆是:
x = rx*cos(b)
y = ry*sin(b)
所以放在一起时(二次旋转公式):
x' = rx*cos(b)*cos(a) + ry*sin(b)*sin(a)
y' = -rx*cos(b)*sin(a) + ry*sin(b)*cos(a)
现在我们有了可以从参数角度得到点的公式,我们想从实际角度计算点。这可以通过更多方式来完成。我选择了:
- 用实际角度计算方向向量
ma
- 取消旋转
a0
- 重新缩放,使椭圆变成圆形
只需将
x
或y
(仅一个)乘以比率ry/rx
或rx/ry
- 旋转
a0
- 使用atan2获取参数角度
此处为 C++/VCL 示例:
//---------------------------------------------------------------------------
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;
}
//---------------------------------------------------------------------------
第一个函数只是按线渲染椭圆(这样您就可以匹配您的渲染和坐标系)。第二个是你需要的计算。它计算 mx,my
对应于真实角度 ma
.
此处预览:
蓝色是椭圆,灰色是实际角度的方向(我使用鼠标位置),Aqua 是椭圆中心和实际角度计算点之间的线。如您所见,它完全重叠,因此可以正常工作。它沿着整个圆周工作。
为简单起见,我使用了 atan2
函数,该函数很常见,但至少在我的环境中我必须处理一些边缘情况...