Вращение точки с помощью матрицы поворота/углов Эйлера

Ниже представлены 3 функции, каждая из которых вращает две точки вокруг соответствующих осей. Точки поочередно вращаются вокруг осей X Y Z (Не думаю что порядок, впрочем, важен). Объект действительно вращается без деформаций, но оси вращения смещаются относительно осей объекта, чего быть не должно. При вращении вокруг одной оси все в порядке, но при двух и трех происходят сдвиги. Мне нужно понять в чем дело.

from tkinter import *
from functools import partial
import math

from PIL import Image, ImageTk
import numpy as np
import sys

from os import listdir, getcwd
from os.path import isfile, join

def rotate_object(xs, xe, ys, ye, zs, ze, ax, ay, az, rot_axis ):
    axcos = math.cos(math.radians(ay))
    axsin = math.sin(math.radians(ay))
    aycos = math.cos(math.radians(ax))
    aysin = math.sin(math.radians(ax))
    azcos = math.cos(math.radians(az))
    azsin = math.sin(math.radians(az))

    def rotY(xs, xe, ys, ye, zs, ze):
        # OY Поворачиваю ось
        xs1 =  xs*aycos + ys*0 + zs*aysin
        ys1 =  xs*0     + ys*1 + zs*0
        zs1 = -xs*aysin + ys*0 + zs*aycos
        xs = xs1; ys = ys1; zs = zs1
    
        xe1 =  xe*aycos + ye*0 + ze*aysin
        ye1 =  xe*0     + ye*1 + ze*0
        ze1 = -xe*aysin + ye*0 + ze*aycos
        xe = xe1; ye = ye1; ze = ze1
        return (xs, xe, ys, ye, zs, ze)

    def rotX(xs, xe, ys, ye, zs, ze):
        # OX Поворачиваю ось
        xs1 = xs*1 + ys*0     + zs*0
        ys1 = xs*0 + ys*axcos - zs*axsin
        zs1 = xs*0 + ys*axsin + zs*axcos
        xs = xs1; ys = ys1; zs = zs1
    
        xe1 = xe*1 + ye*0     + ze*0
        ye1 = xe*0 + ye*axcos - ze*axsin
        ze1 = xe*0 + ye*axsin + ze*axcos
        xe = xe1; ye = ye1; ze = ze1
        return (xs, xe, ys, ye, zs, ze)

    
    def rotZ(xs, xe, ys, ye, zs, ze):
        # OZ Поворачиваю ось
        xs1 = xs*azcos - ys*azsin + zs*0
        ys1 = xs*azsin + ys*azcos + zs*0
        zs1 = xs*0     + ys*0     + zs*1
        xs = xs1; ys = ys1; zs = zs1
    
        xe1 = xe*azcos - ye*azsin + ze*0
        ye1 = xe*azsin + ye*azcos + ze*0
        ze1 = xe*0     + ye*0     + ze*1
        xe = xe1; ye = ye1; ze = ze1
        return (xs, xe, ys, ye, zs, ze)


    xs, xe, ys, ye, zs, ze = rotX(xs, xe, ys, ye, zs, ze)
    xs, xe, ys, ye, zs, ze = rotY(xs, xe, ys, ye, zs, ze)
    xs, xe, ys, ye, zs, ze = rotZ(xs, xe, ys, ye, zs, ze)


    return (xs, xe, ys, ye, zs, ze)


class WindowTK(Tk):
    def __init__(self, *args, **kwargs):
        Tk.__init__(self, *args, **kwargs)
        self.geometry(args[0])
        self.grid_rowconfigure    (0, weight=1)
        self.grid_columnconfigure (0, weight=1)
        self.title("title")
        self.update()
        self.width  = self.winfo_width()
        self.height = self.winfo_height()
        self.width_half = int(self.width/2)
        self.height_half = int(self.height/2)

