用 python 写了个 3D 旋转三角形

用 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))

评论