改变乌龟图形的操作
Changing the operation of the turtle graphics
首先,我对编程还很陌生,而且在这里咬牙切齿。无论如何...
我正在尝试使用 this code 作为模型在 python 中构建太阳能 system/orbiting 模拟。
这是我的完整代码:
# Import math and turtle
import math
from turtle import *
import turtle
# Gravitational Constant
G = 6.67428e-11
# Scale: 1 pixel = 1 astronomical unit
# 1 astronomical unit = 1 AU = 149597900 km
AU = (149.6e6 * 1000) # 149.6 million km in meters
Scale = 250 / AU
wn = turtle.Screen()
wn = turtle.bgcolor('black')
class body(Turtle):
# Subclass of turtle representing a body
# Additional attributes:
# mass in kg
# vx, vy: x, y velocities in m/sc
# px, py: x, y positions in m
# Set background to black
# Turtle.bgcolor('black')
name = 'body'
mass = None
vx = vy = 0.0
px = py = 0.0
def attraction(self, other):
# (body): (fx, fy)
# returns the force exerted on this body by the other body.
# Report and error if the other object is the same as this one.
if self is other:
raise ValueError("Attraction of object %r to itself requested" % self.name)
# Compute the distance of the other body:
sx, sy = self.px, self.py
ox, oy = other.px, other.py
dx = (ox - sx)
dy = (oy - sy)
d = math.sqrt(dx**2 + dy**2)
# Report an Error if the distance is 0
# ZeroDivisionError would happen later otherwise
if d == 0:
raise ValueError("Collision between objects %r and %r" % (self.name, other.name))
# Compute the force of attraction
f = G * self.mass * other.mass / (d**2)
# Compute the direction of the force
theta = math.atan2(dy, dx)
fx = math.cos(theta) * f
fy = math.sin(theta) * f
return fx, fy
def updateInfo(step, bodies):
# (int, [body])
# Displays infomation about status of simulation.
print('Step #{}'.format(step))
for body in bodies:
s = '{:<8} Pos.={:>6.2f} Vel.={:>10.3f}'.format(body.name, body.px/AU, body.py/AU, body.vy)
print(s)
print()
def loop(bodies):
# (body)
# Never returns; loop through the simulation and updates the positions of given bodies
timestep = 24*3600 # One Day
for body in bodies:
body.penup()
body.hideturtle()
step = 1
while True:
updateInfo(step, bodies)
step += 1
force = {}
for body in bodies:
# Adds all the forces exerted on a body
totalFx = totalFy = 0.0
for other in bodies:
# Don't calculate attraction to itself
if body is other:
continue
fx, fy = body.attraction(other)
totalFx += fx
totalFy += fy
# Record the total force exerted
force[body] = (totalFx, totalFy)
# Update velocities based on the force.
for body in bodies:
fx, fy = force[body]
body.vx += fx / body.mass * timestep
body.vy += fy / body.mass * timestep
# Update positions
body.px += body.vx * timestep
body.py += body.vy * timestep
body.goto(body.px*Scale, body.py*Scale)
body.dot(5)
def main():
sun = body()
sun.name = 'Sun'
sun.mass = 1.98892 * 10**30
sun.pencolor('yellow')
earth = body()
earth.name = 'Earth'
earth.mass = 5.9742 * 10**24
earth.px = -1*AU
earth.vy = 29.783 * 1000
earth.pencolor('blue')
venus = body()
venus.name = 'Venus'
venus.mass = 4.8685 * 10**24
venus.px = 0.723 * AU
venus.vy = -35.02 * 1000
venus.pencolor('orange')
loop([sun, earth, venus])
if __name__ == '__main__':
main()
我为 window 添加了 import turtle
行,因为我想将 window 颜色更改为黑色。我知道这两种导入样式之间的主要区别以及为什么有些人更喜欢其中一种(或者为什么一种不好)。我想更改 class 的结构,这样我就可以更好地控制模拟的图形,并摆脱 from x import *
方法。欢迎任何建议!
我的选择是使用导入:
from turtle import Screen, Turtle
通过阻止功能接口强制将面向对象的接口设置为 turtle。但我会更进一步:
fx, fy = force[body]
body.vx += fx / body.mass * timestep
body.vy += fy / body.mass * timestep
# Update positions
body.px += body.vx * timestep
body.py += body.vy * timestep
Turtle 可以使用向量 (Vec2D
),所以我们不需要对 x 和 y 值进行单独的操作。
for body in bodies:
body.penup()
body.hideturtle()
这可以是 Body
class 初始值设定项的一部分。
body.goto(body.px*Scale, body.py*Scale)
我们可以预先缩放乌龟的坐标系,避免在主循环中进行缩放操作。
while True:
不是像 turtle 这样的事件驱动的世界——而是使用 ontimer()
。以下返工实施了上述建议,以展示我可能如何解决此问题:
# Import math and turtle
import math
from turtle import Screen, Turtle, Vec2D
# Gravitational Constant
G = 6.67428e-11
# Scale: 1 pixel = 1 astronomical unit
# 1 astronomical unit = 1 AU = 149597900 km
AU = 149.6e6 * 1000 # 149.6 million km in meters
TIME_STEP = 24 * 3600 # One day
FORMAT = "{:<8} Pos.={:>6.2f} Vel.={:>10.3f}"
CURSOR_SIZE = 20
class Body(Turtle):
''' Subclass of turtle representing a body. '''
# Additional attributes:
# mass in kg
# velocity in m/sc
# position in m
name = 'body'
mass = None
velocity = Vec2D(0.0, 0.0)
Position = Vec2D(0.0, 0.0) # Position to avoid conflict with turtle's position() method
def __init__(self, dot_size=5):
super().__init__(shape='circle')
self.shapesize(dot_size / CURSOR_SIZE)
self.penup()
def attraction(self, other):
''' Return the force exerted on this body by the other body. '''
# Report an error if the other object is the same as this one.
if self is other:
raise ValueError("Attraction of object {} to itself requested".format(self.name))
# Compute the distance of the other body:
dx, dy = other.Position - self.Position
distance = math.sqrt(dx**2 + dy**2)
# Report an Error if the distance is 0
# ZeroDivisionError would happen later otherwise
if distance == 0:
raise ValueError("Collision between objects {} and {}".format(self.name, other.name))
# Compute the force of attraction
force = G * self.mass * other.mass / distance**2
# Compute the direction of the force
theta = math.atan2(dy, dx)
direction = Vec2D(math.cos(theta), math.sin(theta))
return direction * force
def updateInfo(step: int, bodies: list):
''' Display information about status of simulation. '''
print('Step #{}'.format(step))
for body in bodies:
print(FORMAT.format(body.name, abs(body.Position * (1 / AU)), abs(body.velocity)))
print()
def single_step(bodies, step=1):
''' Step through the simulation and updates the positions of given bodies. '''
updateInfo(step, bodies)
for body in bodies:
# Adds all the forces exerted on a body
totalForce = Vec2D(0.0, 0.0)
for other in bodies:
# Don't calculate attraction to itself
if body is not other:
totalForce += body.attraction(other)
# Update velocities based on the force.
body.velocity += totalForce * (1 / body.mass) * TIME_STEP
# Update position
body.Position += body.velocity * TIME_STEP
body.setposition(body.Position)
screen.update()
screen.ontimer(lambda: single_step(bodies, step + 1))
if __name__ == '__main__':
screen = Screen()
screen.bgcolor('black')
screen.setworldcoordinates(-2 * AU, -2 * AU, 2 * AU, 2 * AU)
screen.tracer(False)
sun = Body(10)
sun.name = 'Sun'
sun.mass = 1.98892 * 10**30
sun.color('yellow')
earth = Body()
earth.name = 'Earth'
earth.mass = 5.9742 * 10**24
earth.Position = Vec2D(-AU, 0.0)
earth.velocity = Vec2D(0.0, 29.783 * 1000)
earth.color('blue')
venus = Body()
venus.name = 'Venus'
venus.mass = 4.8685 * 10**24
venus.Position = Vec2D(0.723 * AU, 0.0)
venus.velocity = Vec2D(0.0, -35.02 * 1000)
venus.color('orange')
single_step([sun, earth, venus])
screen.mainloop()
updateInfo()
函数让我感到困惑,因为它打印了四个数据参数中的三个。 (即打印 Y 位置作为速度!)我只是随机做了一些事情,打印向量的绝对值。
首先,我对编程还很陌生,而且在这里咬牙切齿。无论如何...
我正在尝试使用 this code 作为模型在 python 中构建太阳能 system/orbiting 模拟。
这是我的完整代码:
# Import math and turtle
import math
from turtle import *
import turtle
# Gravitational Constant
G = 6.67428e-11
# Scale: 1 pixel = 1 astronomical unit
# 1 astronomical unit = 1 AU = 149597900 km
AU = (149.6e6 * 1000) # 149.6 million km in meters
Scale = 250 / AU
wn = turtle.Screen()
wn = turtle.bgcolor('black')
class body(Turtle):
# Subclass of turtle representing a body
# Additional attributes:
# mass in kg
# vx, vy: x, y velocities in m/sc
# px, py: x, y positions in m
# Set background to black
# Turtle.bgcolor('black')
name = 'body'
mass = None
vx = vy = 0.0
px = py = 0.0
def attraction(self, other):
# (body): (fx, fy)
# returns the force exerted on this body by the other body.
# Report and error if the other object is the same as this one.
if self is other:
raise ValueError("Attraction of object %r to itself requested" % self.name)
# Compute the distance of the other body:
sx, sy = self.px, self.py
ox, oy = other.px, other.py
dx = (ox - sx)
dy = (oy - sy)
d = math.sqrt(dx**2 + dy**2)
# Report an Error if the distance is 0
# ZeroDivisionError would happen later otherwise
if d == 0:
raise ValueError("Collision between objects %r and %r" % (self.name, other.name))
# Compute the force of attraction
f = G * self.mass * other.mass / (d**2)
# Compute the direction of the force
theta = math.atan2(dy, dx)
fx = math.cos(theta) * f
fy = math.sin(theta) * f
return fx, fy
def updateInfo(step, bodies):
# (int, [body])
# Displays infomation about status of simulation.
print('Step #{}'.format(step))
for body in bodies:
s = '{:<8} Pos.={:>6.2f} Vel.={:>10.3f}'.format(body.name, body.px/AU, body.py/AU, body.vy)
print(s)
print()
def loop(bodies):
# (body)
# Never returns; loop through the simulation and updates the positions of given bodies
timestep = 24*3600 # One Day
for body in bodies:
body.penup()
body.hideturtle()
step = 1
while True:
updateInfo(step, bodies)
step += 1
force = {}
for body in bodies:
# Adds all the forces exerted on a body
totalFx = totalFy = 0.0
for other in bodies:
# Don't calculate attraction to itself
if body is other:
continue
fx, fy = body.attraction(other)
totalFx += fx
totalFy += fy
# Record the total force exerted
force[body] = (totalFx, totalFy)
# Update velocities based on the force.
for body in bodies:
fx, fy = force[body]
body.vx += fx / body.mass * timestep
body.vy += fy / body.mass * timestep
# Update positions
body.px += body.vx * timestep
body.py += body.vy * timestep
body.goto(body.px*Scale, body.py*Scale)
body.dot(5)
def main():
sun = body()
sun.name = 'Sun'
sun.mass = 1.98892 * 10**30
sun.pencolor('yellow')
earth = body()
earth.name = 'Earth'
earth.mass = 5.9742 * 10**24
earth.px = -1*AU
earth.vy = 29.783 * 1000
earth.pencolor('blue')
venus = body()
venus.name = 'Venus'
venus.mass = 4.8685 * 10**24
venus.px = 0.723 * AU
venus.vy = -35.02 * 1000
venus.pencolor('orange')
loop([sun, earth, venus])
if __name__ == '__main__':
main()
我为 window 添加了 import turtle
行,因为我想将 window 颜色更改为黑色。我知道这两种导入样式之间的主要区别以及为什么有些人更喜欢其中一种(或者为什么一种不好)。我想更改 class 的结构,这样我就可以更好地控制模拟的图形,并摆脱 from x import *
方法。欢迎任何建议!
我的选择是使用导入:
from turtle import Screen, Turtle
通过阻止功能接口强制将面向对象的接口设置为 turtle。但我会更进一步:
fx, fy = force[body]
body.vx += fx / body.mass * timestep
body.vy += fy / body.mass * timestep
# Update positions
body.px += body.vx * timestep
body.py += body.vy * timestep
Turtle 可以使用向量 (Vec2D
),所以我们不需要对 x 和 y 值进行单独的操作。
for body in bodies:
body.penup()
body.hideturtle()
这可以是 Body
class 初始值设定项的一部分。
body.goto(body.px*Scale, body.py*Scale)
我们可以预先缩放乌龟的坐标系,避免在主循环中进行缩放操作。
while True:
不是像 turtle 这样的事件驱动的世界——而是使用 ontimer()
。以下返工实施了上述建议,以展示我可能如何解决此问题:
# Import math and turtle
import math
from turtle import Screen, Turtle, Vec2D
# Gravitational Constant
G = 6.67428e-11
# Scale: 1 pixel = 1 astronomical unit
# 1 astronomical unit = 1 AU = 149597900 km
AU = 149.6e6 * 1000 # 149.6 million km in meters
TIME_STEP = 24 * 3600 # One day
FORMAT = "{:<8} Pos.={:>6.2f} Vel.={:>10.3f}"
CURSOR_SIZE = 20
class Body(Turtle):
''' Subclass of turtle representing a body. '''
# Additional attributes:
# mass in kg
# velocity in m/sc
# position in m
name = 'body'
mass = None
velocity = Vec2D(0.0, 0.0)
Position = Vec2D(0.0, 0.0) # Position to avoid conflict with turtle's position() method
def __init__(self, dot_size=5):
super().__init__(shape='circle')
self.shapesize(dot_size / CURSOR_SIZE)
self.penup()
def attraction(self, other):
''' Return the force exerted on this body by the other body. '''
# Report an error if the other object is the same as this one.
if self is other:
raise ValueError("Attraction of object {} to itself requested".format(self.name))
# Compute the distance of the other body:
dx, dy = other.Position - self.Position
distance = math.sqrt(dx**2 + dy**2)
# Report an Error if the distance is 0
# ZeroDivisionError would happen later otherwise
if distance == 0:
raise ValueError("Collision between objects {} and {}".format(self.name, other.name))
# Compute the force of attraction
force = G * self.mass * other.mass / distance**2
# Compute the direction of the force
theta = math.atan2(dy, dx)
direction = Vec2D(math.cos(theta), math.sin(theta))
return direction * force
def updateInfo(step: int, bodies: list):
''' Display information about status of simulation. '''
print('Step #{}'.format(step))
for body in bodies:
print(FORMAT.format(body.name, abs(body.Position * (1 / AU)), abs(body.velocity)))
print()
def single_step(bodies, step=1):
''' Step through the simulation and updates the positions of given bodies. '''
updateInfo(step, bodies)
for body in bodies:
# Adds all the forces exerted on a body
totalForce = Vec2D(0.0, 0.0)
for other in bodies:
# Don't calculate attraction to itself
if body is not other:
totalForce += body.attraction(other)
# Update velocities based on the force.
body.velocity += totalForce * (1 / body.mass) * TIME_STEP
# Update position
body.Position += body.velocity * TIME_STEP
body.setposition(body.Position)
screen.update()
screen.ontimer(lambda: single_step(bodies, step + 1))
if __name__ == '__main__':
screen = Screen()
screen.bgcolor('black')
screen.setworldcoordinates(-2 * AU, -2 * AU, 2 * AU, 2 * AU)
screen.tracer(False)
sun = Body(10)
sun.name = 'Sun'
sun.mass = 1.98892 * 10**30
sun.color('yellow')
earth = Body()
earth.name = 'Earth'
earth.mass = 5.9742 * 10**24
earth.Position = Vec2D(-AU, 0.0)
earth.velocity = Vec2D(0.0, 29.783 * 1000)
earth.color('blue')
venus = Body()
venus.name = 'Venus'
venus.mass = 4.8685 * 10**24
venus.Position = Vec2D(0.723 * AU, 0.0)
venus.velocity = Vec2D(0.0, -35.02 * 1000)
venus.color('orange')
single_step([sun, earth, venus])
screen.mainloop()
updateInfo()
函数让我感到困惑,因为它打印了四个数据参数中的三个。 (即打印 Y 位置作为速度!)我只是随机做了一些事情,打印向量的绝对值。