2024-09-02|閱讀時間 ‧ 約 23 分鐘

The Nature of Code閱讀心得與Python實作:3.5 Polar vs. Cartesian...

這一節的標題是
3.5 Polar vs. Cartesian Coordinates
因為方格子標題字數限制,所以沒完整顯現

到目前為止,我們在畫圖時,使用的都是直角座標系統,也就是笛卡兒座標(Cartesian coordinate)系統。在直角座標系統中,只要給定x、y軸的座標值,我們就可以找到那個點。所以,在直角座標系統中的點,就以(x, y)來表示。

除了直角座標系統外,極座標(polar coordinate)系統是另一種相當有用的座標系統。這種座標系統,使用的是半徑和旋轉角度來定位空間中的點。這裡的半徑,指的是距離原點的距離;而角度指的,則是繞著原點旋轉多大的角度。所以,極座標系統中的點,就以(r, θ)來表示,其中r就是半徑;而θ則是角度。

以向量的觀點來看,直角座標系統的x、y,就是向量的兩個分量;而極座標系統的r、θ,則是向量的大小和方向,也就是長度和角度。

利用三角函數,可以在直角座標和極座標之間進行轉換

  • 由極座標轉成直角座標
    x = r cosθ
    y = r sinθ
  • 由直角座標轉成極座標
    r = (x2+y2)1/2
    θ = tan-1(y/x)

在pygame中,也提供了方法,可以在直角座標和極座標之間進行轉換:

由極座標轉成直角座標

vec = pygame.Vector2()
vec.from_polar((r, theta))

vec = pygame.Vector2.from_polar((r, theta))

由直角座標轉成極座標

vec = pygame.Vector2(x, y)
(r, theta) = vec.as_polar()

要注意的是,這裡的角度theta,單位是「度」。

某些問題使用直角座標系統來處理時,會有些難度,但是如果用極座標系統來處理,卻會非常簡單。例如,從下面這個例子就可以看出來,要讓一個圖案繞著一個點做圓周運動,用極座標系統來處理,就會比用直角座標系統來處理,要簡單許多。

Example 3.4: Polar to Cartesian

# python version 3.10.9
import sys

import pygame # version 2.3.0


pygame.init()

pygame.display.set_caption("Example 3.4: Polar to Cartesian")

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)

WIDTH, HEIGHT = screen_size = (640, 360)
screen = pygame.display.set_mode(screen_size)

screen_center = (WIDTH//2, HEIGHT//2)

FPS = 60
frame_rate = pygame.time.Clock()

r, theta = 75, 0

while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()

screen.fill(WHITE)

# 圓球位置
position = pygame.Vector2.from_polar((r, theta)) + pygame.Vector2(screen_center)

pygame.draw.line(screen, BLACK, screen_center, position, 2)
pygame.draw.circle(screen, BLACK, position, 16)

# 角度的單位是「度」
theta += 1

pygame.display.update()
frame_rate.tick(FPS)

Exercise 3.5

# python version 3.10.9
import sys

import pygame # version 2.3.0

pygame.init()


pygame.display.set_caption("Exercise 3.5")

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)

WIDTH, HEIGHT = screen_size = (640, 360)
screen = pygame.display.set_mode(screen_size)

screen_center = (WIDTH//2, HEIGHT//2)

FPS = 60
frame_rate = pygame.time.Clock()

r, theta = 0, 0

screen.fill(WHITE)

while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()

position = pygame.Vector2.from_polar((r, theta)) + pygame.Vector2(screen_center)
pygame.draw.circle(screen, BLACK, position, 5)

r += 0.05
theta += 1 # 角度的單位是「度」

pygame.display.update()
frame_rate.tick(FPS)

Exercise 3.6

class Spaceship:
def __init__(self, mass=1, heading=90):
# 取得顯示畫面的大小
self.screen = pygame.display.get_surface()
self.width, self.height = self.screen.get_size()

# 讓傳遞進來的數值來決定物體的質量
self.mass = mass

# 航向,單位為「度」
self.heading = heading

# 起始位置位於畫面中央
self.position = pygame.Vector2(self.width//2, self.height//2)

# 最高速限
self.top_speed = 5

# 初始速度與初始加速度
self.velocity = pygame.Vector2(0, 0)
self.acceleration = pygame.Vector2(0, 0)

# 設定spaceship所在surface的格式為per-pixel alpha
self.surface = pygame.Surface((35, 30), pygame.SRCALPHA)

def set_heading(self):
# 按鍵盤向左鍵逆時針旋轉,向右鍵順時針旋轉
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.heading += 5
elif keys[pygame.K_RIGHT]:
self.heading -= 5

def generate_thrust(self):
# 按下z鍵會產生推力
keys = pygame.key.get_pressed()
if keys[pygame.K_z]:
thrust = pygame.Vector2.from_polar((1, self.heading))
thrust.scale_to_length(0.1)
# 顯示畫面的y軸方向與直角座標系統的y軸方向相反
thrust.y = -thrust.y
else:
# 沒有按下z鍵,產生反向推力,減速到停下為止
if spaceship.velocity.length() > 1e-5:
thrust = -spaceship.velocity
thrust.scale_to_length(0.05)
else:
thrust = pygame.Vector2(0, 0)

return thrust

def apply_force(self, force):
self.acceleration += force/self.mass

def update(self):
self.velocity += self.acceleration
# 速度太大時限速,太小時直接歸零
if self.velocity.length() > self.top_speed:
self.velocity.scale_to_length(self.top_speed)
elif self.velocity.length() < 1e-5:
self.velocity *= 0

self.position += self.velocity

self.acceleration *= 0

def show(self):
body = [(35, 15), (5, 0), (5, 30)]
pygame.draw.polygon(self.surface, (0, 0, 0), body)

nozzle1 = pygame.Rect(0, 5, 5, 5)
pygame.draw.rect(self.surface, (0, 0, 0), nozzle1)

nozzle2 = pygame.Rect(0, 20, 5, 5)
pygame.draw.rect(self.surface, (0, 0, 0), nozzle2)

rotated_surface = pygame.transform.rotate(self.surface, self.heading)
rect_new = rotated_surface.get_rect(center=self.position)

# 把spaceship所在的surface貼到最後要顯示的畫面上
self.screen.blit(rotated_surface, (rect_new.x, rect_new.y))

def check_edges(self):
if self.position.x > self.width:
self.position.x = 0
elif self.position.x < 0:
self.position.x = self.width

if self.position.y > self.height:
self.position.y = 0
elif self.position.y < 0:
self.position.y = self.height

# python version 3.10.9
import sys

import pygame # version 2.3.0

pygame.init()

pygame.display.set_caption("Exercise 3.6")

WHITE = (255, 255, 255)

screen_size = (640, 360)
screen = pygame.display.set_mode(screen_size)

FPS = 60
frame_rate = pygame.time.Clock()

spaceship = Spaceship()

while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()

screen.fill(WHITE)

spaceship.set_heading()

thrust = spaceship.generate_thrust()
spaceship.apply_force(thrust)

spaceship.update()
spaceship.check_edges()
spaceship.show()

pygame.display.update()
frame_rate.tick(FPS)


分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.