首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >使用 Python 绘制外摆线

使用 Python 绘制外摆线

原创
作者头像
华科云商小徐
发布2025-02-07 11:34:12
发布2025-02-07 11:34:12
3180
举报
文章被收录于专栏:小徐学爬虫小徐学爬虫

外摆线(Epicycloid)是一个非常有趣的曲线,它是由一个圆在另一个圆的外面滚动时所描绘出来的轨迹。

以下是使用 Python 和 matplotlib 绘制外摆线的代码:

问题背景

在尝试使用计算机绘制外摆线时,我遇到了一些问题。对于那些不熟悉外摆线的人,外摆线的参数方程如下:

x(theta) = (R - r)cos(theta) + dcos((R-r)/rtheta)

以及

y(theta) = (R - r)sin(theta) - dsin((R-r)/rtheta)

维基百科对轨迹线的定义可以进一步解释:

外摆线是一个轨迹线,由一个附着在半径为 r 的圆上的点描绘而成,该圆在半径为 R 的固定圆的内部滚动,该点与内部圆的中心相距 d。

因此,当 r = d = 1 且 R = 3 时,外摆线应该看起来像这样:

但是,这肯定不是我使用计算方法最终得到的结果。我的外摆线(具有相同的值)看起来像这样:

由于 x 和 y 值由角度 theta 处的 x 和 y 的函数确定,因此我假设我可以简单地循环从 0 到 2pi 的 theta 值,并定期分别计算 x 和 y 值,然后以极坐标形式绘制坐标(其中 r2 = x2 + y**2),但我想我错了。也许我的公式是错的,但我刚刚与数学堆栈交换的几个人一起检查了一遍,我们无法弄清楚哪里出了问题。如果我的方法是错误的,应该使用什么方法来计算外摆线?

以下是代码:

代码语言:javascript
复制
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

解决方案

第一个回答者指出错误在于将 x、y 和 theta 转换为极坐标形式以进行输出。Theta 是参数方程的参数,它不是曲线点的极角(实际上它是小圆圈中心的极角),因此 x 和 y 是可以直接使用的笛卡尔坐标。只需为每个步骤绘制该点。这是 Delphi 测试,它准确地绘制您想要的结果(Canvas.Pixels[x, y] 绘制具有 (x,y) 坐标的点):

代码语言:javascript
复制
   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;

运行这段代码将生成一个外摆线图形。如果需要调整参数,比如改变半径或者改变曲线的形状,可以修改 Rrd 的值。

希望这对你有帮助!如果有其他问题,请告诉我。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档