用 python 写了个 3D 旋转三角形
写这个东西的主要目的……是给小朋友开开眼界(我真够无聊的
先上效果图!
然后是代码!
import random
# import numpy as np #
import math
import os
import time
os.system("title welcome to 3D try of snowstar.")
# 今天我们来做一个 显示 #D 图形的实验
# 好的开始!
# 首先,构造一个三维空间
# ↑ 没错,构造完了
# 我们给刚才构造的物体连一连线,这样会比较直观一点!
def makePointsFromLine(pointA, pointB):
t = 0
step = 1
points = []
# 嗯,教科书式的求两点距离公式
pointDistance = math.sqrt(
(pointB[0] - pointA[0]) ** 2 +
(pointB[1] - pointA[1]) ** 2 +
(pointB[2] - pointA[2]) ** 2 )
# 百分比步伐。。。这里x0.9的意思是步子小一点点,让点稍微密一点点(当然也会更慢)
percentStep = (1.0 / pointDistance) * 0.9
# 基本上就是把这条线切成好多个点,每个点的距离是这条线的 百分之 percent
percent = 0
while percent <= 1:
x = int(round(pointA[0] + percent * (pointB[0] - pointA[0])))
y = int(round(pointA[1] + percent * (pointB[1] - pointA[1])))
z = int(round(pointA[2] + percent * (pointB[2] - pointA[2])))
points.append([x, y, z])
percent += percentStep
return points
# 我发誓这是我取过的最长的函数名了。。。2016年8月29日17:41:08
def makePointsFromLinesFromPointsToPoints(dongXi):
pointsFromLines = []
len = dongXi.__len__()
for a in range(len):
for b in range(a + 1, len):
pointA = dongXi[a]
pointB = dongXi[b]
pointsFromLines += makePointsFromLine(pointA, pointB)
return pointsFromLines
# 嗯好了,我们来画一个屏幕
w = 59
h = 26
# 这个就是被拍扁的z轴了(看不懂跳过,先看下面
zlist = []
# 用不同的符号表示远近,越粗的越近,模拟近大远小
zsign = ["@@", "88", "OO", ";;","::", "..", " ."]
zsignlen = zsign.__len__()
# 这个函数决定屏幕上的某点显示什么鬼,是整个程序中最核心的部分
def posChar(x, y):
global zlist
global zlistlen
global zsign
global zsignlen
global points
global points2D
i = 0
if [x, y] not in points2D: # 去掉那些没有点的地方
return " "
for z in zlist: # 按z轴 从大到小询问,这里有没有人啊!!有没有啊!!
if [x, y, z] in points: # 如果有的话!!就印出来啦!!
return zsign[ int(i * zsignlen / zlistlen) ]
i += 1 # 嗯, i变大的时候,zsign 的图画就会变小了
return "XX" # 理论上这条不会被运行到,不过。。。万一我蠢呢!!!
# 这个函数是去除重复的点用的
def removeDupes(seq):
checked = []
for e in seq:
if e not in checked:
checked.append(e)
return checked
def perpareForPoints():
# 给上面的那个奇怪的形状,画成线,然后得到一堆点
global points
global zlist
global zlistlen
global points
global points2D
points = [[int(round(v)) for v in point] for point in pointSource]
# 画完之后是一堆3D空间上的点,我们把Z轴拍扁,变成2D空间点集
points2D = [point[0:2] for point in points]
# 接下来我们要抠掉多余的因为拍扁而 叠在一起的2D的点,每个位置留一个就够啦
points2D = removeDupes(points2D)
# 聪明的你已经发现。。上面2步只用1行就可以完成...
# 然后把那个3D的点集排成一个z轴,让它分一分前后
zlist = [point[2] for point in points]
zlist.sort(reverse=True) # 从大到小排,大的在前面
zlistlen = zlist.__len__()
def draw():
# 开始渲染!!
buf = []
buf.append("="*45+"welcome to 3D try of snowstar."+"="*44)
for y in range(-int(h/2), h-int(h/2)):
# 这么要紧的地方怎么可以没有注释呢!!!!嗯有了
buf.append("".join([ posChar(x, y) for x in range(-int(w/2), w-int(w/2))] ) + " ")
buf.append("="*45+"welcome to 3D try of snowstar."+"="*44)
# 打到屏幕上
os.system("cls")
print("\n".join(buf))
time.sleep( 1.0/61 ) # 设定为 60 fps
# 定义几个矩阵计算用的函数
# 别问我为什么不用numpy.....
# 因为。。。。我要用这东西教小盆友的啊!
# 这样我就不用再教人家装numpy啦!!!(好像更麻烦了呢
def 转置矩阵(矩阵):
len = 矩阵[0].__len__()
输出矩阵 = []
for i in range(len):
输出矩阵.append([行向量[i] for 行向量 in 矩阵])
return 输出矩阵
def 向量点积向量(向量A, 向量B):
if len(向量A) != len(向量B):
raise("雪星你又蠢啦两向量点积长度必须相同才行")
结果 = 0
for i in range(len(向量A)):
结果 += 向量A[i] * 向量B[i]
return 结果
def 向量点积矩阵(向量A, 矩阵B):
输出向量 = []
for 行向量 in 转置矩阵(矩阵B):
输出向量.append( 向量点积向量(向量A, 行向量) )
return 输出向量
def 矩阵点积矩阵(矩阵A, 矩阵B):
输出矩阵 = []
for 行向量 in 矩阵A:
输出矩阵.append( 向量点积矩阵(行向量, 矩阵B) )
return 输出矩阵
# 旋转函数
def rotating():
global wuti
global pointSource
theta = 0.05 # 不懂是多少度总之能转起来就行啦
# 这是一个普通的2D旋转矩阵,直接查 wiki 就可以啦
a, b = math.cos(theta), -math.sin(theta)
c, d = math.sin(theta), math.cos(theta)
# 我们把y轴方向固定下来,不让它转,让其它轴绕着它转
rotateMatrix = [[a, 0, b],
[0, 1, 0],
[c, 0, d]]
# 怎么转呢?直接点积啦
wuti = 矩阵点积矩阵(wuti, rotateMatrix)
# 不够过瘾? 再加个X轴! 以 1/4 的几率旋转,那么它就会得到一个浮动的效果。。。
if random.random() <= 1/4 and (int(time.time()) % 60) < 20:
# 我们把x轴方向固定下来,不让它转,让其它轴绕着它转
rotateMatrix = [[1, 0, 0],
[0, a, b],
[0, c, d]]
# 怎么转呢?直接点积啦
wuti = 矩阵点积矩阵(wuti, rotateMatrix)
pointSource = makePointsFromLinesFromPointsToPoints(wuti)
# 接下来我们构造一个三维物体,比如说……
# 三棱锥! P-ABC !!
# 大概长这样
# *-------*
# \\ //
# \\ //
# \ * /
# \|/
# *
wuti = [
[-10, -10, -10], # 点A
[ 10, -10, -10], # 点B
[ 0, 10, -10], # 点C
[ 0, 0, 10], # 点P
]
# 我们来初始化一下,第一个图形!
pointSource = makePointsFromLinesFromPointsToPoints(wuti)
perpareForPoints()
# 运行啦!!!!
fps = 0
t = time.time()
while(True):
draw()
rotating()
perpareForPoints()
dt = time.time() - t
t += dt
os.system("title " + "fts: %d" % (1 / dt))
评论
发表评论