Program_Light

KMS14 : Python을 이용한 simple rotatable 3D 구현 프로그램 (source)

KMS studio 2021. 10. 2. 13:00

이번에는 Python을 이용해서 간단한 회전 가능한 3D 체 모델링 프로그램을 만들어봤는데, 기능이 너무 조악해서 소올찍히 올릴지 말지 상당히 고민했습니다. 물론 지금 부족한 기능들은 모두 다음주에 추가해서 올릴 계획입니다.
지금 프로그램은 프로토? 라고 봐주세요


consideration

참고로, 이 프로그램은 점을 A도 회전시킬 때 sinA + cosAi를 점에 곱하여 회전된 점의 좌표를 계산하는 방식을 취하고 있습니다. 

또한, 모든 sin, cos함수는 degree각도를 사용합니다. (간지나고 표준적으로다가 원주각을 사용할려고 했는데, 그냥 degree로 했어요;;)


source

source.py

import tkinter
import math
import time
import random

# X coor, Y coor, Z coor
#nodeL = [[200, -150, 200], [-200, -150, 200], [-200, -150, -200], [200, -150, -200], [0, 250, 0]]
#edgeL = [(0, 1), (1, 2), (2, 3), (3, 0), (0, 4), (1, 4), (2, 4), (3, 4)]
#faceL = []
nodeL = [];
edgeL = [];
faceL = [];

# coor indi
coorL = [[20, 0, 0], [0, 20, 0], [0, 0, -20]]
coorC = ['#FF0000', '#00FF00', '#0000FF']

#Object size
Obj_C = '#000000' #object color
Nod_R = 5 # node radius
Edg_W = 5 # Edge width

scale = 1
Scl_L = 20 #scale max limit

Cen_X = 300 # center X
Cen_Y = 300 # center y

MOS_S = 2#0.3 # mouse sensitivity

Dx = 0 # delta x angle spin
Dy = 0 # delta y angle spin

def sin(angle):
    if(angle % 90):
        return math.sin(angle * math.pi / 180)
    else:
        if(angle % 180):
            return -1 if (angle % 360 - 90) else 1
        else:
            return 0;

def cos(angle):
    if(angle % 90):
        return math.cos(angle * math.pi / 180)
    else:
        if(angle % 180):
            return 0
        else:
            return -1 if (angle % 360) else 1

window = tkinter.Tk()
win = tkinter.Canvas(window, width = Cen_X * 2, height = Cen_Y * 2)

# process mouse event
Bx = By = -1

#mouse move while mouse was in clicked state
def FNC1(event):
    global Bx, By, Dx, Dy
    if not ((Bx == -1) and (By == -1)):
        Ang_X = event.x - Bx
        Ang_Y = event.y - By
        Dx = (Ang_X + 360) % 360
        Dy = (360 - Ang_Y) % 360
    Bx = event.x
    By = event.y

#mouses click
def FNC2(event):
    global Bx, By
    Bx = By = -1

#wheel mouse
def FNC3(event):
    global scale, Scl_L
    if event.delta == 120:
        if scale < max(Cen_X, Cen_Y) / Scl_L:
            scale += 0.15
    elif event.delta == -120:
        if scale > 0.15: 
            scale -= 0.15

def Rotate_A(R, Z, A):
    return (R * cos(A)) - (Z * sin(A)), (R * sin(A)) + (Z * cos(A))

for i in range(1, 4, 1):
    win.bind("<Button-" + str(i) + ">", FNC1)
    win.bind("<B" + str(i) + "-Motion>", FNC1)
    win.bind("<ButtonRelease-" + str(i) + ">", FNC2)
    win.bind("<MouseWheel>", FNC3);
win.pack()

RAD = 200
XL = 72
YL = 36
DAXL = 360 // XL
YL -= 2
DAYL = 180 // (YL + 2)

NYL = 90 - DAYL

Ny = sin(NYL) * RAD
Nr = cos(NYL) * RAD
for i in range(0, 360, DAXL):
    nodeL.append([cos(i) * Nr, Ny, sin(i) * Nr])
    edgeL.append((i // DAXL, (i // DAXL + 1) % XL))
NYL = (NYL - DAYL + 360) % 360
for j in range(0, YL, 1):
    Ny = sin(NYL) * RAD
    Nr = cos(NYL) * RAD
    for i in range(0, 360, DAXL):
        nodeL.append([cos(i) * Nr, Ny, sin(i) * Nr])
        edgeL.append(((XL * (j + 1)) + (i // DAXL), (XL * (j + 1)) + ((i // DAXL + 1) % XL)))
        edgeL.append(((XL * j) + (i // DAXL), (XL * (j + 1)) + (i // DAXL)))
    NYL = (NYL - DAYL + 360) % 360

SCH = '0123456789ABCDEF'
for j in range(0, YL, 1):
    for i in range(0, XL, 1):
        CR = '#' + random.choice(SCH) + random.choice(SCH) + random.choice(SCH) + random.choice(SCH) + random.choice(SCH) + random.choice(SCH)
        faceL.append(((XL * j) + i, (XL * j) + ((i + 1) % XL), (XL * (j + 1)) + ((i + 1) % XL), (XL * (j + 1)) + i, CR))

win.create_text(Cen_X, 20, text= "move cursor to spin",fill="black",font=('Arial 15 bold'))
Dx = Dy = 0

while 1:
    win.delete('figure')
    win.delete('coor')
    
    #Rotate node
    for node in nodeL:
        node[0], node[2] = Rotate_A(node[0], node[2], Dx)
        node[2], node[1] = Rotate_A(node[2], node[1], Dy)
    #Rotate coor
    for coor in coorL:
        coor[0], coor[2] = Rotate_A(coor[0], coor[2], Dx)
        coor[2], coor[1] = Rotate_A(coor[2], coor[1], Dy)

    Dx = Dy = 0;

    #Draw coor
    for i in range (0, len(coorL), 1):
        win.create_line(Cen_X, Cen_Y, Cen_X - coorL[i][0], Cen_Y - coorL[i][1], fill = coorC[i], width = 3, tags = ('edge', 'coor'))
    #Draw edge
    #for edge in edgeL:
        #if (nodeL[edge[0]][2] >= 0) and (nodeL[edge[1]][2] >= 0):
            #win.create_line(Cen_X - nodeL[edge[0]][0], Cen_Y - nodeL[edge[0]][1], Cen_X - nodeL[edge[1]][0], Cen_Y - nodeL[edge[1]][1], fill = Obj_C, width = Edg_W, tags=('edge', 'figure'))
    #Draw face
    for face in faceL:
        if (nodeL[face[0]][2] >= 0) and (nodeL[face[1]][2] >= 0) and (nodeL[face[2]][2] >= 0) and (nodeL[face[3]][2] >= 0):
            win.create_polygon(Cen_X - nodeL[face[0]][0], Cen_Y - nodeL[face[0]][1], Cen_X - nodeL[face[1]][0], Cen_Y - nodeL[face[1]][1], Cen_X - nodeL[face[2]][0], Cen_Y - nodeL[face[2]][1], Cen_X - nodeL[face[3]][0], Cen_Y - nodeL[face[3]][1], fill = face[4], tags=('face', 'figure'))
    win.update()
    time.sleep(0.01)

window.mainloop()

excute

 

사용 방법은 간단합니다.
실행시킬 시, 프로그램은 자동으로 랜덤하게 다양한 색깔을 가지는 구체를 만들어냅니다.
클릭 후 드래그로 회전시킬 수 있고, 마우스 휠로 확대 및 축소할 수 있습니다.

(축소.확대 기능은 이 프로그램에서는 빠짐)