这个不是我画的
画镜像图,是上编程课时,孩子们比较喜欢的一个案例,虽然数学公式可能不懂,但程序的设计意图,理解起来没什么困难,一句“万花筒”,就什么都解决了。。
核心代码,就是根据原始坐标点,沿屏幕中心,旋转 n-1 次,获取 n-1 个镜像点。
def add_point(point):
'''添加当前点,并计算所有镜像点,添加到 m_lines 里'''
if points ==[] or point != points[-1]: # 加一个判断,避免加入重复的点,浪费空间
points.append(point)
for i in range(1,n): # 计算所有镜像点坐标
mp = get_mirror_point(point,m_angle * i) # 第 i 个镜像点
m_lines[i][-1].append(mp)
def get_mirror_point(p,angle):
'''计算 p 点 沿屏幕中心(cx,cy)旋转 angle 后的坐标值'''
cx,cy = W/2, H/2 # 以屏幕中心点为坐标原点
angle = math.radians(angle) # 角度转化为弧度
x,y = p[0] - cx, cy - p[1]
# 用到旋转公式
mx = round( x * math.cos(angle) - y * math.sin(angle))
my = round( x * math.sin(angle) y * math.cos(angle))
nx = mx cx
ny = cy - my
return nx,ny
上面代码用到了三角函数,其实用向量来计算的话,会更简单一些。
没时间解释了,下面贴出完整代码(mirror.py):
import pygame
from pygame.locals import *
import os,random,sys,math
import time
# 常量
W , H = 1280,800
n = 5 # 镜像次数
GREEN = (0,255,0)
# 初始化游戏窗口
pygame.init()
# screen = pygame.display.set_mode((0,0),FULLSCREEN)
screen = pygame.display.set_mode((W,H))
W,H = screen.get_size()
pygame.display.set_caption("极坐标")
# 全局变量
lines = [] # 用户画的原始线段
points = [] # 当前画的线段的点集合(从鼠标落下到鼠标抬起,鼠标经过的所有坐标点)
'''
m_lines 是个三维数组,分三个层次:
第一层:所有线段集合,用户线段集合 (n-1)个镜像线段集合
第二层:一个用户线段集合里,包含多个分开的线段,每次落笔抬笔之间,即构成一个线段
第三层:每个线段,包含的点的集合
'''
m_lines = [] # 镜像线段 mirror lines,由用户绘制线段,生成的所有镜像线段,包含了用户线段
colors = []
def init():
'''初始化'''
global lines,points,m_angle
m_angle = 360 / n # 镜像单位角度
m_lines.clear() # 清空
for i in range(n):
m_lines.append([[]]) # 根据镜像数量,先分配好空间
colors.append(random_rainbow_color()) # 随机颜色
lines = m_lines[0] # 线段
points = lines[0] # 点
def random_rainbow_color():
'''随机生成饱和度最大的颜色'''
h = random.randint(0,360)
color = pygame.Color((0,0,0))
color.hsla = (h,100,50,100)
return color
def get_mirror_point(p,angle):
'''计算 p 点 沿屏幕中心(cx,cy)旋转 angle 后的坐标值'''
cx,cy = W/2, H/2 # 以屏幕中心点为坐标原点
angle = math.radians(angle) # 角度转化为弧度
x,y = p[0] - cx, cy - p[1]
# 用到旋转公式
mx = round( x * math.cos(angle) - y * math.sin(angle))
my = round( x * math.sin(angle) y * math.cos(angle))
nx = mx cx
ny = cy - my
# print(p,(x,y),(mx,my),(nx,ny))
return nx,ny
def new_line():
'''分配新的线段空间'''
global points
lines.append([])
points = lines[-1]
for i in range(1,n):
m_lines[i].append([])
def add_point(point):
'''添加当前点,并计算所有镜像点,添加到 m_lines 里'''
if points ==[] or point != points[-1]: # 加一个判断,避免加入重复的点,浪费空间
points.append(point)
for i in range(1,n): # 计算所有镜像点坐标
mp = get_mirror_point(point,m_angle * i) # 第 i 个镜像点
m_lines[i][-1].append(mp)
def save_image():
'''保存图片'''
filename = 'mirror-' time.strftime("%Y%m%d%H%M%S", time.localtime()) '.png'
pygame.image.save(screen,filename)
init()
drawing = False
running = True
clock = pygame.time.Clock()
while running:
# 事件
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
if event.key == K_SPACE: # 按空格键清屏
init()
if event.key == K_s:
key_pressed = pygame.key.get_pressed()
if key_pressed[K_LCTRL] or key_pressed[K_RCTRL]: # CTRL s 组合键,保存图片
save_image()
if event.type == MOUSEBUTTONDOWN: # 按下鼠标
drawing = True # 开始画线
if event.type == MOUSEBUTTONUP: # 放开鼠标
drawing = False # 画线结束
new_line() #分配新的线段空间
if event.type == MOUSEMOTION: # 移动鼠标
if drawing:
point = pygame.mouse.get_pos()
add_point(point) # 添加当前坐标点和对应的镜像点
screen.fill(0) # 填充背景
# 开始画图
for i,m_line in enumerate(m_lines):
for line in m_line:
if len(line) >1:
pygame.draw.lines(screen,colors[i],False,line,2) # colors[i]
# 结束画图
pygame.display.update()
clock.tick(30)
空格:清屏
CTRL S : 保存图片
这个是我画的