首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >带有平移器的双摆动画

带有平移器的双摆动画
EN

Code Review用户
提问于 2020-02-01 11:11:16
回答 1查看 586关注 0票数 8

这是我的第一个项目:我用Python上的tkinter制作了一个双摆动画。你能给我一些关于哪些方面可以改进的反馈意见吗?谢谢!

代码:

代码语言:javascript
复制
# General imports
import tkinter as tk
import random
import math as m

# Parameters
G = 9.81


class Pendulum():
    def __init__(self, theta: float, theta_dot: float,
                 mass: float, length: float,
                 width: int = 3):
        """Creates a Pendulum with a given position, velocity, length and mass.
        width represent the width of the rope of the pendulum.
        The size of the pendulum is proportional to its mass."""

        self.theta = theta
        self.theta_dot = theta_dot
        self.mass = mass
        self.length = length
        self.width = width


class App(tk.Tk):
    def __init__(self,
                 pendulum_1: Pendulum, pendulum_2: Pendulum,
                 width: int = 600, height: int = 600,
                 offset_width: int = 300, offset_height: int = 120,
                 dt: float = 0.05):
        """Initialize the widget for the double pendulum animation.

        offset_width and offset_height represent the x and y offsets from the
        top left corner of the canvas to place the first pendulum."""

        # Setting attributes
        self.width = width
        self.height = height
        self.offset_width = offset_width
        self.offset_height = offset_height
        self.dt = dt
        self.pendulum_1 = pendulum_1
        self.pendulum_2 = pendulum_2
        self.trace_coords = []

        # Setting canvas widget
        tk.Tk.__init__(self)
        self.title("Double Pendulum")
        self.canvas = tk.Canvas(self,
                                width=self.width, height=self.height)
        self.canvas.pack(side="top")

        # Action
        self.after(1, self.draw_frame)

    def update_pendulums_positions(self):
        """Update the angle positions and velocities of the two pendulums"""

        # Dealing with the first pendulum equation of motion
        num_1 = -G * (2 * self.pendulum_1.mass + self.pendulum_2.mass)
        num_1 *= m.sin(self.pendulum_1.theta)

        num_2 = -self.pendulum_2.mass * G
        num_2 *= m.sin(
            self.pendulum_1.theta -
            2 * self.pendulum_2.theta
        )

        num_3 = -2 * m.sin(self.pendulum_1.theta-self.pendulum_2.theta)
        num_3 *= self.pendulum_2.mass
        num_3 *= (
            self.pendulum_2.theta_dot**2 * self.pendulum_2.length +
            self.pendulum_1.theta_dot**2 * self.pendulum_1.length *
            m.cos(
                self.pendulum_1.theta -
                self.pendulum_2.theta
            )
        )

        denom_1 = self.pendulum_1.length * (
            2 * self.pendulum_1.mass +
            self.pendulum_2.mass -
            self.pendulum_2.mass *
            m.cos(
                2 * self.pendulum_1.theta -
                2 * self.pendulum_2.theta
            )
        )

        # Dealing with the second pendulum equation of motion

        num_4 = 2 * m.sin(self.pendulum_1.theta - self.pendulum_2.theta)

        num_5 = (
            self.pendulum_1.theta_dot**2 *
            self.pendulum_1.length *
            (self.pendulum_1.mass + self.pendulum_2.mass)
        )

        num_6 = G * (self.pendulum_1.mass + self.pendulum_2.mass)
        num_6 *= m.cos(self.pendulum_1.theta)

        num_7 = self.pendulum_2.theta_dot**2 * self.pendulum_2.length
        num_7 *= self.pendulum_2.mass * m.cos(
            self.pendulum_1.theta -
            self.pendulum_2.theta
        )

        denom_2 = self.pendulum_2.length * (
            2 * self.pendulum_1.mass +
            self.pendulum_2.mass -
            self.pendulum_2.mass *
            m.cos(
                2 * self.pendulum_1.theta -
                2 * self.pendulum_2.theta
            )
        )

        # Compute the accelerations
        theta1_dotdot = (num_1 + num_2 + num_3) / denom_1
        theta2_dotdot = (num_4*(num_5+num_6+num_7)) / denom_2

        # Update the velocities and positions
        self.pendulum_1.theta_dot += theta1_dotdot * self.dt
        self.pendulum_1.theta += self.pendulum_1.theta_dot * self.dt
        self.pendulum_2.theta_dot += theta2_dotdot * self.dt
        self.pendulum_2.theta += self.pendulum_2.theta_dot * self.dt

    def draw_pendulums(self):
        """Draw the two pendulums and the trace"""

        # Cartesian coordinates
        x1 = self.pendulum_1.length * m.sin(self.pendulum_1.theta)
        y1 = self.pendulum_1.length * m.cos(self.pendulum_1.theta)

        x2 = x1 + self.pendulum_2.length * m.sin(self.pendulum_2.theta)
        y2 = y1 + self.pendulum_2.length * m.cos(self.pendulum_2.theta)

        # Update the trace of the second pendulum
        self.trace_coords.append(
            (
                self.offset_width + x2,
                self.offset_height + y2,
                self.offset_width + x2,
                self.offset_height + y2
            )
        )

        # Draw the trace
        self.canvas.create_line(self.trace_coords, fill='black', tag='trace')

        # Draw the first pendulum
        self.canvas.create_line(
            self.offset_width, self.offset_height,
            self.offset_width + x1, self.offset_height + y1,
            width=self.pendulum_1.width, fill='pink', tags='pendulum'
        )
        self.canvas.create_oval(
            self.offset_width - self.pendulum_1.mass + x1,
            self.offset_height - self.pendulum_1.mass + y1,
            self.offset_width + self.pendulum_1.mass + x1,
            self.offset_height + self.pendulum_1.mass + y1,
            fill='pink', outline='pink', tags='pendulum'
        )

        # Draw the second pendulum
        self.canvas.create_line(
            self.offset_width + x1, self.offset_height + y1,
            self.offset_width + x2, self.offset_height + y2,
            width=self.pendulum_2.width, fill='pink', tags='pendulum'
        )
        self.canvas.create_oval(
            self.offset_width - self.pendulum_2.mass + x2,
            self.offset_height - self.pendulum_2.mass + y2,
            self.offset_width + self.pendulum_2.mass + x2,
            self.offset_height + self.pendulum_2.mass + y2,
            fill='pink', outline='pink', tags='pendulum'
        )

    def draw_frame(self):
        """Draw the current frame"""

        # Delete objects on the canvas to redraw
        self.canvas.delete('trace')
        self.canvas.delete('pendulum')

        # Update the positions and draw the frame
        self.update_pendulums_positions()
        self.draw_pendulums()

        # Repeat
        self.after(1, self.draw_frame)


