使用Python绘制内接圆轨迹
我一直在尝试用电脑绘制一些内摆线,但遇到了一些问题。对于不太了解的人来说,内摆线的参数方程是:
x(theta) = (R - r)cos(theta) + d*cos((R-r)/r*theta)
还有
y(theta) = (R - r)sin(theta) - d*sin((R-r)/r*theta)
维基百科对内摆线的定义可以进一步解释:
内摆线是一个由一个点在半径为 r 的圆圈上滚动,围绕一个固定的半径为 R 的圆圈内部所描绘的轨迹,这个点距离内部圆心的距离为 d。
所以,当 r = d = 1
和 R = 3
时,内摆线应该看起来像这样:
但我用我的计算方法得到的结果显然不是这样。我的内摆线(用相同的值)看起来像这样:
因为 x 和 y 的值是通过一个关于角度 theta
的函数来确定的,我以为可以简单地从 0 到 2pi 循环遍历 theta
的值,在某些间隔计算 x 和 y 的值,然后以极坐标的形式绘制坐标(其中 r**2 = x**2 + y**2
),但我想我想错了。也许我的公式有问题,但我刚刚和一些人在数学交流网站上讨论过,我们也没能找出问题所在。如果我的方法错了,应该用什么方法来计算内摆线呢?
这是我的代码:
class _BaseCurve(event.EventAware):
# This is a basic curve class from which all other curves inherit from (as
# you will see below with the Hypotrochoid class). Basically what happens is
# each new curve class must implement a function (relation) to calculate the
# radius of the equation at each angle interval, then plots the equation in
# other code elsewhere.
def __init__(self, radius, init_angle, end_angle, speed, acceleration, *args, **kwargs):
# Initialize geometric data...
self.radius = radius
# Initialize curve start and end angles...
self.init_angle = init_angle
self.end_angle = end_angle
# Initialize time-based curve attributes...
self.speed = speed
self.accel = acceleration
self.current_pos = 0
# Initialize defaults...
self.max_speed = inf
self.min_speed = neginf
self.args = args
self.kwargs = kwargs
def set_max_speed(self, speed):
"""Set the maximum speed the path can achieve."""
if speed < self.min_speed:
errmsg = "Max speed cannot be less than min speed."
raise ValueError(errmsg)
self.max_speed = speed
def set_min_speed(self, speed):
"""Set the minimum speed the path can achieve."""
if speed > self.max_speed:
errmsg = "Min speed cannot be greater than max speed."
raise ValueError(errmsg)
self.max_speed = speed
def set_acceleration(self, acceleration):
"""Set a new acceleration for the path."""
self.accel = acceleration
def move(self):
"""Progress the path forward one step.
The amount progressed each time (curve).move is called
depends on the path's speed parameter and the distance
(i.e. angle_difference) it has to travel. The calculation
is as follows:
angle = angle_difference * current_position + init_angle
Where current_position is the position incremented by the
set speed in (curve).move().
"""
self.current_pos += self.speed
if self.accel != 1:
new_speed = self.speed * self.accel
self.speed = max(min(new_speed, self.max_speed), self.min_speed)
def angle(self):
"""Return the angle of the curve at the current position."""
return self.angle_difference * self.current_pos + self.init_angle
def relation(self):
"""Return the relationship of the current angle to the radius.
This is a blank function left to be filled in by subclasses of
_BasicCurve. The return value for this function must be a function
(or lambda expression), of which that function's return value should
be the radius of the curve at the current position. The parameters of
the return equation should be as follows:
(Assuming `r` is the function representing the relation):
radius = r(current_angle, *args, **kwargs)
Where *args and **kwargs are the additional *args and **kwargs specified
upon initializing the curve.
"""
return NotImplemented
def position(self):
"""Calculate the position on the curve at the current angle.
The return value of this function is the coordinate in polar
form. To view the coordinate in cartesian form, use the
`to_cartesian` function. # Ignore the `to_cartesian` function in this code snippet, it simply converts polar to cartesian coordinates.
NOTE: This function requires self.relation to be implemented.
"""
r = self.relation()
theta = self.current_angle
args = self.args
kwargs = self.kwargs
radius = self.radius*r(theta, *args, **kwargs)
return radius, theta
@property
def angle_difference(self):
"""The difference between the start and end angles specified."""
return (self.end_angle - self.init_angle)
@property
def current_angle(self):
"""The current angle (specified by self.current_pos)."""
return self.angle_difference * self.current_pos + self.init_angle
Curve = _BaseCurve
class Hypotrochoid(Curve):
def relation(self):
def _relation(theta, r, R, d):
x = (R - r)*math.cos(theta) + d*math.cos((R - r)/r * theta)
y = (R - r)*math.sin(theta) - d*math.sin((R - r)/r * theta)
return (x**2 + y**2)**(1/2)
return _relation
1 个回答
1
把x、y和theta转换成极坐标形式输出是个错误。theta是参数方程的一个参数,并不是曲线上某个点的极角(实际上它是小圆心的极角)。
所以x和y就是可以直接使用的笛卡尔坐标。只需要在每一步都绘制这个点就可以了。这是Delphi测试,它能准确地绘制你想要的东西(Canvas.Pixels[x, y]会在(x,y)坐标处绘制一个点)。
R := 120;
rr := 40;
d:= 40;
for i := 0 to 999 do begin
a := i * 2 * Pi / 1000;
y := 200 + Round((R - rr) * sin(a) - d*sin((R-rr)/rr*a));
x := 200 + Round((R - rr) * cos(a) + d*cos((R-rr)/rr*a));
Canvas.Pixels[x, y] := clRed;
end;