rot2d ломает и изменяет позицию

я пишу с нуля 3д движок со своей координатной системой и я застрял на повороте точек. Если смотреть на этот куб достаточно долго, то можно увидеть, что он превращается в параллелепипед.

import pygame
from math import *
(width, height) = (1280, 960)
screen = pygame.display.set_mode((width, height))
running = True


"""
k=open('/storage/emulated/0/Download/airboat.obj')
n=k.readlines()
k.close()
vert=[]
for i in n:
    if len(i.split())!=0:
        if i.split()[0]=="v":
            vert.append(list(map(float,i.split()[1:4])))

"""
d=[[1,1,1],
[1,1,-1],
[1,-1,1],
[1,-1,-1],
[-1,1,1],
[-1,1,-1],
[-1,-1,1],
[-1,-1,-1]]
l=[[0,0,0,0,999,0,(0,255,0)],
[0,0,0,999,0,0,(255,0,0)],
[0,0,0,0,0,999,(0,0,255)],
[0,0,0,0,-999,0,(255,0,255)],
[0,0,0,-999,0,0,(0,255,255)],
[0,0,0,0,0,-999,(255,255,0)]]
import math
def rot2d(ox, oy, theta, px, py):
    px = cos(theta) * (px-ox) - sin(theta) * (py-oy) + ox
    py = sin(theta) * (px-ox) + cos(theta) * (py-oy) + oy
    return [px,py]
def mul2d(vec2d,n): # z is not used
    k=[]
    for i in range(0,2):
        k.append(vec2d[i]*n)
    return k
def div2d(vec2d,n): # z is not used
    k=[]
    for i in range(0,2):
        k.append(vec2d[i]/n)
    return k
def add2d(vec2d,vec2db): # z is not used
    k=[]
    for i in range(0,2):
        k.append(vec2d[i]+vec2db[i])
    return k
def sub2d(vec2d,vec2db): # z is not used
    k=[]
    for i in range(0,2):
        k.append(vec2d[i]-vec2db[i])
    return k
def center2d(h,w,vec2d):
    return [vec2d[0]+w/2,vec2d[1]+h/2]
class Vec3d:
    def __init__(self, x:int, y:int, z:int):
        self.x=x
        self.y=y
        self.z=z        
    def getXY(self,factor=180,safe=True): #todo: vyebat
        if self.z>0:
            return [self.x/(self.z/factor),self.y/(self.z/factor)]      
    def __add__(self,vec):
        return Vec3d(vec.x+self.x,vec.y+self.y,vec.z+self.z)
    def __sub__(self,vec):
        return Vec3d(vec.x-self.x,vec.y-self.y,vec.z-self.z)
    def __mul__(self,num):
        return Vec3d(self.x*num,self.y*num,self.z*num)
    def __div__(self,num):
        return Vec3d(self.x/num,self.y/num,self.z/num)
    def __repr__(self):
        return f"Vec3d({self.x}, {self.y}, {self.z})"
    def __str__(self):
        return f"Vec3d({self.x}, {self.y}, {self.z})"   
def listToVec3d(l):
    return Vec3d(l[0],l[1],l[2])
camerapos=Vec3d(0,0,6)
dtime=0
time=0
rot=0
clk= pygame.time.Clock()
pygame.font.init()
font = pygame.font.SysFont('Consolas', 50)

def rotate(model: list, axis: str, center: list, deg: int):
    d=model
    deg=radians(deg)
    if axis=="xy" or axis=="yx":
        for k in d:
            n=rot2d(center[0],center[1],deg,k[0],k[1])
            k[0]=n[0]
            k[1]=n[1]
    if axis=="yz" or axis=="zy":
        for k in d:
            n=rot2d(center[1],center[2],deg,k[1],k[2])
            k[1]=n[0]
            k[2]=n[1]
    if axis=="zx" or axis=="xz":
        for k in d:
            n=rot2d(center[2],center[0],deg,k[2],k[0])
            k[2]=n[0]
            k[0]=n[1]
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
    screen.fill((16,16,16))
    pygame.draw.line(screen, (255,255,255),[width,0],[width,height])
    pygame.draw.line(screen, (255,255,255),[0,height],[width,height])
    fps=clk.get_fps()
    for k in d:
        a=(listToVec3d(k)-camerapos).getXY()
        if a!=None:
            c=center2d(height,width,mul2d(a,2))
            screen.set_at(list(map(int,c)),(255,255,255))
        pygame.draw.circle(screen, (255,255,255), c,5)
    for lin in l:
        ai=(listToVec3d(lin[0:3])-camerapos).getXY()
        bi=(listToVec3d(lin[3:6])-camerapos).getXY()
        if ai!=None:
            a=center2d(height,width,mul2d(ai,2))
        if bi!=None:
            b=center2d(height,width,mul2d(bi,2))
        if a!=None and b!=None: pygame.draw.line(screen, lin[6],a,b)
    text_surface= font.render(f"fps: {round(fps,2)}", (255,0,255),True)
    screen.blit(text_surface, (50, 50))
    text_surface= font.render(f"sec: {round(1/(fps+0.01),2)}", (255,0,255),True)
    screen.blit(text_surface, (50, 100))
    text_surface= font.render(f"rot: {rot}", (255,0,255),True)
    screen.blit(text_surface, (50, 150))    
    dtime=1/(fps+0.001)
    clk.tick()
    time+=dtime
    rot+=1
    rotate(d, "xz", [0,0,0], 1)
    if rot>360:
        rotate(d, "xz", [0,0,0], -rot)
        rot=0
    pygame.display.flip()

Ответы (1 шт):

Автор решения: MBo
def rot2d(ox, oy, theta, px, py):
   px = cos(theta) * (px-ox) - sin(theta) * (py-oy) + ox
   py = sin(theta) * (px-ox) + cos(theta) * (py-oy) + oy

=>

def rot2d(ox, oy, theta, px, py):
   t = px
   px = cos(theta) * (px-ox) - sin(theta) * (py-oy) + ox
   py = sin(theta) * (t-ox) + cos(theta) * (py-oy) + oy

Из мелочей - один раз посчитайте синус и косинус, и используйте в качестве параметров rot2d, вместо того, чтобы для каждой точки, да ещё дважды считать

→ Ссылка