if __name__ == '__main__':

    # Initialization of the two pendulums
    theta1 = random.random() * 2 * m.pi
    theta2 = random.random() * 2 * m.pi

    pendulum_1_parameters = {
        "theta": theta1,
        "theta_dot": 0,
        "mass": 10,
        "length": 100,
        "width": 3
    }
    pendulum_2_parameters = {
        "theta": theta2,
        "theta_dot": 0,
        "mass": 10,
        "length": 100,
        "width": 3
    }

    pendulum_1 = Pendulum(**pendulum_1_parameters)
    pendulum_2 = Pendulum(**pendulum_2_parameters)

    # Run the animation
    animation_parameters = {
        "pendulum_1": pendulum_1,
        "pendulum_2": pendulum_2,
        "width": 600,
        "height": 600,
        "offset_width": 300,
        "offset_height": 150,
        "dt": 0.05
    }
    app = App(**animation_parameters)
    app.mainloop()
EN

回答 1

Code Review用户

回答已采纳

发布于 2020-02-01 16:27:46

看着他们平衡是一种催眠!

假设增量时间总是0,05秒。为了获得更好的模拟,您应该检索真实的增量时间。在代码质量方面,在方法update_pendulums_positions中,如果使用显式变量名而不是"num_“之类的内容,代码的可读性将大大提高。是的,这是物理计算,但是,所有的计算都有意义。如果变量名称更长,您的代码不会运行得更慢!(是的,已经听到一位同事为C++这么说)

您还应该重构一些片段,例如:

  • draw_pendulums: create_line和create_oval是相同的,只要把摆作为函数的一个参数
  • "denom“(分母?)的计算:它们之间只有长度变化。
  • 更新速度和位置:每个钟摆的两条线是相同的。
  • 基数坐标,只需添加一个(x,y)偏移量作为参数,根据第一个摆的坐标处理第二个摆的计算。
  • num_3和num_7:如果我是对的,这两个可以写成
代码语言:javascript
复制
num_3 = -2 * m.sin(self.pendulum_1.theta - self.pendulum_2.theta)
num_3 *= self.pendulum_2.mass * self.pendulum_2.theta_dot**2 * self.pendulum_2.length
num_3 *= 1 + m.cos(self.pendulum_1.theta - self.pendulum_2.theta)

num_7 = self.pendulum_2.mass * self.pendulum_2.theta_dot**2 * self.pendulum_2.length
num_7 *= m.cos(self.pendulum_1.theta - self.pendulum_2.theta)

你可以看到有些计算是做了两次的。使用中间变量执行一次。您的应用程序并不重,但这将有助于通过优化您的代码使其更具有可伸缩性。

TL,DR:干的自证代码 (尽管写注释是一条规则,但是拥有一段“人类可读的”代码也不会有什么坏处)。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/236491

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档