Вращение точки с помощью матрицы поворота/углов Эйлера
Ниже представлены 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 - выбрать ось вращения. ЛКМ - вращать. Попробуйте вращать по разным осям, увидите, что вначале вращение идет вокруг оси, затем сбивается и оси вращаются вокруг невидимых осей.