如何在Python中编写螺旋函数?

4
我正在尝试编写一个Python函数,它接受两个参数(x,y),并以螺旋方向返回角度。假设螺旋中心位于位置(x0,y0)。然后给定(0,0),它返回45度。对于其他一些点,如从y轴顶部开始的第二个交点(0,90),角度约为170度。对于不接触红线的任何点,它都应该返回您期望的方向角度。螺旋只是一个通用的东西,用来显示角度方向。是否有人知道如何编写这样的函数?谢谢。

2
我认为,你最好在数学上询问三角函数部分,然后在这里澄清实现细节。 - bereal
这是阿基米德螺线。在你的情况下,“x”和“y”是什么?笛卡尔坐标吗? - Tengis
如果您提供的点不在螺旋线上,答案会是什么呢?由于浮点精度问题,几乎不可能精确地指定螺旋线上的一个点(除非在轴上),所以即使您试图保持在螺旋线上,在实践中几乎肯定会遇到这个问题。 - Blckknght
它不必完全在螺旋线上。正如我在问题中所述“对于任何未接触红线的点,它应返回您期望方向的角度。螺旋只是一般性的东西,用于显示角度的方向。”,角度应该近似于您期望的方向。例如,顶部y轴交点为175度,下面的交点为170度,那么我猜在两者之间的点的角度将是172.5度。 - omega
答案支持我的信念,认为这是一个数学问题而不是编程问题,因此我投票关闭该问题,理由是不相关。 - High Performance Mark
5个回答

2
这是一个阿基米德螺旋线。正如该页面在极坐标中所说,该曲线的公式为 r = aθ,通常标量 a 为 1,即 r = θ。极坐标转换为直角坐标的公式为:
x = r cos θ, y = r sin θ

因此
x = θ cos θ, y = θ sin θ

将θ从0变化到6π会得到你所拥有的曲线。当你改变参数θ并获得x和y值时,所得到的结果是相对于原点(0, 0)的。对于你的情况,你需要进行平移,即通过x和y偏移量将点移动到相应的位置。如果你想要一个更大的相同曲线版本,你需要进行缩放(在平移之前),即将x和y值都乘以一个常数因子。

θ 是极坐标系中的一个值;x 和 y 是笛卡尔坐标系中的值。θ 是由点到原点形成的线与X轴之间的角度。你需要将笛卡尔坐标系转换为极坐标系。请参阅此处链接:http://en.wikipedia.org/wiki/Polar_coordinate_system#Converting_between_polar_and_Cartesian_coordinates。 - legends2k
如果我执行atan2(y,x),那只会给我从(0,0)到(x,y)的角度...我不是在寻找那个。 - omega
我在上方添加了一张新图片。 (x,y) 映射到图像上的某个箭头。返回值应该是箭头的方向。 - omega
你的意思是需要在(x,y)处的切线吗? - legends2k
我通过将 atan2 得到的角度加上 pi/2 来得到它。 - omega
显示剩余3条评论

1

(我认为螺旋图像比有用更加令人困惑...)

对于一个点 (x, y),您希望返回角度 theta(以度为单位),其中 (1,0) 位于 0 度,(0, 1) 位于 90 度。

因此,我们想要找到 theta。使用三角学,我们知道 x 是邻边,y 是对边,且 tan(theta) == y/x

这略微受到 tan() 每 180 度重复的影响 - tan(y/x) == tan(-y/-x)。幸运的是,Python 有一个内置函数 atan2 来补偿这一点。它以弧度返回 theta,然后我们将其转换为度数,如下所示:

from math import atan2, degrees

x, y = (2, 2)
theta = degrees(atan2(y, x))   # => theta == 45.0

然而,atan2返回的值在-2*pi < theta <= 2*pi (-179.9... to +180 degrees)之间;你希望它以(0.. 359.9...)度为单位:

theta = (degrees(atan2(y, x)) + 360.0) % 360.0

这给了我从(0,0)到(x,y)的角度,这不是我要找的。 - omega
我在上方添加了一张新图片。 (x,y) 映射到图像上的某个箭头。返回值应该是箭头的方向。 - omega

1
你需要一个矢量场,其中一个轨迹是阿基米德螺旋线。
如果使用以下螺旋线公式:
x = θ cos θ, y = θ sin θ
从legends2k的答案中计算其切线,则得到
vx = cos θ - θ sin θ, vy = sin θ + θ cos θ
通过最直接的方式消除x和y中的θ,得到一般向量场
vx = x/r-y, vy = y/r + x,其中r=sqrt(x^2+y^2)。
然后可以通过atan2(vy,vx)获得向量场的角度。

0

如果有人能够验证这个,我会很感激。

这个解决方案允许你拥有一个半径,它的增长速度与角度不同。

半径:

r(t) = r_scalar * t
d(r(t))/dt = r_scaler

度数:

a(t) = a0 + a_scalar * t
d(a(t))/dt = a_scaler

位置:

x(t),y(t) = x0 + r(t)*cos(a(t)), y0 + r(t)*sin(a(t))

现在我们可以计算任何t的方向:

d(x(t))/dt = r(t)*(-sin(a(t))*d(a(t))/dt) + d(r(t))/dt*cos(a(t))
d(y(t))/dt = r(t)*(cos(a(t))*d(a(t))/dt) + d(r(t))/dt*cos(a(t))

这简化了:

d(x(t))/dt = r(t)*(-sin(a(t))*a_scaler) + r_scaler*cos(a(t))
d(y(t))/dt = r(t)*(cos(a(t))*a_scaler) + r_scaler*sin(a(t))

为了得到a、b的值,使得它们最接近x(t)和y(t),你可以先近似地认为距离x0,y0到x1,y1会满足r(t)。

t0 = sqrt((x0 - x2)^2 + (y0 - y1)^2)/r_scalar

由于螺旋线上最接近的点将在相同的角度处,因此微调t以满足角度要求。

t1 = t0-t2 where atan((y0 - y1)/(x0 - x2)) = (a0 + a_scalar * t0) % 2*pi - (a0 + a_scalar * t2)

因此

t2 = (((a0 + a_scalar * t0) % 2*pi) - atan((y0 - y1)/(x0 - x2)) + a0)/a_scalar

然后,最接近的方向角是atan((d(x(t0-t2))/dt / d(y(t0-t2))/dt))


-1
import numpy as np
import matplotlib.pyplot as plt
import math

def fermat_spiral(dot):
    data=[]    
    d=dot*0.1
    for i in range(dot): 
        t = i / d * np.pi
        x = (1 +  t) * math.cos(t)
        y = (1 +  t) * math.sin(t)
        data.append([x,y])
    narr = np.array(data)
    f_s = np.concatenate((narr,-narr))
    return f_s

f_spiral = fermat_spiral(20000)
plt.scatter(f_spiral[len(f_spiral)//2:,0],f_spiral[len(f_spiral)//2:,1])
plt.scatter(f_spiral[:len(f_spiral)//2,0],f_spiral[:len(f_spiral)//2,1])
plt.show()

请不要只粘贴代码,而是给出一些上下文。请参见此处以了解如何编写一个好的答案。 - gehbiszumeis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接