Python 坐标旋转随着迭代变小
Python coordinate rotations becoming smaller with iterations
当我的程序通过坐标旋转算法迭代时,我的坐标变得无限小,我遇到了一个问题。我在下面放了一张 gif,以较慢的帧速率展示了这一点;随着它的继续,这条线最终消失了。
https://i.gyazo.com/368fbc65dbc5d3deaa282a4b72ec5d22.mp4
我认为问题在于 sin 和 cos 可能会截断数字,但我不确定
def run(root, canvas, line, x, y, width, height):
# counter clockwise rotation of cartesian coordinates
# X = xcosθ + ysinθ
# Y = -xsinθ + ycosθ
theta = 1/8
x, y = tk_to_cart(width/2, height/2, x, y)
x = x * cos(theta) + y * sin(theta)
y = -x * sin(theta) + y * cos(theta)
x, y = cart_to_tk(width/2, height/2, x, y)
canvas.delete(line)
line = canvas.create_line(width/2, height/2, x, y)
root.after(20, lambda: run(root, canvas, line, x, y, width, height))
tk_to_cart 和 cart_to_tk 只是 canvas 的简单翻译,因为 tkinter 的坐标系在左上角有 0,0。
可能沿线某处精度下降,特别是如果 tk_to_cart
and/or cart_to_tk
舍入或截断为整数。
几个想法:
- 让数据仅在一个方向上流动(从模型到屏幕),而不是来回流动;而且,如果这没有帮助,
- 避免重复旋转,而是增加角度并始终从原来的
x
和y
旋转。
类似于:
def run(root, canvas, line, x_cart, y_cart, width, height, theta=0):
# counter clockwise rotation of cartesian coordinates
# X = xcosθ + ysinθ
# Y = -xsinθ + ycosθ
theta += 1/8
x_rot = x_cart * cos(theta) + y_cart * sin(theta)
y_rot = -x_cart * sin(theta) + y_cart * cos(theta)
x_tk, y_tl = cart_to_tk(width/2, height/2, x_rot, y_rot)
canvas.delete(line)
line = canvas.create_line(width/2, height/2, x_tk, y_tk)
root.after(20, lambda: run(root, canvas, line, x_cart, y_cart, width, height, theta))
我想提出这个解决方案,使直线绕一个点。
基本三角函数
参考下图
您有某个 length
的片段,从点 x_o, y_o
到点 x_p, y_p
。其中 x_o, y_o
是线段的旋转中心和翻译参考系的原点 x', y'
相对于 canva
参考系 x, y
.
现在,x_o, y_o
和 length
是输入数据,不会随时间改变(除非您决定改变)。
鉴于 x', y'
中的一些三角函数:
x_p = length * cos(theta)
y_p = length * sin(theta)
由于您的参照系已翻译,上述公式变为:
x_p = length * cos(theta) + x_o
y_p = length * sin(theta) + y_o
如您所见,您正在计算 x_p, y_p
,因此此处的长度不会改变。
翻译成代码
让我们将 theta
表示为循环中更新的 math.pi
的倍数。
方法变为:
def draw_line(root, canvas, length, x_o=0, y_o=0, theta=0.1, rotation='cw', line=None):
if line is not None:
canvas.delete(line)
x_p = length * math.cos(math.pi * theta) + x_o
y_p = length * math.sin(math.pi * theta) + y_o
k = {'ccw': -1, 'cw': 1}[rotation]
theta = theta + k * 0.05 # or any non linear function to change the motion
line = canvas.create_line(x_o, y_o, x_p, y_p)
root.after(100, lambda: draw_line(root, canvas, length, x_o, y_o, theta, rotation, line))
因此,运行 进行测试:
import tkinter
import math
root = tkinter.Tk()
canvas = tkinter.Canvas(root)
canvas.pack()
length = 100
x_o = 100
y_o = 100
draw_line(root, canvas, x_o, y_o, length, rotation='ccw')
使用这种基本的三角方法可以更容易地控制运动。
您可以更改旋转方向 cw
或 ccw
。您可以使用非线性函数计算 theta,以随时间改变 angular 速度。您可以添加一个速度参数作为参数。您可以随着时间的推移更改 x_o, y_o
以使该行四处移动。等等
当我的程序通过坐标旋转算法迭代时,我的坐标变得无限小,我遇到了一个问题。我在下面放了一张 gif,以较慢的帧速率展示了这一点;随着它的继续,这条线最终消失了。
https://i.gyazo.com/368fbc65dbc5d3deaa282a4b72ec5d22.mp4
我认为问题在于 sin 和 cos 可能会截断数字,但我不确定
def run(root, canvas, line, x, y, width, height):
# counter clockwise rotation of cartesian coordinates
# X = xcosθ + ysinθ
# Y = -xsinθ + ycosθ
theta = 1/8
x, y = tk_to_cart(width/2, height/2, x, y)
x = x * cos(theta) + y * sin(theta)
y = -x * sin(theta) + y * cos(theta)
x, y = cart_to_tk(width/2, height/2, x, y)
canvas.delete(line)
line = canvas.create_line(width/2, height/2, x, y)
root.after(20, lambda: run(root, canvas, line, x, y, width, height))
tk_to_cart 和 cart_to_tk 只是 canvas 的简单翻译,因为 tkinter 的坐标系在左上角有 0,0。
可能沿线某处精度下降,特别是如果 tk_to_cart
and/or cart_to_tk
舍入或截断为整数。
几个想法:
- 让数据仅在一个方向上流动(从模型到屏幕),而不是来回流动;而且,如果这没有帮助,
- 避免重复旋转,而是增加角度并始终从原来的
x
和y
旋转。
类似于:
def run(root, canvas, line, x_cart, y_cart, width, height, theta=0):
# counter clockwise rotation of cartesian coordinates
# X = xcosθ + ysinθ
# Y = -xsinθ + ycosθ
theta += 1/8
x_rot = x_cart * cos(theta) + y_cart * sin(theta)
y_rot = -x_cart * sin(theta) + y_cart * cos(theta)
x_tk, y_tl = cart_to_tk(width/2, height/2, x_rot, y_rot)
canvas.delete(line)
line = canvas.create_line(width/2, height/2, x_tk, y_tk)
root.after(20, lambda: run(root, canvas, line, x_cart, y_cart, width, height, theta))
我想提出这个解决方案,使直线绕一个点。
基本三角函数
参考下图
您有某个 length
的片段,从点 x_o, y_o
到点 x_p, y_p
。其中 x_o, y_o
是线段的旋转中心和翻译参考系的原点 x', y'
相对于 canva
参考系 x, y
.
现在,x_o, y_o
和 length
是输入数据,不会随时间改变(除非您决定改变)。
鉴于 x', y'
中的一些三角函数:
x_p = length * cos(theta)
y_p = length * sin(theta)
由于您的参照系已翻译,上述公式变为:
x_p = length * cos(theta) + x_o
y_p = length * sin(theta) + y_o
如您所见,您正在计算 x_p, y_p
,因此此处的长度不会改变。
翻译成代码
让我们将 theta
表示为循环中更新的 math.pi
的倍数。
方法变为:
def draw_line(root, canvas, length, x_o=0, y_o=0, theta=0.1, rotation='cw', line=None):
if line is not None:
canvas.delete(line)
x_p = length * math.cos(math.pi * theta) + x_o
y_p = length * math.sin(math.pi * theta) + y_o
k = {'ccw': -1, 'cw': 1}[rotation]
theta = theta + k * 0.05 # or any non linear function to change the motion
line = canvas.create_line(x_o, y_o, x_p, y_p)
root.after(100, lambda: draw_line(root, canvas, length, x_o, y_o, theta, rotation, line))
因此,运行 进行测试:
import tkinter
import math
root = tkinter.Tk()
canvas = tkinter.Canvas(root)
canvas.pack()
length = 100
x_o = 100
y_o = 100
draw_line(root, canvas, x_o, y_o, length, rotation='ccw')
使用这种基本的三角方法可以更容易地控制运动。
您可以更改旋转方向 cw
或 ccw
。您可以使用非线性函数计算 theta,以随时间改变 angular 速度。您可以添加一个速度参数作为参数。您可以随着时间的推移更改 x_o, y_o
以使该行四处移动。等等