问题描述
当我的程序迭代坐标旋转算法时,我的坐标变得无限小,我遇到了一个问题。我在下面放了一个 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,y)
x = x * cos(theta) + y * sin(theta)
y = -x * sin(theta) + y * cos(theta)
x,y = cart_to_tk(width/2,y)
canvas.delete(line)
line = canvas.create_line(width/2,y)
root.after(20,lambda: run(root,height))
tk_to_cart 和 cart_to_tk 只是画布上的简单翻译,因为 tkinter 的坐标系在左上角有 0,0。
解决方法
可能会损失沿线某处的精度,特别是如果 tk_to_cart
和/或 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,x_tk,y_tk)
root.after(20,lambda: run(root,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,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,x_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,rotation='ccw')
使用这种基本的三角方法可以更容易地控制运动。
您可以更改旋转方向 cw
或 ccw
。您可以使用非线性函数计算 theta 以随时间改变角速度。您可以添加速度参数作为参数。您可以随着时间的推移更改 x_o,y_o
以使线条移动。等