class Canvas3D(Canvas):
    def __init__(self, *args, **kwargs):
        Canvas.__init__(self, *args, **kwargs)
        self.windowTK = args[0]
        self.rot_axis = "None"
        self.rot_angle_d = {"X":0, "Y":0, "Z":0}
        self.rot_per_pixel_degree = 1
        self.config(bg=kwargs["bg"])
        self.binding()
        self.start()

    def start(self):
        self.c_axis = Axis(self)
        self.c_canvas3D_text = Canvas3D_text(self)


    def recount_coord(self, xs, xe, ys, ye, zs, ze):
        ax = self.rot_angle_d["X"]
        ay = self.rot_angle_d["Y"]
        az = self.rot_angle_d["Z"]
        xs, xe, ys, ye, zs, ze = rotate_object(xs, xe, ys, ye, zs, ze, ax, ay, az, self.rot_axis )

       # Смещение в центр
        x1 = xs + self.windowTK.width_half  
        y1 = ys + self.windowTK.height_half
        x2 = xe + self.windowTK.width_half 
        y2 = ye + self.windowTK.height_half

        return [x1, x2, y1, y2]

    def binding(self):
        self.bind("<Button-1>",  self.cursor_coord)
        self.bind("<B1-Motion>", self.cursor_motion)
        self.windowTK.bind("1", partial(self.set_rotation_axis, "X"))
        self.windowTK.bind("2", partial(self.set_rotation_axis, "Y"))
        self.windowTK.bind("3", partial(self.set_rotation_axis, "Z"))
        self.windowTK.bind("4", partial(self.set_rotation_axis, "None"))
        self.windowTK.bind("5", partial(self.set_rotation_axis, "Reset"))

    def cursor_coord(self, event):
        self.cursor_x1 = event.x
        self.cursor_y1 = event.y

    def cursor_motion(self, event):
        self.after(100)
        self.cursor_x2 = event.x
        self.cursor_y2 = event.y
        self.cursor_xdif = self.cursor_x2 - self.cursor_x1
        self.cursor_ydif = self.cursor_y2 - self.cursor_y1
        self.cursor_x1 += self.cursor_xdif
        self.cursor_y1 += self.cursor_ydif
        if self.rot_axis in self.rot_angle_d:
            self.rot_angle_d[self.rot_axis] += self.rot_per_pixel_degree*self.cursor_xdif
        elif self.rot_axis == "Reset":
            self.rot_angle_d = {"X":0, "Y":0, "Z":0}
        print(self.cursor_x1, self.cursor_y1, self.rot_angle_d)
        self.c_axis.start()


    def set_rotation_axis(self, value, event):
        self.rot_axis = str(value)
        self.c_canvas3D_text.text = "ROT {}".format(self.rot_axis)
        self.c_canvas3D_text.start()


class Axis():
    def __init__(self, *args, **kwargs):
        self.canvas3D = args[0]
        self.windowTK = self.canvas3D.windowTK
        self.id_l = []
        self.start()

    def start(self):
        for id in self.id_l: self.canvas3D.delete(id); self.id_l = []

        radius = 5
        x1, x2, y1, y2 = self.canvas3D.recount_coord(xs = 0, xe = 0, ys = 0, ye = 0, zs = 0, ze = 0)
        x1 -= radius
        y1 -= radius
        x2 += radius
        y2 += radius
        id = self.canvas3D.create_oval(x1, y1, x2, y2, fill = "tomato"); self.id_l.append(id)

        x1, x2, y1, y2 = self.canvas3D.recount_coord(xs = 0, xe = 50, ys = 0, ye = 0, zs = 0, ze = 0)
        id = self.canvas3D.create_line(x1, y1, x2, y2, fill = "red", arrow = "last"); self.id_l.append(id)
        id = self.canvas3D.create_text(x2, y2, text = "Y", anchor = "nw", font = "arial 10 bold"); self.id_l.append(id)

        x1, x2, y1, y2 = self.canvas3D.recount_coord(xs = 0, xe = 0, ys = 0, ye = 50, zs = 0, ze = 0)
        id = self.canvas3D.create_line(x1, y1, x2, y2, fill = "green", arrow = "last"); self.id_l.append(id)
        id = self.canvas3D.create_text(x2, y2, text = "X", anchor = "nw", font = "arial 10 bold"); self.id_l.append(id)

        x1, x2, y1, y2 = self.canvas3D.recount_coord(xs = 0, xe = 0, ys = 0, ye = 0, zs = 0, ze = 50)
        id = self.canvas3D.create_line(x1, y1, x2, y2, fill = "blue", arrow = "last"); self.id_l.append(id)
        id = self.canvas3D.create_text(x2, y2, text = "Z", anchor = "nw", font = "arial 10 bold"); self.id_l.append(id)

class Canvas3D_text():
    def __init__(self, *args, **kwargs):
        self.canvas3D = args[0]
        self.windowTK = self.canvas3D.windowTK
        self.id_l = []
        self.text = "None"
        self.start()

    def start(self):
        for id in self.id_l: self.canvas3D.delete(id); self.id_l = []
        x1, x2, y1, y2 = 10, 100, 10, 100
        id = self.canvas3D.create_text(x1, y1, text = self.text, anchor = "nw", font = "arial 10 bold")
        if not id in self.id_l: self.id_l.append(id)

    def get_pixel_l(self, image):
        pixel_l = []
        pixel_matrix = []
        rgb_image = image.convert('RGB')
        for y in range(self.pixel_in_image):
            for x in range(self.pixel_in_image):
                r, g, b = rgb_image.getpixel((x, y))
                rgb = r + g + b
                rgb_max = 255*3
                result  = rgb/rgb_max * self.height_max  # Получаю долю по высоте отмаксимальной высоты
                pixel_l.append(result)
            pixel_matrix.append(pixel_l)
            pixel_l = []

        
        return(pixel_matrix)

if __name__ == "__main__":
    tk = WindowTK("500x500+0+0")
    canvas3D = Canvas3D(tk, bg="aquamarine")
    canvas3D.pack(expand=True, fill="both")
    tk.mainloop()

Весь код. Клавиши 1, 2, 3 - выбрать ось вращения. ЛКМ - вращать. Попробуйте вращать по разным осям, увидите, что вначале вращение идет вокруг оси, затем сбивается и оси вращаются вокруг невидимых осей.


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